<commit_before>use std::fmt;

use crate::error::EncryptedCryptEncodingError;
use crate::error::ParseError;

use lazy_static::lazy_static;
use regex::Regex;
use rmp_serde;
use serde::{Deserialize, Serialize};

const BASE64_CONFIG: base64::Config = base64::STANDARD_NO_PAD;

const BEGIN_CRYPT_STR: &'static str = "BEGIN CRYPT";
const BEGIN_ENCRYPTED_CRYPT_ENCRYPTION_MARKER_STR: &'static str = "..";
const END_CRYPT_STR: &'static str = "END CRYPT";
const BEGIN_CRYPT_LEN: usize = BEGIN_CRYPT_STR.len();
const BEGIN_ENCRYPTED_CRYPT_LEN: usize =
    BEGIN_CRYPT_STR.len() + BEGIN_ENCRYPTED_CRYPT_ENCRYPTION_MARKER_STR.len();
const END_CRYPT_LEN: usize = END_CRYPT_STR.len();

lazy_static! {
    static ref IS_CRYPT_FILE_RE: Regex = Regex::new(r"(?i)BEGIN[\s\W\p{Punct}]*CRYPT").unwrap();
}

#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct EncryptedCryptBlock {
    pub algorithm: String,
    pub nonce: Vec<u8>,
    pub ciphertext: Vec<u8>,
}

impl EncryptedCryptBlock {
    pub fn from_str(input: &str) -> Result<Self, ParseError> {
        let trimmed_input = if input[..BEGIN_CRYPT_LEN]
            .to_uppercase()
            .starts_with(BEGIN_CRYPT_STR)
        {
            &input[BEGIN_ENCRYPTED_CRYPT_LEN..(input.len() - END_CRYPT_LEN)]
        } else {
            input
        };
        let input_bytes = base64::decode_config(trimmed_input, BASE64_CONFIG)
            .map_err(|e| ParseError::Base64Decode(e))?;
        let block: EncryptedCryptBlock =
            rmp_serde::from_read(&*input_bytes).map_err(|e| ParseError::MessagePackDecode(e))?;

        Ok(block)
    }

    pub fn to_ascii_armor(&self) -> Result<String, EncryptedCryptEncodingError> {
        let mut buf = Vec::new();
        // TODO: extract to `fn to_msg_pack()`?
        self.serialize(&mut rmp_serde::Serializer::new(&mut buf))
            .map_err(|e| EncryptedCryptEncodingError::MessagePackEncode(e))?;

        let encoded = base64::encode_config(buf, BASE64_CONFIG);
        Ok(format!(
            "{}{}{}{}",
            BEGIN_CRYPT_STR, BEGIN_ENCRYPTED_CRYPT_ENCRYPTION_MARKER_STR, encoded, END_CRYPT_STR
        ))
    }
}

#[test]
fn test_parsing_encrypted_crypt_block() {
    let block = EncryptedCryptBlock {
        algorithm: "test_algo".to_string(),
        nonce: b"nonce".to_vec(),
        ciphertext: b"this is some good ciphertext".to_vec(),
    };
    let armored = block.to_ascii_armor().unwrap();

    let parsed = EncryptedCryptBlock::from_str(&armored).unwrap();

    assert_eq!(parsed, block);
}

#[derive(Debug, PartialEq)]
pub enum Block {
    Plaintext(String),
    UnencryptedCryptBlock(String),
    EncryptedCryptBlock(EncryptedCryptBlock),
}

impl Block {
    pub fn is_encrypted(&self) -> bool {
        match self {
            Block::Plaintext(_) | Block::UnencryptedCryptBlock(_) => false,
            Block::EncryptedCryptBlock(_) => true,
        }
    }
}

#[derive(Debug)]
pub struct CryptFile {
    pub blocks: Vec<Block>,
}

impl fmt::Display for CryptFile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.blocks
            .iter()
            .map(|block| match block {
                Block::Plaintext(text) => write!(f, "{}", text),
                Block::UnencryptedCryptBlock(text) => {
                    write!(f, "{}\n{}\n{}", BEGIN_CRYPT_STR, text, END_CRYPT_STR)
                }
                Block::EncryptedCryptBlock(block) => {
                    let ascii_armored = block.to_ascii_armor().map_err(|e| fmt::Error)?;
                    write!(f, "{}", ascii_armored)
                }
            })
            .collect()
    }
}

impl CryptFile {
    pub fn is_crypt_file(contents: &str) -> bool {
        IS_CRYPT_FILE_RE.is_match(contents)
    }

    pub fn from_str(contents: &str) -> Result<CryptFile, ParseError> {
        let mut blocks = Vec::new();

        // Replace with Regexes with case insensitive matching
        let crypt_block_starts: Vec<_> = contents.match_indices(BEGIN_CRYPT_STR).collect();
        let crypt_block_ends: Vec<_> = contents.match_indices(END_CRYPT_STR).collect();
        dbg!(&crypt_block_starts);
        dbg!(&crypt_block_ends);
        if crypt_block_starts.len() != crypt_block_ends.len() {
            return Err(ParseError::MismatchNumStartEndCryptBlocks);
        }

        if !crypt_block_starts.is_empty() && crypt_block_starts[0].0 != 0 {
            let first_crypt_block = crypt_block_starts[0].0;
            let plaintext = contents[0..first_crypt_block].to_string();
            blocks.push(Block::Plaintext(plaintext));
        }

        let mut prev_end = 0;
        for ((start_idx, _), (end_idx, _)) in crypt_block_starts
            .into_iter()
            .zip(crypt_block_ends.into_iter())
        {
            if end_idx < start_idx {
                return Err(ParseError::EndBeforeBegin(0));
            }
            if prev_end != 0 && prev_end < start_idx {
                return Err(ParseError::BeginBeforeEnd(0));
            }
            if prev_end != 0 {
                let plaintext = contents[(prev_end + END_CRYPT_STR.len())..start_idx].to_string();
                blocks.push(Block::Plaintext(plaintext));
            }

            let matching_contents = &contents[(start_idx + BEGIN_CRYPT_STR.len())..end_idx];
            let block =
                if matching_contents.starts_with(BEGIN_ENCRYPTED_CRYPT_ENCRYPTION_MARKER_STR) {
                    Block::EncryptedCryptBlock(EncryptedCryptBlock::from_str(
                        &matching_contents[BEGIN_ENCRYPTED_CRYPT_ENCRYPTION_MARKER_STR.len()..],
                    )?)
                } else {
                    Block::UnencryptedCryptBlock(matching_contents.trim().to_string())
                };
            blocks.push(block);
            prev_end = end_idx;
        }

        Ok(CryptFile { blocks })
    }
}

#[test]
fn test_from_str_unencrypted() {
    let contents = r#"hello worldBEGIN CRYPThelloworldencryptedEND CRYPTBEGIN CRYPTthisis a test\n\nEND CRYPT
"#;
    let parsed = CryptFile::from_str(contents).unwrap();
    dbg!(parsed);
}
<commit_msg>text-crypt: strip whitespace in ascii_armor when parsing, fixed bug in parsing<commit_after>use std::fmt;

use crate::error::EncryptedCryptEncodingError;
use crate::error::ParseError;

use lazy_static::lazy_static;
use regex::Regex;
use rmp_serde;
use serde::{Deserialize, Serialize};

const BASE64_CONFIG: base64::Config = base64::STANDARD_NO_PAD;

const BEGIN_CRYPT_STR: &'static str = "BEGIN CRYPT";
const BEGIN_ENCRYPTED_CRYPT_ENCRYPTION_MARKER_STR: &'static str = ".e.";
const END_CRYPT_STR: &'static str = "END CRYPT";
const BEGIN_CRYPT_LEN: usize = BEGIN_CRYPT_STR.len();
const BEGIN_ENCRYPTED_CRYPT_LEN: usize =
    BEGIN_CRYPT_STR.len() + BEGIN_ENCRYPTED_CRYPT_ENCRYPTION_MARKER_STR.len();
const END_CRYPT_LEN: usize = END_CRYPT_STR.len();

lazy_static! {
    static ref IS_CRYPT_FILE_RE: Regex = Regex::new(r"(?i)BEGIN[\s\W\p{Punct}]*CRYPT").unwrap();
}

#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct EncryptedCryptBlock {
    pub algorithm: String,
    pub nonce: Vec<u8>,
    pub ciphertext: Vec<u8>,
}

impl EncryptedCryptBlock {
    pub fn from_str(input: &str) -> Result<Self, ParseError> {
        let input: String = input.chars().filter(|c| !c.is_whitespace()).collect();
        let trimmed_input = if input[..BEGIN_CRYPT_LEN]
            .to_uppercase()
            .starts_with(BEGIN_CRYPT_STR)
        {
            &input[BEGIN_ENCRYPTED_CRYPT_LEN..(input.len() - END_CRYPT_LEN)]
        } else {
            &input
        };
        let input_bytes = base64::decode_config(trimmed_input, BASE64_CONFIG)
            .map_err(|e| ParseError::Base64Decode(e))?;
        let block: EncryptedCryptBlock =
            rmp_serde::from_read(&*input_bytes).map_err(|e| ParseError::MessagePackDecode(e))?;

        Ok(block)
    }

    pub fn to_ascii_armor(&self) -> Result<String, EncryptedCryptEncodingError> {
        let mut buf = Vec::new();
        // TODO: extract to `fn to_msg_pack()`?
        self.serialize(&mut rmp_serde::Serializer::new(&mut buf))
            .map_err(|e| EncryptedCryptEncodingError::MessagePackEncode(e))?;

        let encoded = base64::encode_config(buf, BASE64_CONFIG);
        Ok(format!(
            "{}{}{}{}",
            BEGIN_CRYPT_STR, BEGIN_ENCRYPTED_CRYPT_ENCRYPTION_MARKER_STR, encoded, END_CRYPT_STR
        ))
    }
}

#[test]
fn test_parsing_encrypted_crypt_block() {
    let block = EncryptedCryptBlock {
        algorithm: "test_algo".to_string(),
        nonce: b"nonce".to_vec(),
        ciphertext: b"this is some good ciphertext".to_vec(),
    };
    let armored = block.to_ascii_armor().unwrap();

    let parsed = EncryptedCryptBlock::from_str(&armored).unwrap();

    assert_eq!(parsed, block);
}

#[derive(Debug, PartialEq)]
pub enum Block {
    Plaintext(String),
    UnencryptedCryptBlock(String),
    EncryptedCryptBlock(EncryptedCryptBlock),
}

impl Block {
    pub fn is_encrypted(&self) -> bool {
        match self {
            Block::Plaintext(_) | Block::UnencryptedCryptBlock(_) => false,
            Block::EncryptedCryptBlock(_) => true,
        }
    }
}

#[derive(Debug)]
pub struct CryptFile {
    pub blocks: Vec<Block>,
}

impl fmt::Display for CryptFile {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.blocks
            .iter()
            .map(|block| match block {
                Block::Plaintext(text) => write!(f, "{}", text),
                Block::UnencryptedCryptBlock(text) => {
                    write!(f, "{}\n{}\n{}", BEGIN_CRYPT_STR, text, END_CRYPT_STR)
                }
                Block::EncryptedCryptBlock(block) => {
                    let ascii_armored = block.to_ascii_armor().map_err(|e| fmt::Error)?;
                    write!(f, "{}", ascii_armored)
                }
            })
            .collect()
    }
}

impl CryptFile {
    pub fn is_crypt_file(contents: &str) -> bool {
        IS_CRYPT_FILE_RE.is_match(contents)
    }

    pub fn from_str(contents: &str) -> Result<CryptFile, ParseError> {
        let mut blocks = Vec::new();

        // Replace with Regexes with case insensitive matching
        let crypt_block_starts: Vec<_> = contents.match_indices(BEGIN_CRYPT_STR).collect();
        let crypt_block_ends: Vec<_> = contents.match_indices(END_CRYPT_STR).collect();
        dbg!(&crypt_block_starts);
        dbg!(&crypt_block_ends);
        if crypt_block_starts.len() != crypt_block_ends.len() {
            return Err(ParseError::MismatchNumStartEndCryptBlocks);
        }

        if !crypt_block_starts.is_empty() && crypt_block_starts[0].0 != 0 {
            let first_crypt_block = crypt_block_starts[0].0;
            let plaintext = contents[0..first_crypt_block].to_string();
            blocks.push(Block::Plaintext(plaintext));
        }

        let mut prev_end = 0;
        for ((start_idx, _), (end_idx, _)) in crypt_block_starts
            .into_iter()
            .zip(crypt_block_ends.into_iter())
        {
            if end_idx < start_idx {
                return Err(ParseError::EndBeforeBegin(0));
            }
            if prev_end != 0 && prev_end > start_idx {
                return Err(ParseError::BeginBeforeEnd(0));
            }
            if prev_end != 0 {
                let plaintext = contents[(prev_end + END_CRYPT_STR.len())..start_idx].to_string();
                blocks.push(Block::Plaintext(plaintext));
            }

            let matching_contents = &contents[(start_idx + BEGIN_CRYPT_STR.len())..end_idx];
            let block =
                if matching_contents.starts_with(BEGIN_ENCRYPTED_CRYPT_ENCRYPTION_MARKER_STR) {
                    Block::EncryptedCryptBlock(EncryptedCryptBlock::from_str(
                        &matching_contents[BEGIN_ENCRYPTED_CRYPT_ENCRYPTION_MARKER_STR.len()..],
                    )?)
                } else {
                    Block::UnencryptedCryptBlock(matching_contents.trim().to_string())
                };
            blocks.push(block);
            prev_end = end_idx;
        }

        Ok(CryptFile { blocks })
    }
}

#[test]
fn test_from_str_unencrypted() {
    let contents = r#"hello worldBEGIN CRYPThelloworldencryptedEND CRYPTBEGIN CRYPTthisis a test\n\nEND CRYPT
"#;
    let parsed = CryptFile::from_str(contents).unwrap();
    dbg!(parsed);
}

// TODO: Add more test cases for errors
<|endoftext|>


<commit_before>use crypto;
use crypto::mac::Mac;
use crypto::symmetriccipher::BlockDecryptor;

use protobuf;
use protobuf::stream::CodedInputStream;

use rustc_serialize::hex::ToHex;

use std::collections::HashMap;
use std::fs;
use std::io::Cursor;
use std::io::Read;
use std::io::Write;
use std::sync::Arc;

use misc::*;

use zbackup;
use zbackup::proto;
use zbackup::randaccess::*;
use zbackup::read::*;

const KEY_SIZE: usize =
	16;

const CACHE_MAX_SIZE: usize =
	0x10000;

pub struct MasterIndexEntry {
	pub bundle_id: [u8; 24],
	pub size: u64,
}

pub struct Repository {
	path: String,
	storage_info: proto::StorageInfo,
	master_index: HashMap <[u8; 24], MasterIndexEntry>,
	chunk_cache: HashMap <[u8; 24], Arc <Vec <u8>>>,
}

impl Repository {

	pub fn open (
		repository_path: & str,
		password_file_path: Option <& str>,
	) -> Result <Repository, String> {

		// load info file

		stderrln! (
			"Loading repository {}",
			repository_path);

		let storage_info =
			try! (
				read_storage_info (
					& format! (
						"{}/info",
						repository_path)));

		if storage_info.has_encryption_key () {

			stderrln! (
				"ENCRYPTION KEY SALT: {:?}",
				storage_info.get_encryption_key ().get_salt ());

			stderrln! (
				"ENCRYPTION KEY ROUNDS: {:?}",
				storage_info.get_encryption_key ().get_rounds ());

			stderrln! (
				"ENCRYPTED KEY: {:?}",
				storage_info.get_encryption_key ().get_encrypted_key ());

			stderrln! (
				"KEY CHECK INPUT: {:?}",
				storage_info.get_encryption_key ().get_key_check_input ());

			stderrln! (
				"KEY CHECK HMAC: {:?}",
				storage_info.get_encryption_key ().get_key_check_hmac ());

			// derive password key from password

			let mut password_hmac =
				crypto::hmac::Hmac::new (
					crypto::sha1::Sha1::new (),
					b"password");

			let mut password_result =
				[0u8; KEY_SIZE];

			crypto::pbkdf2::pbkdf2 (
				& mut password_hmac,
				storage_info.get_encryption_key ().get_salt (),
				storage_info.get_encryption_key ().get_rounds (),
				& mut password_result);

			stderrln! (
				"PASSWORD RESULT: {:?}",
				password_result);

			// decrypt actual key using password key

			let key_decryptor =
				crypto::aessafe::AesSafe128Decryptor::new (
					& password_result);

			let mut key_result =
				[0u8; KEY_SIZE];

			key_decryptor.decrypt_block (
				& storage_info.get_encryption_key ().get_encrypted_key (),
				& mut key_result);

			stderrln! (
				"KEY RESULT: {:?}",
				key_result);

			// derive check result to verify password

			let mut check_hmac =
				crypto::hmac::Hmac::new (
					crypto::sha1::Sha1::new (),
					& key_result);

			check_hmac.input (
				storage_info.get_encryption_key ().get_key_check_input ());

			let mut check_result =
				[0u8; 20];

			check_hmac.raw_result (
				& mut check_result);

			stderrln! (
				"SIGNED RESULT: {:?}",
				check_result);

		}

		// load indexes

		stderr! (
			"Loading indexes");

		let mut master_index: HashMap <[u8; 24], MasterIndexEntry> =
			HashMap::with_capacity (0x10000);

		let mut count: u64 = 0;

		for dir_entry_or_error in try! (
			io_result (
				fs::read_dir (
					format! (
						"{}/index",
						repository_path)))
		) {

			let dir_entry =
				try! (
					io_result (
						dir_entry_or_error));

			let file_name =
				dir_entry.file_name ();

			let index_name =
				file_name.to_str ().unwrap ();

			let index =
				try! (
					read_index (
						& format! (
							"{}/index/{}",
							repository_path,
							index_name)));

			for (index_bundle_header, bundle_info) in index {

				for chunk_record in bundle_info.get_chunk_record () {

					master_index.insert (
						to_array (chunk_record.get_id ()),
						MasterIndexEntry {

							bundle_id:
								to_array (index_bundle_header.get_id ()),

							size:
								chunk_record.get_size () as u64

						},
					);

				}

			}

			if count & 0x3f == 0x3f {
				stderr! (
					".");
			}

			count += 1;

		}

		stderr! (
			"\n");

		// return

		Ok (Repository {
			path: repository_path.to_string (),
			storage_info: storage_info,
			master_index: master_index,
			chunk_cache: HashMap::new (),
		})

	}

	pub fn read_and_expand_backup (
		& mut self,
		backup_name: & str,
	) -> Result <Vec <u8>, String> {

		// load backup

		stderr! (
			"Loading backup {}",
			backup_name);

		let backup_info =
			try! (
				read_backup_file (
					format! (
						"{}/backups/{}",
						& self.path,
						backup_name)));

		// expand backup data

		let mut input =
			Cursor::new (
				backup_info.get_backup_data ().to_owned ());

		for _iteration in 0 .. backup_info.get_iterations () {

			let mut temp_output: Cursor <Vec <u8>> =
				Cursor::new (
					Vec::new ());

			try! (
				self.follow_instructions (
					& mut input,
					& mut temp_output,
					& |count| {
						if count & 0xf == 0xf {
							stderr! (".");
						}
					}));

			input =
				Cursor::new (
					temp_output.into_inner ());

		}

		stderr! (
			"\n");

		Ok (input.into_inner ())

	}

	pub fn restore (
		& mut self,
		backup_name: & str,
		output: & mut Write,
	) -> Result <(), String> {

		let mut input =
			Cursor::new (
				try! (
					self.read_and_expand_backup (
						backup_name)));

		// restore backup

		stderr! (
			"Restoring backup");

		try! (
			self.follow_instructions (
				& mut input,
				output,
				& |count| {
					if count & 0x1ff == 0x1ff {
						stderr! (".");
					}
				}));

		stderr! (
			"\n");

		stderrln! (
			"Restore complete");

		Ok (())

	}

	pub fn restore_test (
		& mut self,
		backup_name: & str,
		output: & mut Write,
	) -> Result <(), String> {

		stderr! (
			"Loading backup {}",
			backup_name);

		let mut input =
			try! (
				RandomAccess::new (
					self,
					backup_name));

		let mut buffer: Vec <u8> =
			Vec::with_capacity (
				1024 * 1024);

		unsafe {

			buffer.set_len (
				1024 * 1024);

		}

		// restore backup

		stderr! (
			"Restoring backup");

		loop {

			let bytes_read =
				try! (
					io_result (
						input.read (
							& mut buffer)));

			if bytes_read == 0 {
				break;
			}

			try! (
				io_result (
					output.write (
						& buffer [
							0 .. bytes_read ])));

		}

		stderrln! (
			"Restore complete");

		Ok (())

	}

	pub fn follow_instruction (
		& mut self,
		backup_instruction: & proto::BackupInstruction,
		output: & mut Write,
	) -> Result <(), String> {

		if backup_instruction.has_chunk_to_emit () {

			let chunk_id =
				to_array (
					backup_instruction.get_chunk_to_emit ());

			let chunk_data = try! (
				self.get_chunk (
					chunk_id));

			try! (
				io_result (
					output.write (
						& chunk_data)));

		}

		if backup_instruction.has_bytes_to_emit () {

			try! (
				io_result (
					output.write (
						backup_instruction.get_bytes_to_emit ())));

		}

		Ok (())

	}

	pub fn follow_instructions (
		& mut self,
		input: & mut Read,
		output: & mut Write,
		progress: & Fn (u64),
	) -> Result <(), String> {

		let mut coded_input_stream =
			CodedInputStream::new (
				input);

		let mut count: u64 = 0;

		while ! try! (
			protobuf_result (
				coded_input_stream.eof ())
		) {

			let instruction_length =
				try! (
					protobuf_result (
						coded_input_stream.read_raw_varint32 ()));

			let instruction_old_limit =
				try! (
					protobuf_result (
						coded_input_stream.push_limit (
							instruction_length)));

			let backup_instruction =
				try! (
					protobuf_result (
						protobuf::core::parse_from::<proto::BackupInstruction> (
							&mut coded_input_stream)));

			coded_input_stream.pop_limit (
				instruction_old_limit);

			try! (
				self.follow_instruction (
					& backup_instruction,
					output));

			progress (
				count);

			count += 1;

		}

		Ok (())

	}

	pub fn get_chunk (
		& mut self,
		chunk_id: [u8; 24],
	) -> Result <Arc <Vec <u8>>, String> {

		if ! self.chunk_cache.contains_key (& chunk_id) {

			if self.chunk_cache.len () >= CACHE_MAX_SIZE {

				self.chunk_cache.clear ();

			}

			let index_entry = match
				self.master_index.get (& chunk_id) {

				Some (value) =>
					value,

				None => {

					return Err (
						format! (
							"Missing chunk: {}",
							chunk_id.to_hex ()));

				},

			};

			for (found_chunk_id, found_chunk_data) in try! (

				read_bundle (
					& format! (
						"{}/bundles/{}/{}",
						self.path,
						& index_entry.bundle_id.to_hex () [0 .. 2],
						index_entry.bundle_id.to_hex ()))

			) {

				self.chunk_cache.insert (
					found_chunk_id,
					Arc::new (
						found_chunk_data));

			}

		}

		let chunk_data =
			self.chunk_cache.get (
				& chunk_id,
			).unwrap ();

		Ok (
			chunk_data.clone ())

	}

	pub fn get_index_entry (
		& mut self,
		chunk_id: & [u8; 24],
	) -> Result <& MasterIndexEntry, String> {

		return match self.master_index.get (
			chunk_id,
		) {

			Some (value) =>
				Ok (value),

			None =>
				Err (
					format! (
						"Missing chunk: {}",
						chunk_id.to_hex ())
				),

		};

	}

	pub fn open_backup (
		& mut self,
		backup_name: & str,
	) -> Result <RandomAccess, String> {

		RandomAccess::new (
			self,
			backup_name)

	}

}
<commit_msg>clean up password code and use password file instead of hard-coded test value<commit_after>use crypto;
use crypto::mac::Mac;
use crypto::symmetriccipher::BlockDecryptor;

use protobuf;
use protobuf::stream::CodedInputStream;

use rustc_serialize::hex::ToHex;

use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::Cursor;
use std::io::Read;
use std::io::Write;
use std::sync::Arc;

use misc::*;

use zbackup::proto;
use zbackup::randaccess::*;
use zbackup::read::*;

const KEY_SIZE: usize =
	16;

const CACHE_MAX_SIZE: usize =
	0x10000;

pub struct MasterIndexEntry {
	pub bundle_id: [u8; 24],
	pub size: u64,
}

pub struct Repository {
	path: String,
	storage_info: proto::StorageInfo,
	master_index: HashMap <[u8; 24], MasterIndexEntry>,
	chunk_cache: HashMap <[u8; 24], Arc <Vec <u8>>>,
}

impl Repository {

	pub fn open (
		repository_path: & str,
		password_file_path: Option <& str>,
	) -> Result <Repository, String> {

		// load info file

		stderrln! (
			"Loading repository {}",
			repository_path);

		let storage_info =
			try! (
				read_storage_info (
					& format! (
						"{}/info",
						repository_path)));

		if storage_info.has_encryption_key () {

			if password_file_path.is_none () {

				return Err (
					"Required password file not provided".to_string ());

			}

			// read password from file

			let mut password_file =
				try! (
					io_result (
						File::open (
							password_file_path.unwrap ())));

			let mut password_string =
				String::new ();

			try! (
				io_result (
					password_file.read_to_string (
						& mut password_string)));

			// remove trailing newline

			if password_string.ends_with ("\n") {

				let password_length =
					password_string.len ();

				password_string.truncate (
					password_length - 1);

			}

			let password_bytes =
				password_string.as_bytes ();

			// derive password key from password

			let mut password_hmac =
				crypto::hmac::Hmac::new (
					crypto::sha1::Sha1::new (),
					password_bytes);

			let mut password_result =
				[0u8; KEY_SIZE];

			crypto::pbkdf2::pbkdf2 (
				& mut password_hmac,
				storage_info.get_encryption_key ().get_salt (),
				storage_info.get_encryption_key ().get_rounds (),
				& mut password_result);

			// decrypt actual key using password key

			let key_decryptor =
				crypto::aessafe::AesSafe128Decryptor::new (
					& password_result);

			let mut key_result =
				[0u8; KEY_SIZE];

			key_decryptor.decrypt_block (
				& storage_info.get_encryption_key ().get_encrypted_key (),
				& mut key_result);

			// derive check result to verify password

			let mut check_hmac =
				crypto::hmac::Hmac::new (
					crypto::sha1::Sha1::new (),
					& key_result);

			check_hmac.input (
				storage_info.get_encryption_key ().get_key_check_input ());

			let mut check_result =
				[0u8; 20];

			check_hmac.raw_result (
				& mut check_result);

			// error if the password does not verify

			let expected_result =
				storage_info.get_encryption_key ().get_key_check_hmac ();

			if check_result != expected_result {

				return Err (
					"Incorrect password".to_string ());

			}

		}

		// load indexes

		stderr! (
			"Loading indexes");

		let mut master_index: HashMap <[u8; 24], MasterIndexEntry> =
			HashMap::with_capacity (0x10000);

		let mut count: u64 = 0;

		for dir_entry_or_error in try! (
			io_result (
				fs::read_dir (
					format! (
						"{}/index",
						repository_path)))
		) {

			let dir_entry =
				try! (
					io_result (
						dir_entry_or_error));

			let file_name =
				dir_entry.file_name ();

			let index_name =
				file_name.to_str ().unwrap ();

			let index =
				try! (
					read_index (
						& format! (
							"{}/index/{}",
							repository_path,
							index_name)));

			for (index_bundle_header, bundle_info) in index {

				for chunk_record in bundle_info.get_chunk_record () {

					master_index.insert (
						to_array (chunk_record.get_id ()),
						MasterIndexEntry {

							bundle_id:
								to_array (index_bundle_header.get_id ()),

							size:
								chunk_record.get_size () as u64

						},
					);

				}

			}

			if count & 0x3f == 0x3f {
				stderr! (
					".");
			}

			count += 1;

		}

		stderr! (
			"\n");

		// return

		Ok (Repository {
			path: repository_path.to_string (),
			storage_info: storage_info,
			master_index: master_index,
			chunk_cache: HashMap::new (),
		})

	}

	pub fn read_and_expand_backup (
		& mut self,
		backup_name: & str,
	) -> Result <Vec <u8>, String> {

		// load backup

		stderr! (
			"Loading backup {}",
			backup_name);

		let backup_info =
			try! (
				read_backup_file (
					format! (
						"{}/backups/{}",
						& self.path,
						backup_name)
				).or_else (
					|error| {
						stderrln! ("");
						Err (error)
					}
				)
			);

		// expand backup data

		let mut input =
			Cursor::new (
				backup_info.get_backup_data ().to_owned ());

		for _iteration in 0 .. backup_info.get_iterations () {

			let mut temp_output: Cursor <Vec <u8>> =
				Cursor::new (
					Vec::new ());

			try! (
				self.follow_instructions (
					& mut input,
					& mut temp_output,
					& |count| {
						if count & 0xf == 0xf {
							stderr! (".");
						}
					}));

			input =
				Cursor::new (
					temp_output.into_inner ());

		}

		stderr! (
			"\n");

		Ok (input.into_inner ())

	}

	pub fn restore (
		& mut self,
		backup_name: & str,
		output: & mut Write,
	) -> Result <(), String> {

		let mut input =
			Cursor::new (
				try! (
					self.read_and_expand_backup (
						backup_name)));

		// restore backup

		stderr! (
			"Restoring backup");

		try! (
			self.follow_instructions (
				& mut input,
				output,
				& |count| {
					if count & 0x1ff == 0x1ff {
						stderr! (".");
					}
				}));

		stderr! (
			"\n");

		stderrln! (
			"Restore complete");

		Ok (())

	}

	pub fn restore_test (
		& mut self,
		backup_name: & str,
		output: & mut Write,
	) -> Result <(), String> {

		stderr! (
			"Loading backup {}",
			backup_name);

		let mut input =
			try! (
				RandomAccess::new (
					self,
					backup_name));

		let mut buffer: Vec <u8> =
			Vec::with_capacity (
				1024 * 1024);

		unsafe {

			buffer.set_len (
				1024 * 1024);

		}

		// restore backup

		stderr! (
			"Restoring backup");

		loop {

			let bytes_read =
				try! (
					io_result (
						input.read (
							& mut buffer)));

			if bytes_read == 0 {
				break;
			}

			try! (
				io_result (
					output.write (
						& buffer [
							0 .. bytes_read ])));

		}

		stderrln! (
			"Restore complete");

		Ok (())

	}

	pub fn follow_instruction (
		& mut self,
		backup_instruction: & proto::BackupInstruction,
		output: & mut Write,
	) -> Result <(), String> {

		if backup_instruction.has_chunk_to_emit () {

			let chunk_id =
				to_array (
					backup_instruction.get_chunk_to_emit ());

			let chunk_data = try! (
				self.get_chunk (
					chunk_id));

			try! (
				io_result (
					output.write (
						& chunk_data)));

		}

		if backup_instruction.has_bytes_to_emit () {

			try! (
				io_result (
					output.write (
						backup_instruction.get_bytes_to_emit ())));

		}

		Ok (())

	}

	pub fn follow_instructions (
		& mut self,
		input: & mut Read,
		output: & mut Write,
		progress: & Fn (u64),
	) -> Result <(), String> {

		let mut coded_input_stream =
			CodedInputStream::new (
				input);

		let mut count: u64 = 0;

		while ! try! (
			protobuf_result (
				coded_input_stream.eof ())
		) {

			let instruction_length =
				try! (
					protobuf_result (
						coded_input_stream.read_raw_varint32 ()));

			let instruction_old_limit =
				try! (
					protobuf_result (
						coded_input_stream.push_limit (
							instruction_length)));

			let backup_instruction =
				try! (
					protobuf_result (
						protobuf::core::parse_from::<proto::BackupInstruction> (
							&mut coded_input_stream)));

			coded_input_stream.pop_limit (
				instruction_old_limit);

			try! (
				self.follow_instruction (
					& backup_instruction,
					output));

			progress (
				count);

			count += 1;

		}

		Ok (())

	}

	pub fn get_chunk (
		& mut self,
		chunk_id: [u8; 24],
	) -> Result <Arc <Vec <u8>>, String> {

		if ! self.chunk_cache.contains_key (& chunk_id) {

			if self.chunk_cache.len () >= CACHE_MAX_SIZE {

				self.chunk_cache.clear ();

			}

			let index_entry = match
				self.master_index.get (& chunk_id) {

				Some (value) =>
					value,

				None => {

					return Err (
						format! (
							"Missing chunk: {}",
							chunk_id.to_hex ()));

				},

			};

			for (found_chunk_id, found_chunk_data) in try! (

				read_bundle (
					& format! (
						"{}/bundles/{}/{}",
						self.path,
						& index_entry.bundle_id.to_hex () [0 .. 2],
						index_entry.bundle_id.to_hex ()))

			) {

				self.chunk_cache.insert (
					found_chunk_id,
					Arc::new (
						found_chunk_data));

			}

		}

		let chunk_data =
			self.chunk_cache.get (
				& chunk_id,
			).unwrap ();

		Ok (
			chunk_data.clone ())

	}

	pub fn get_index_entry (
		& mut self,
		chunk_id: & [u8; 24],
	) -> Result <& MasterIndexEntry, String> {

		return match self.master_index.get (
			chunk_id,
		) {

			Some (value) =>
				Ok (value),

			None =>
				Err (
					format! (
						"Missing chunk: {}",
						chunk_id.to_hex ())
				),

		};

	}

	pub fn open_backup (
		& mut self,
		backup_name: & str,
	) -> Result <RandomAccess, String> {

		RandomAccess::new (
			self,
			backup_name)

	}

}
<|endoftext|>


<commit_before>use syntax::ast::expr::{Expr, ConstExpr, BlockExpr, TypeOfExpr, LocalExpr, VarDeclExpr, GetConstFieldExpr, GetFieldExpr, CallExpr, WhileLoopExpr, IfExpr, SwitchExpr, ObjectDeclExpr, ArrayDeclExpr, FunctionDeclExpr, ArrowFunctionDeclExpr, UnaryOpExpr, BinOpExpr, ConstructExpr, ReturnExpr, ThrowExpr, AssignExpr};
use syntax::ast::constant::{CNum, CInt, CString, CBool, CRegExp, CNull, CUndefined};
use syntax::ast::op::{OpSub, OpAdd, OpMul, OpDiv, OpMod};
use syntax::ast::op::{UnaryMinus, UnaryPlus, UnaryNot};
use syntax::ast::op::{BinNum, BinBit, BinLog, BinComp};
use syntax::ast::op::{BitAnd, BitOr, BitXor, BitShl, BitShr};
use syntax::ast::op::{LogAnd, LogOr};
use syntax::ast::op::{CompEqual, CompNotEqual, CompStrictEqual, CompStrictNotEqual, CompGreaterThan, CompGreaterThanOrEqual, CompLessThan, CompLessThanOrEqual};
use stdlib::value::{Value, ValueData, VNull, VUndefined, VString, VNumber, VInteger, VObject, VBoolean, VFunction, ResultValue, to_value, from_value, ToValue};
use stdlib::object::{INSTANCE_PROTOTYPE, PROTOTYPE};
use stdlib::function::{NativeFunc, RegularFunc, RegularFunction};
use stdlib::{console, math, object, array, function, json, number, error, uri, string};
use collections::treemap::TreeMap;
use std::vec::Vec;
use std::gc::Gc;
use std::cell::RefCell;
use jit::{Context, Function, Type, Types, CDECL};
use jit;
/// A variable scope
pub struct Scope {
	/// The value of `this` in the scope
	pub this: Value,
	/// The variables declared in the scope
	pub vars: Value
}
/// An execution engine
pub trait Executor<T> {
	/// Make a new execution engine
	fn new() -> Self;
	/// Set a global variable called `name` with the value `val`
	fn set_global(&mut self, name:String, val:Value) -> Value;
	/// Resolve the global variable `name`
	fn get_global(&self, name:String) -> Value;
	/// Create a new scope and return it
	fn make_scope(&mut self, this:Value) -> Scope;
	/// Destroy the current scope
	fn destroy_scope(&mut self) -> Scope;
	/// Compile the expression
	fn compile(&self, expr:&Expr) -> Box<T>;
	/// Run an expression
	fn run(&mut self, comp:Box<T>) -> ResultValue;
}
/// A Javascript JIT compiler
pub struct JITCompiler {
	/// The JIT Context
	context: Box<Context>,
	/// An object representing the global object
	pub global: Value,
	/// The scopes
	pub scopes: Vec<Scope>
}
impl JITCompiler {
	#[inline(always)]
	/// Get the current scope
	pub fn scope(&self) -> Scope {
		*self.scopes.get(self.scopes.len() - 1)
	}
	fn with_builder<R>(&self, cb: || -> R) -> R {
		self.context.build_start();
		let rv = cb();
		self.context.build_end();
		rv
	}
}
impl Executor<Function> for JITCompiler {
	fn new() -> JITCompiler {
		let global = ValueData::new_obj(None);
		object::init(global);
		console::init(global);
		math::init(global);
		array::init(global);
		function::init(global);
		json::init(global);
		number::init(global);
		error::init(global);
		string::init(global);
		uri::init(global);
		JITCompiler {
			context: Context::new(),
			global: global,
			scopes: vec!(Scope {
				this: global,
				vars: global
			})
		}
	}
	#[inline(always)]
	fn set_global(&mut self, name:String, val:Value) -> Value {
		self.global.borrow().set_field(name, val)
	}
	#[inline(always)]
	fn get_global(&self, name:String) -> Value {
		self.global.borrow().get_field(name)
	}
	fn make_scope(&mut self, this:Value) -> Scope {
		let scope = Scope {
			this: this,
			vars: ValueData::new_obj(None)
		};
		self.scopes.push(scope);
		scope
	}
	#[inline(always)]
	fn destroy_scope(&mut self) -> Scope {
		self.scopes.pop().unwrap()
	}
	fn compile(&self, expr: &Expr) -> Box<Function> {
		self.with_builder(|| {
			let valuedata_t = Types::get_int();
			let valuedata_ptr_t = Type::create_pointer(&*valuedata_t);
			let value_t = Type::create_struct(&[&*valuedata_ptr_t]);
			let default_sig_t = Type::create_signature(CDECL, Types::get_void(), &[&*valuedata_ptr_t]);
			fn compile_value(func:&Function, expr: &Expr) -> Box<jit::Value> {
				let valuedata_t = Types::get_int();
				let valuedata_ptr_t = Type::create_pointer(&*valuedata_t);
				let value_t = Type::create_struct(&[&*valuedata_ptr_t]);
				match expr.def {
					ConstExpr(CNull) => {
						fn create_null_value() -> Value {
							Gc::new(VNull)
						}
						let create_null_sig = Type::create_signature(CDECL, &*value_t, &[]);
						func.insn_call_native0("create_null_value", create_null_value, &*create_null_sig, &[])
					},
					ConstExpr(CUndefined) => {
						fn create_undef_value() -> Value {
							Gc::new(VUndefined)
						}
						let create_undef_sig = Type::create_signature(CDECL, &*value_t, &[]);
						func.insn_call_native0("create_undef_value", create_undef_value, &*create_undef_sig, &[])
					},
					ConstExpr(CBool(v)) => {
						let create_bool_value = to_value::<bool>;
						let val = func.constant_int32_as_type(v as i32, &*Types::get_bool());
						let create_bool_sig = Type::create_signature(CDECL, &*value_t, &[&*Types::get_bool()]);
						func.insn_call_native1("create_bool_value", create_bool_value, &*create_bool_sig, &[&*val])
					},
					ConstExpr(CNum(n)) => {
						let create_number_value = to_value::<f64>;
						let val = func.constant_float64(n);
						let create_number_sig = Type::create_signature(CDECL, &*value_t, &[&*Types::get_float64()]);
						func.insn_call_native1("create_number_value", create_number_value, &* create_number_sig, &[&*val])
					},
					UnaryOpExpr(op, ref a) => {
						let i_a = compile_value(func, *a);
						let unop_sig = Type::create_signature(CDECL, &*value_t, &[&*value_t]);
						let (name, op_func) = match op {
							UnaryPlus => return i_a,
							UnaryMinus => {
								fn neg_value(a:Value) -> Value {
									Gc::new(-a.borrow())
								}
								("neg", neg_value)
							}
							UnaryNot => {
								fn not_value(a: Value) -> Value {
									Gc::new(!a.borrow())
								}
								("not", not_value)
							},
							_ => fail!("Unimplemented {}", op)
						};
						func.insn_call_native1(name, op_func, unop_sig, &[&*i_a])
					},
					BinOpExpr(op, ref a, ref b) => {
						let i_a = compile_value(func, *a);
						let i_b = compile_value(func, *b);
						let binop_sig = Type::create_signature(CDECL, &*value_t, &[&*value_t, &*value_t]);

						let (name, op_func) = match op {
							BinNum(OpAdd) => {
								fn add_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() + *b.borrow())
								}
								("add", add_values)
							},
							BinNum(OpSub) => {
								fn sub_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() - *b.borrow())
								}
								("sub", sub_values)
							},
							BinNum(OpMul) => {
								fn mul_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() * *b.borrow())
								}
								("mul", mul_values)
							},
							BinNum(OpDiv) => {
								fn div_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() / *b.borrow())
								}
								("div", div_values)
							},
							BinNum(OpMod) => {
								fn mod_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() % *b.borrow())
								}
								("mod", mod_values)
							},
							BinBit(BitAnd) => {
								fn and_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() & *b.borrow())
								}
								("and", and_values)
							},
							BinBit(BitOr) => {
								fn or_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() | *b.borrow())
								}
								("or", or_values)
							},
							BinBit(BitXor) => {
								fn xor_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() ^ *b.borrow())
								}
								("xor", xor_values)
							},
							BinBit(BitShl) => {
								fn shl_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() << *b.borrow())
								}
								("shl", shl_values)
							},
							BinBit(BitShr) => {
								fn shr_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() >> *b.borrow())
								}
								("shr", shr_values)
							},
							BinLog(LogOr) => {
								fn or_values(a: Value, b:Value) -> Value {
									to_value(a.borrow().is_true() || b.borrow().is_true())
								}
								("or", or_values)
							},
							BinLog(LogAnd) => {
								fn and_values(a: Value, b:Value) -> Value {
									to_value(a.borrow().is_true() && b.borrow().is_true())
								}
								("and", and_values)
							},
							BinComp(CompEqual) | BinComp(CompStrictEqual) => {
								fn eq_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() == b.borrow())
								}
								("eq", eq_values)
							},
							BinComp(CompNotEqual) | BinComp(CompStrictNotEqual) => {
								fn neq_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() != b.borrow())
								}
								("neq", neq_values)
							},
							BinComp(CompLessThan) => {
								fn lt_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() < b.borrow())
								}
								("lt", lt_values)
							},
							BinComp(CompLessThanOrEqual) => {
								fn lte_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() <= b.borrow())
								}
								("lte", lte_values)
							},
							BinComp(CompGreaterThan) => {
								fn gt_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() > b.borrow())
								}
								("gt", gt_values)
							},
							BinComp(CompGreaterThanOrEqual) => {
								fn gte_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() >= b.borrow())
								}
								("gte", gte_values)
							}
						};
						func.insn_call_native2(name, op_func, binop_sig, &[&*i_a, &*i_b])
					},
					_ => fail!("Unimplemented {}", expr)
				}
			}
			let func = self.context.create_function(&*default_sig_t);
			let result = func.get_param(0);
			let value = compile_value(func, expr);
			func.insn_store_relative(&result, 0, &*value);
			func.compile();
			func
		})
	}
	fn run(&mut self, comp:Box<Function>) -> ResultValue {
		let func: fn(&mut Value) = comp.closure();
		let mut rv: Value = Gc::new(VUndefined);
		func(&mut rv);
		return Ok(rv);
	}
}<commit_msg>Restructure JIT Compiler to return the value instead of setting a pointer to it<commit_after>use syntax::ast::expr::{Expr, ConstExpr, BlockExpr, TypeOfExpr, LocalExpr, VarDeclExpr, GetConstFieldExpr, GetFieldExpr, CallExpr, WhileLoopExpr, IfExpr, SwitchExpr, ObjectDeclExpr, ArrayDeclExpr, FunctionDeclExpr, ArrowFunctionDeclExpr, UnaryOpExpr, BinOpExpr, ConstructExpr, ReturnExpr, ThrowExpr, AssignExpr};
use syntax::ast::constant::{CNum, CInt, CString, CBool, CRegExp, CNull, CUndefined};
use syntax::ast::op::{OpSub, OpAdd, OpMul, OpDiv, OpMod};
use syntax::ast::op::{UnaryMinus, UnaryPlus, UnaryNot};
use syntax::ast::op::{BinNum, BinBit, BinLog, BinComp};
use syntax::ast::op::{BitAnd, BitOr, BitXor, BitShl, BitShr};
use syntax::ast::op::{LogAnd, LogOr};
use syntax::ast::op::{CompEqual, CompNotEqual, CompStrictEqual, CompStrictNotEqual, CompGreaterThan, CompGreaterThanOrEqual, CompLessThan, CompLessThanOrEqual};
use stdlib::value::{Value, ValueData, VNull, VUndefined, VString, VNumber, VInteger, VObject, VBoolean, VFunction, ResultValue, to_value, from_value, ToValue};
use stdlib::object::{INSTANCE_PROTOTYPE, PROTOTYPE};
use stdlib::function::{NativeFunc, RegularFunc, RegularFunction};
use stdlib::{console, math, object, array, function, json, number, error, uri, string};
use collections::treemap::TreeMap;
use std::vec::Vec;
use std::gc::Gc;
use std::cell::RefCell;
use jit::{Context, Function, Type, Types, CDECL};
use jit;
/// A variable scope
pub struct Scope {
	/// The value of `this` in the scope
	pub this: Value,
	/// The variables declared in the scope
	pub vars: Value
}
/// An execution engine
pub trait Executor<T> {
	/// Make a new execution engine
	fn new() -> Self;
	/// Set a global variable called `name` with the value `val`
	fn set_global(&mut self, name:String, val:Value) -> Value;
	/// Resolve the global variable `name`
	fn get_global(&self, name:String) -> Value;
	/// Create a new scope and return it
	fn make_scope(&mut self, this:Value) -> Scope;
	/// Destroy the current scope
	fn destroy_scope(&mut self) -> Scope;
	/// Compile the expression
	fn compile(&self, expr:&Expr) -> Box<T>;
	/// Run an expression
	fn run(&mut self, comp:Box<T>) -> ResultValue;
}
/// A Javascript JIT compiler
pub struct JITCompiler {
	/// The JIT Context
	context: Box<Context>,
	/// An object representing the global object
	pub global: Value,
	/// The scopes
	pub scopes: Vec<Scope>
}
impl JITCompiler {
	#[inline(always)]
	/// Get the current scope
	pub fn scope(&self) -> Scope {
		*self.scopes.get(self.scopes.len() - 1)
	}
	fn with_builder<R>(&self, cb: || -> R) -> R {
		self.context.build_start();
		let rv = cb();
		self.context.build_end();
		rv
	}
}
impl Executor<Function> for JITCompiler {
	fn new() -> JITCompiler {
		let global = ValueData::new_obj(None);
		object::init(global);
		console::init(global);
		math::init(global);
		array::init(global);
		function::init(global);
		json::init(global);
		number::init(global);
		error::init(global);
		string::init(global);
		uri::init(global);
		JITCompiler {
			context: Context::new(),
			global: global,
			scopes: vec!(Scope {
				this: global,
				vars: global
			})
		}
	}
	#[inline(always)]
	fn set_global(&mut self, name:String, val:Value) -> Value {
		self.global.borrow().set_field(name, val)
	}
	#[inline(always)]
	fn get_global(&self, name:String) -> Value {
		self.global.borrow().get_field(name)
	}
	fn make_scope(&mut self, this:Value) -> Scope {
		let scope = Scope {
			this: this,
			vars: ValueData::new_obj(None)
		};
		self.scopes.push(scope);
		scope
	}
	#[inline(always)]
	fn destroy_scope(&mut self) -> Scope {
		self.scopes.pop().unwrap()
	}
	fn compile(&self, expr: &Expr) -> Box<Function> {
		self.with_builder(|| {
			let valuedata_t = Types::get_int();
			let valuedata_ptr_t = Type::create_pointer(&*valuedata_t);
			let value_t = Type::create_struct(&[&*valuedata_ptr_t]);
			let default_sig_t = Type::create_signature(CDECL, &*value_t, &[]);
			fn compile_value(func:&Function, expr: &Expr) -> Box<jit::Value> {
				let valuedata_t = Types::get_int();
				let valuedata_ptr_t = Type::create_pointer(&*valuedata_t);
				let value_t = Type::create_struct(&[&*valuedata_ptr_t]);
				match expr.def {
					ConstExpr(CNull) => {
						fn create_null_value() -> Value {
							Gc::new(VNull)
						}
						let create_null_sig = Type::create_signature(CDECL, &*value_t, &[]);
						func.insn_call_native0("create_null_value", create_null_value, &*create_null_sig, &[])
					},
					ConstExpr(CUndefined) => {
						fn create_undef_value() -> Value {
							Gc::new(VUndefined)
						}
						let create_undef_sig = Type::create_signature(CDECL, &*value_t, &[]);
						func.insn_call_native0("create_undef_value", create_undef_value, &*create_undef_sig, &[])
					},
					ConstExpr(CBool(v)) => {
						let create_bool_value = to_value::<bool>;
						let val = func.constant_int32_as_type(v as i32, &*Types::get_bool());
						let create_bool_sig = Type::create_signature(CDECL, &*value_t, &[&*Types::get_bool()]);
						func.insn_call_native1("create_bool_value", create_bool_value, &*create_bool_sig, &[&*val])
					},
					ConstExpr(CNum(n)) => {
						let create_number_value = to_value::<f64>;
						let val = func.constant_float64(n);
						let create_number_sig = Type::create_signature(CDECL, &*value_t, &[&*Types::get_float64()]);
						func.insn_call_native1("create_number_value", create_number_value, &* create_number_sig, &[&*val])
					},
					UnaryOpExpr(op, ref a) => {
						let i_a = compile_value(func, *a);
						let unop_sig = Type::create_signature(CDECL, &*value_t, &[&*value_t]);
						let (name, op_func) = match op {
							UnaryPlus => return i_a,
							UnaryMinus => {
								fn neg_value(a:Value) -> Value {
									Gc::new(-a.borrow())
								}
								("neg", neg_value)
							}
							UnaryNot => {
								fn not_value(a: Value) -> Value {
									Gc::new(!a.borrow())
								}
								("not", not_value)
							},
							_ => fail!("Unimplemented {}", op)
						};
						func.insn_call_native1(name, op_func, unop_sig, &[&*i_a])
					},
					BinOpExpr(op, ref a, ref b) => {
						let i_a = compile_value(func, *a);
						let i_b = compile_value(func, *b);
						let binop_sig = Type::create_signature(CDECL, &*value_t, &[&*value_t, &*value_t]);

						let (name, op_func) = match op {
							BinNum(OpAdd) => {
								fn add_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() + *b.borrow())
								}
								("add", add_values)
							},
							BinNum(OpSub) => {
								fn sub_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() - *b.borrow())
								}
								("sub", sub_values)
							},
							BinNum(OpMul) => {
								fn mul_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() * *b.borrow())
								}
								("mul", mul_values)
							},
							BinNum(OpDiv) => {
								fn div_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() / *b.borrow())
								}
								("div", div_values)
							},
							BinNum(OpMod) => {
								fn mod_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() % *b.borrow())
								}
								("mod", mod_values)
							},
							BinBit(BitAnd) => {
								fn and_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() & *b.borrow())
								}
								("and", and_values)
							},
							BinBit(BitOr) => {
								fn or_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() | *b.borrow())
								}
								("or", or_values)
							},
							BinBit(BitXor) => {
								fn xor_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() ^ *b.borrow())
								}
								("xor", xor_values)
							},
							BinBit(BitShl) => {
								fn shl_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() << *b.borrow())
								}
								("shl", shl_values)
							},
							BinBit(BitShr) => {
								fn shr_values(a: Value, b:Value) -> Value {
									Gc::new(a.borrow() >> *b.borrow())
								}
								("shr", shr_values)
							},
							BinLog(LogOr) => {
								fn or_values(a: Value, b:Value) -> Value {
									to_value(a.borrow().is_true() || b.borrow().is_true())
								}
								("or", or_values)
							},
							BinLog(LogAnd) => {
								fn and_values(a: Value, b:Value) -> Value {
									to_value(a.borrow().is_true() && b.borrow().is_true())
								}
								("and", and_values)
							},
							BinComp(CompEqual) | BinComp(CompStrictEqual) => {
								fn eq_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() == b.borrow())
								}
								("eq", eq_values)
							},
							BinComp(CompNotEqual) | BinComp(CompStrictNotEqual) => {
								fn neq_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() != b.borrow())
								}
								("neq", neq_values)
							},
							BinComp(CompLessThan) => {
								fn lt_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() < b.borrow())
								}
								("lt", lt_values)
							},
							BinComp(CompLessThanOrEqual) => {
								fn lte_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() <= b.borrow())
								}
								("lte", lte_values)
							},
							BinComp(CompGreaterThan) => {
								fn gt_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() > b.borrow())
								}
								("gt", gt_values)
							},
							BinComp(CompGreaterThanOrEqual) => {
								fn gte_values(a: Value, b:Value) -> Value {
									to_value(a.borrow() >= b.borrow())
								}
								("gte", gte_values)
							}
						};
						func.insn_call_native2(name, op_func, binop_sig, &[&*i_a, &*i_b])
					},
					_ => fail!("Unimplemented {}", expr)
				}
			}
			let func = self.context.create_function(&*default_sig_t);
			let value = compile_value(func, expr);
			func.insn_return(&*value);
			func.compile();
			func
		})
	}
	fn run(&mut self, comp:Box<Function>) -> ResultValue {
		let func: fn() -> Value = comp.closure();
		return Ok(func());
	}
}<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module implements the `Any` trait, which enables dynamic typing
//! of any type.

use cast::transmute;
use option::{Option, Some, None};
use to_str::ToStr;
use unstable::intrinsics;
use util::Void;
use unstable::intrinsics::TypeId;

///////////////////////////////////////////////////////////////////////////////
// Any trait
///////////////////////////////////////////////////////////////////////////////

/// The `Any` trait is implemented by all types, and can be used as a trait object
/// for dynamic typing
pub trait Any {
    /// Get the `TypeId` of `self`
    fn get_type_id(&self) -> TypeId;

    /// Get a void pointer to `self`
    fn as_void_ptr(&self) -> *Void;

    /// Get a mutable void pointer to `self`
    fn as_mut_void_ptr(&mut self) -> *mut Void;
}

impl<T: 'static> Any for T {
    /// Get the `TypeId` of `self`
    fn get_type_id(&self) -> TypeId {
        TypeId::of::<T>()
    }

    /// Get a void pointer to `self`
    fn as_void_ptr(&self) -> *Void {
        self as *T as *Void
    }

    /// Get a mutable void pointer to `self`
    fn as_mut_void_ptr(&mut self) -> *mut Void {
        self as *mut T as *mut Void
    }
}

///////////////////////////////////////////////////////////////////////////////
// Extension methods for Any trait objects.
// Implemented as three extension traits so that generics work.
///////////////////////////////////////////////////////////////////////////////

/// Extension methods for a referenced `Any` trait object
pub trait AnyRefExt<'self> {
    /// Returns true if the boxed type is the same as `T`
    fn is<T: 'static>(self) -> bool;

    /// Returns some reference to the boxed value if it is of type `T`, or
    /// `None` if it isn't.
    fn as_ref<T: 'static>(self) -> Option<&'self T>;
}

impl<'self> AnyRefExt<'self> for &'self Any {
    #[inline]
    fn is<T: 'static>(self) -> bool {
        // Get TypeId of the type this function is instantiated with
        let t = TypeId::of::<T>();

        // Get TypeId of the type in the trait object
        let boxed = self.get_type_id();

        // Compare both TypeIds on equality
        t == boxed
    }

    #[inline]
    fn as_ref<T: 'static>(self) -> Option<&'self T> {
        if self.is::<T>() {
            Some(unsafe { transmute(self.as_void_ptr()) })
        } else {
            None
        }
    }
}

/// Extension methods for a mutable referenced `Any` trait object
pub trait AnyMutRefExt<'self> {
    /// Returns some mutable reference to the boxed value if it is of type `T`, or
    /// `None` if it isn't.
    fn as_mut<T: 'static>(self) -> Option<&'self mut T>;
}

impl<'self> AnyMutRefExt<'self> for &'self mut Any {
    #[inline]
    fn as_mut<T: 'static>(self) -> Option<&'self mut T> {
        if self.is::<T>() {
            Some(unsafe { transmute(self.as_mut_void_ptr()) })
        } else {
            None
        }
    }
}

/// Extension methods for a owning `Any` trait object
pub trait AnyOwnExt {
    /// Returns the boxed value if it is of type `T`, or
    /// `None` if it isn't.
    fn move<T: 'static>(self) -> Option<~T>;
}

impl AnyOwnExt for ~Any {
    #[inline]
    fn move<T: 'static>(self) -> Option<~T> {
        if self.is::<T>() {
            unsafe {
                // Extract the pointer to the boxed value, temporary alias with self
                let ptr: ~T = transmute(self.as_void_ptr());

                // Prevent destructor on self being run
                intrinsics::forget(self);

                Some(ptr)
            }
        } else {
            None
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// Trait implementations
///////////////////////////////////////////////////////////////////////////////

impl ToStr for ~Any {
    fn to_str(&self) -> ~str { ~"~Any" }
}

impl<'self> ToStr for &'self Any {
    fn to_str(&self) -> ~str { ~"&Any" }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::AnyRefExt;
    use option::{Some, None};

    #[deriving(Eq)]
    struct Test;

    static TEST: &'static str = "Test";

    #[test]
    fn any_as_void_ptr() {
        let (a, b, c) = (~5u as ~Any, ~TEST as ~Any, ~Test as ~Any);
        let a_r: &Any = a;
        let b_r: &Any = b;
        let c_r: &Any = c;

        assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
        assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
        assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());

        let (a, b, c) = (@5u as @Any, @TEST as @Any, @Test as @Any);
        let a_r: &Any = a;
        let b_r: &Any = b;
        let c_r: &Any = c;

        assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
        assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
        assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());

        let (a, b, c) = (&5u as &Any, &TEST as &Any, &Test as &Any);
        let a_r: &Any = a;
        let b_r: &Any = b;
        let c_r: &Any = c;

        assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
        assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
        assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());

        let mut x = Test;
        let mut y: &'static str = "Test";
        let (a, b, c) = (&mut 5u as &mut Any,
                         &mut y as &mut Any,
                         &mut x as &mut Any);
        let a_r: &Any = a;
        let b_r: &Any = b;
        let c_r: &Any = c;

        assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
        assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
        assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());

        let (a, b, c) = (5u, "hello", Test);
        let (a_r, b_r, c_r) = (&a as &Any, &b as &Any, &c as &Any);

        assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
        assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
        assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());
    }

    #[test]
    fn any_as_mut_void_ptr() {
        let y: &'static str = "Test";
        let mut a = ~5u as ~Any;
        let mut b = ~y as ~Any;
        let mut c = ~Test as ~Any;

        let a_ptr = a.as_mut_void_ptr();
        let b_ptr = b.as_mut_void_ptr();
        let c_ptr = c.as_mut_void_ptr();

        let a_r: &mut Any = a;
        let b_r: &mut Any = b;
        let c_r: &mut Any = c;

        assert_eq!(a_ptr, a_r.as_mut_void_ptr());
        assert_eq!(b_ptr, b_r.as_mut_void_ptr());
        assert_eq!(c_ptr, c_r.as_mut_void_ptr());

        let mut x = Test;
        let mut y: &'static str = "Test";
        let a = &mut 5u as &mut Any;
        let b = &mut y as &mut Any;
        let c = &mut x as &mut Any;

        let a_ptr = a.as_mut_void_ptr();
        let b_ptr = b.as_mut_void_ptr();
        let c_ptr = c.as_mut_void_ptr();

        let a_r: &mut Any = a;
        let b_r: &mut Any = b;
        let c_r: &mut Any = c;

        assert_eq!(a_ptr, a_r.as_mut_void_ptr());
        assert_eq!(b_ptr, b_r.as_mut_void_ptr());
        assert_eq!(c_ptr, c_r.as_mut_void_ptr());

        let y: &'static str = "Test";
        let mut a = 5u;
        let mut b = y;
        let mut c = Test;

        let a_ptr = a.as_mut_void_ptr();
        let b_ptr = b.as_mut_void_ptr();
        let c_ptr = c.as_mut_void_ptr();

        let (a_r, b_r, c_r) = (&mut a as &mut Any, &mut b as &mut Any, &mut c as &mut Any);

        assert_eq!(a_ptr, a_r.as_mut_void_ptr());
        assert_eq!(b_ptr, b_r.as_mut_void_ptr());
        assert_eq!(c_ptr, c_r.as_mut_void_ptr());
    }

    #[test]
    fn any_referenced() {
        let (a, b, c) = (&5u as &Any, &TEST as &Any, &Test as &Any);

        assert!(a.is::<uint>());
        assert!(!b.is::<uint>());
        assert!(!c.is::<uint>());

        assert!(!a.is::<&'static str>());
        assert!(b.is::<&'static str>());
        assert!(!c.is::<&'static str>());

        assert!(!a.is::<Test>());
        assert!(!b.is::<Test>());
        assert!(c.is::<Test>());
    }

    #[test]
    fn any_owning() {
        let (a, b, c) = (~5u as ~Any, ~TEST as ~Any, ~Test as ~Any);

        assert!(a.is::<uint>());
        assert!(!b.is::<uint>());
        assert!(!c.is::<uint>());

        assert!(!a.is::<&'static str>());
        assert!(b.is::<&'static str>());
        assert!(!c.is::<&'static str>());

        assert!(!a.is::<Test>());
        assert!(!b.is::<Test>());
        assert!(c.is::<Test>());
    }

    #[test]
    fn any_managed() {
        let (a, b, c) = (@5u as @Any, @TEST as @Any, @Test as @Any);

        assert!(a.is::<uint>());
        assert!(!b.is::<uint>());
        assert!(!c.is::<uint>());

        assert!(!a.is::<&'static str>());
        assert!(b.is::<&'static str>());
        assert!(!c.is::<&'static str>());

        assert!(!a.is::<Test>());
        assert!(!b.is::<Test>());
        assert!(c.is::<Test>());
    }

    #[test]
    fn any_as_ref() {
        let a = &5u as &Any;

        match a.as_ref::<uint>() {
            Some(&5) => {}
            x => fail!("Unexpected value {:?}", x)
        }

        match a.as_ref::<Test>() {
            None => {}
            x => fail!("Unexpected value {:?}", x)
        }
    }

    #[test]
    fn any_as_mut() {
        let mut a = 5u;
        let mut b = ~7u;

        let a_r = &mut a as &mut Any;
        let tmp: &mut uint = b;
        let b_r = tmp as &mut Any;

        match a_r.as_mut::<uint>() {
            Some(x) => {
                assert_eq!(*x, 5u);
                *x = 612;
            }
            x => fail!("Unexpected value {:?}", x)
        }

        match b_r.as_mut::<uint>() {
            Some(x) => {
                assert_eq!(*x, 7u);
                *x = 413;
            }
            x => fail!("Unexpected value {:?}", x)
        }

        match a_r.as_mut::<Test>() {
            None => (),
            x => fail!("Unexpected value {:?}", x)
        }

        match b_r.as_mut::<Test>() {
            None => (),
            x => fail!("Unexpected value {:?}", x)
        }

        match a_r.as_mut::<uint>() {
            Some(&612) => {}
            x => fail!("Unexpected value {:?}", x)
        }

        match b_r.as_mut::<uint>() {
            Some(&413) => {}
            x => fail!("Unexpected value {:?}", x)
        }
    }

    #[test]
    fn any_move() {
        let a = ~8u as ~Any;
        let b = ~Test as ~Any;

        assert_eq!(a.move(), Some(~8u));
        assert_eq!(b.move(), Some(~Test));

        let a = ~8u as ~Any;
        let b = ~Test as ~Any;

        assert_eq!(a.move(), None::<~Test>);
        assert_eq!(b.move(), None::<~uint>);
    }
}
<commit_msg>auto merge of #10840 : cmr/rust/any_docs2, r=huonw<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module implements the `Any` trait, which enables dynamic typing
//! of any type, through runtime reflection.
//!
//! `Any` itself can be used to get a `TypeId`, and has more features when used as a trait object.
//! As `&Any` (a borrowed trait object), it has the `is` and `as_ref` methods, to test if the
//! contained value is of a given type, and to get a reference to the inner value as a type. As
//! `&mut Any`, there is also the `as_mut` method, for getting a mutable reference to the inner
//! value. `~Any` adds the `move` method, which will unwrap a `~T` from the object.  See the
//! extension traits (`*Ext`) for the full details.

use cast::transmute;
use option::{Option, Some, None};
use to_str::ToStr;
use unstable::intrinsics;
use util::Void;
use unstable::intrinsics::TypeId;

///////////////////////////////////////////////////////////////////////////////
// Any trait
///////////////////////////////////////////////////////////////////////////////

/// The `Any` trait is implemented by all types, and can be used as a trait object
/// for dynamic typing
pub trait Any {
    /// Get the `TypeId` of `self`
    fn get_type_id(&self) -> TypeId;

    /// Get a void pointer to `self`
    fn as_void_ptr(&self) -> *Void;

    /// Get a mutable void pointer to `self`
    fn as_mut_void_ptr(&mut self) -> *mut Void;
}

impl<T: 'static> Any for T {
    /// Get the `TypeId` of `self`
    fn get_type_id(&self) -> TypeId {
        TypeId::of::<T>()
    }

    /// Get a void pointer to `self`
    fn as_void_ptr(&self) -> *Void {
        self as *T as *Void
    }

    /// Get a mutable void pointer to `self`
    fn as_mut_void_ptr(&mut self) -> *mut Void {
        self as *mut T as *mut Void
    }
}

///////////////////////////////////////////////////////////////////////////////
// Extension methods for Any trait objects.
// Implemented as three extension traits so that generics work.
///////////////////////////////////////////////////////////////////////////////

/// Extension methods for a referenced `Any` trait object
pub trait AnyRefExt<'self> {
    /// Returns true if the boxed type is the same as `T`
    fn is<T: 'static>(self) -> bool;

    /// Returns some reference to the boxed value if it is of type `T`, or
    /// `None` if it isn't.
    fn as_ref<T: 'static>(self) -> Option<&'self T>;
}

impl<'self> AnyRefExt<'self> for &'self Any {
    #[inline]
    fn is<T: 'static>(self) -> bool {
        // Get TypeId of the type this function is instantiated with
        let t = TypeId::of::<T>();

        // Get TypeId of the type in the trait object
        let boxed = self.get_type_id();

        // Compare both TypeIds on equality
        t == boxed
    }

    #[inline]
    fn as_ref<T: 'static>(self) -> Option<&'self T> {
        if self.is::<T>() {
            Some(unsafe { transmute(self.as_void_ptr()) })
        } else {
            None
        }
    }
}

/// Extension methods for a mutable referenced `Any` trait object
pub trait AnyMutRefExt<'self> {
    /// Returns some mutable reference to the boxed value if it is of type `T`, or
    /// `None` if it isn't.
    fn as_mut<T: 'static>(self) -> Option<&'self mut T>;
}

impl<'self> AnyMutRefExt<'self> for &'self mut Any {
    #[inline]
    fn as_mut<T: 'static>(self) -> Option<&'self mut T> {
        if self.is::<T>() {
            Some(unsafe { transmute(self.as_mut_void_ptr()) })
        } else {
            None
        }
    }
}

/// Extension methods for a owning `Any` trait object
pub trait AnyOwnExt {
    /// Returns the boxed value if it is of type `T`, or
    /// `None` if it isn't.
    fn move<T: 'static>(self) -> Option<~T>;
}

impl AnyOwnExt for ~Any {
    #[inline]
    fn move<T: 'static>(self) -> Option<~T> {
        if self.is::<T>() {
            unsafe {
                // Extract the pointer to the boxed value, temporary alias with self
                let ptr: ~T = transmute(self.as_void_ptr());

                // Prevent destructor on self being run
                intrinsics::forget(self);

                Some(ptr)
            }
        } else {
            None
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
// Trait implementations
///////////////////////////////////////////////////////////////////////////////

impl ToStr for ~Any {
    fn to_str(&self) -> ~str { ~"~Any" }
}

impl<'self> ToStr for &'self Any {
    fn to_str(&self) -> ~str { ~"&Any" }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::AnyRefExt;
    use option::{Some, None};

    #[deriving(Eq)]
    struct Test;

    static TEST: &'static str = "Test";

    #[test]
    fn any_as_void_ptr() {
        let (a, b, c) = (~5u as ~Any, ~TEST as ~Any, ~Test as ~Any);
        let a_r: &Any = a;
        let b_r: &Any = b;
        let c_r: &Any = c;

        assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
        assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
        assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());

        let (a, b, c) = (@5u as @Any, @TEST as @Any, @Test as @Any);
        let a_r: &Any = a;
        let b_r: &Any = b;
        let c_r: &Any = c;

        assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
        assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
        assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());

        let (a, b, c) = (&5u as &Any, &TEST as &Any, &Test as &Any);
        let a_r: &Any = a;
        let b_r: &Any = b;
        let c_r: &Any = c;

        assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
        assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
        assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());

        let mut x = Test;
        let mut y: &'static str = "Test";
        let (a, b, c) = (&mut 5u as &mut Any,
                         &mut y as &mut Any,
                         &mut x as &mut Any);
        let a_r: &Any = a;
        let b_r: &Any = b;
        let c_r: &Any = c;

        assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
        assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
        assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());

        let (a, b, c) = (5u, "hello", Test);
        let (a_r, b_r, c_r) = (&a as &Any, &b as &Any, &c as &Any);

        assert_eq!(a.as_void_ptr(), a_r.as_void_ptr());
        assert_eq!(b.as_void_ptr(), b_r.as_void_ptr());
        assert_eq!(c.as_void_ptr(), c_r.as_void_ptr());
    }

    #[test]
    fn any_as_mut_void_ptr() {
        let y: &'static str = "Test";
        let mut a = ~5u as ~Any;
        let mut b = ~y as ~Any;
        let mut c = ~Test as ~Any;

        let a_ptr = a.as_mut_void_ptr();
        let b_ptr = b.as_mut_void_ptr();
        let c_ptr = c.as_mut_void_ptr();

        let a_r: &mut Any = a;
        let b_r: &mut Any = b;
        let c_r: &mut Any = c;

        assert_eq!(a_ptr, a_r.as_mut_void_ptr());
        assert_eq!(b_ptr, b_r.as_mut_void_ptr());
        assert_eq!(c_ptr, c_r.as_mut_void_ptr());

        let mut x = Test;
        let mut y: &'static str = "Test";
        let a = &mut 5u as &mut Any;
        let b = &mut y as &mut Any;
        let c = &mut x as &mut Any;

        let a_ptr = a.as_mut_void_ptr();
        let b_ptr = b.as_mut_void_ptr();
        let c_ptr = c.as_mut_void_ptr();

        let a_r: &mut Any = a;
        let b_r: &mut Any = b;
        let c_r: &mut Any = c;

        assert_eq!(a_ptr, a_r.as_mut_void_ptr());
        assert_eq!(b_ptr, b_r.as_mut_void_ptr());
        assert_eq!(c_ptr, c_r.as_mut_void_ptr());

        let y: &'static str = "Test";
        let mut a = 5u;
        let mut b = y;
        let mut c = Test;

        let a_ptr = a.as_mut_void_ptr();
        let b_ptr = b.as_mut_void_ptr();
        let c_ptr = c.as_mut_void_ptr();

        let (a_r, b_r, c_r) = (&mut a as &mut Any, &mut b as &mut Any, &mut c as &mut Any);

        assert_eq!(a_ptr, a_r.as_mut_void_ptr());
        assert_eq!(b_ptr, b_r.as_mut_void_ptr());
        assert_eq!(c_ptr, c_r.as_mut_void_ptr());
    }

    #[test]
    fn any_referenced() {
        let (a, b, c) = (&5u as &Any, &TEST as &Any, &Test as &Any);

        assert!(a.is::<uint>());
        assert!(!b.is::<uint>());
        assert!(!c.is::<uint>());

        assert!(!a.is::<&'static str>());
        assert!(b.is::<&'static str>());
        assert!(!c.is::<&'static str>());

        assert!(!a.is::<Test>());
        assert!(!b.is::<Test>());
        assert!(c.is::<Test>());
    }

    #[test]
    fn any_owning() {
        let (a, b, c) = (~5u as ~Any, ~TEST as ~Any, ~Test as ~Any);

        assert!(a.is::<uint>());
        assert!(!b.is::<uint>());
        assert!(!c.is::<uint>());

        assert!(!a.is::<&'static str>());
        assert!(b.is::<&'static str>());
        assert!(!c.is::<&'static str>());

        assert!(!a.is::<Test>());
        assert!(!b.is::<Test>());
        assert!(c.is::<Test>());
    }

    #[test]
    fn any_managed() {
        let (a, b, c) = (@5u as @Any, @TEST as @Any, @Test as @Any);

        assert!(a.is::<uint>());
        assert!(!b.is::<uint>());
        assert!(!c.is::<uint>());

        assert!(!a.is::<&'static str>());
        assert!(b.is::<&'static str>());
        assert!(!c.is::<&'static str>());

        assert!(!a.is::<Test>());
        assert!(!b.is::<Test>());
        assert!(c.is::<Test>());
    }

    #[test]
    fn any_as_ref() {
        let a = &5u as &Any;

        match a.as_ref::<uint>() {
            Some(&5) => {}
            x => fail!("Unexpected value {:?}", x)
        }

        match a.as_ref::<Test>() {
            None => {}
            x => fail!("Unexpected value {:?}", x)
        }
    }

    #[test]
    fn any_as_mut() {
        let mut a = 5u;
        let mut b = ~7u;

        let a_r = &mut a as &mut Any;
        let tmp: &mut uint = b;
        let b_r = tmp as &mut Any;

        match a_r.as_mut::<uint>() {
            Some(x) => {
                assert_eq!(*x, 5u);
                *x = 612;
            }
            x => fail!("Unexpected value {:?}", x)
        }

        match b_r.as_mut::<uint>() {
            Some(x) => {
                assert_eq!(*x, 7u);
                *x = 413;
            }
            x => fail!("Unexpected value {:?}", x)
        }

        match a_r.as_mut::<Test>() {
            None => (),
            x => fail!("Unexpected value {:?}", x)
        }

        match b_r.as_mut::<Test>() {
            None => (),
            x => fail!("Unexpected value {:?}", x)
        }

        match a_r.as_mut::<uint>() {
            Some(&612) => {}
            x => fail!("Unexpected value {:?}", x)
        }

        match b_r.as_mut::<uint>() {
            Some(&413) => {}
            x => fail!("Unexpected value {:?}", x)
        }
    }

    #[test]
    fn any_move() {
        let a = ~8u as ~Any;
        let b = ~Test as ~Any;

        assert_eq!(a.move(), Some(~8u));
        assert_eq!(b.move(), Some(~Test));

        let a = ~8u as ~Any;
        let b = ~Test as ~Any;

        assert_eq!(a.move(), None::<~Test>);
        assert_eq!(b.move(), None::<~uint>);
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use feed::Feed;

#[derive(Debug)]
pub enum Icon {
    Update,
    Error,
}

#[cfg(unix)]
mod unix {
    extern crate notify_rust;
    use std::error::Error;
    use notification::Icon;
    use self::notify_rust::Notification;

    pub fn create(icon: Icon, title: &str, body: &str) -> Result<(), Box<Error>> {
        let icon = match icon {
            Icon::Update => "emblem-sound",
            Icon::Error  => "dialog-error",
        };

        Notification::new()
            .summary(title)
            .body(body)
            .icon(icon)
            .show()
            .map(|_| ())
            .map_err(|e| e.into())
    }
}

#[cfg(windows)]
mod windows {
    extern crate winapi;
    extern crate user32;
    extern crate kernel32;

    use std::error::Error;
    use std::ffi::OsStr;
    use std::mem;
    use std::os::windows::ffi::OsStrExt;
    use std::ptr;
    use std::sync::Mutex;
    use self::winapi::winuser::{WNDCLASSW, HWND_MESSAGE, WM_USER, WM_DESTROY, IDI_INFORMATION, IDI_ERROR};
    use self::winapi::minwindef::{DWORD, UINT, LRESULT, LPARAM, HINSTANCE, WPARAM};
    use self::winapi::windef::{HWND, HICON};
    use self::winapi::winnt::WCHAR;
    use self::winapi::GUID;
    use notification::Icon;

    const NIF_ICON: UINT   = 0x2;
    const NIF_TIP: UINT    = 0x4;
    const NIF_INFO: UINT   = 0x10;
    const NIM_ADD: UINT    = 0;
    const NIM_DELETE: UINT = 2;
    const NIIF_USER: UINT = 0x4;

    const ERROR_CLASS_ALREADY_EXISTS: UINT = 1410;

    #[repr(C)]
    #[allow(non_snake_case)]
    struct NOTIFYICONDATA {
        cbSize:           DWORD,
        hWnd:             HWND,
        uID:              UINT,
        uFlags:           UINT,
        uCallbackMessage: UINT,
        hIcon:            HICON,
        szTip:            [WCHAR; 128],
        dwState:          DWORD,
        dwStateMask:      DWORD,
        szInfo:           [WCHAR; 256],
        uVersion:         UINT,
        szInfoTitle:      [WCHAR; 64],
        dwInfoFlags:      DWORD,
        guidItem:         GUID,
        hBalloonIcon:     HICON,
    }

    #[link(name = "shell32")]
    extern {
        fn Shell_NotifyIconW(mode: DWORD, iconData: *const NOTIFYICONDATA);
    }

    // We cannot implement the sync trait for arbitrary types (see E0117), so we use a struct to bypass it
    struct NotifData {
        data: NOTIFYICONDATA,
    }

    impl NotifData {
        unsafe fn new() -> NotifData {
            let class_name = to_wstring(env!("CARGO_PKG_NAME"));

            let window = WNDCLASSW {
                style:         0,
                lpfnWndProc:   Some(window_proc),
                cbClsExtra:    0,
                cbWndExtra:    0,
                hInstance:     ptr::null_mut(),
                hIcon:         ptr::null_mut(),
                hCursor:       ptr::null_mut(),
                hbrBackground: ptr::null_mut(),
                lpszMenuName:  ptr::null(),
                lpszClassName: class_name.as_ptr(),
            };

            if user32::RegisterClassW(&window) == 0 {
                let error = kernel32::GetLastError() as UINT;

                // No reason to block the user from opening multiple program instances
                if error != ERROR_CLASS_ALREADY_EXISTS {
                    panic!("unable to register notification window class: error {}", error);
                }
            }

            let h_wnd = user32::CreateWindowExW(
                            0,
                            class_name.as_ptr(),
                            class_name.as_ptr(),
                            0, 0, 0, 0, 0,
                            HWND_MESSAGE,
                            ptr::null_mut(),
                            ptr::null_mut(),
                            ptr::null_mut());

            if h_wnd.is_null() {
                panic!("unable to create notification window: error {}", kernel32::GetLastError());
            }

            let notif_data = NOTIFYICONDATA {
                cbSize:           mem::size_of::<NOTIFYICONDATA>() as DWORD,
                hWnd:             h_wnd,
                uID:              1,
                uFlags:           NIF_ICON | NIF_INFO | NIF_TIP,
                uCallbackMessage: WM_USER + 200,
                hIcon:            ptr::null_mut(),
                szTip:            [0; 128],
                dwState:          0,
                dwStateMask:      0,
                szInfo:           [0; 256],
                uVersion:         4,
                szInfoTitle:      [0; 64],
                dwInfoFlags:      NIIF_USER,
                guidItem:         winapi::GUID { Data1: 0, Data2: 0, Data3: 0, Data4: [0; 8] },
                hBalloonIcon:     0 as winapi::HICON,
            };

            NotifData {
                data: notif_data,
            }
        }
    }

    unsafe impl Send for NotifData {}
    unsafe impl Sync for NotifData {}

    pub fn create(icon: Icon, title: &str, body: &str) -> Result<(), Box<Error>> {
        lazy_static! {
            static ref NOTIF_DATA: Mutex<NotifData> = unsafe {
                Mutex::new(NotifData::new())
            };
        }

        let mut notif = NOTIF_DATA.lock().unwrap();
        let notif = &mut notif.data;

        let icon = match icon {
            Icon::Update => IDI_INFORMATION,
            Icon::Error  => IDI_ERROR,
        };

        unsafe {
            let new_icon = user32::LoadIconW(0 as HINSTANCE, icon);

            if !new_icon.is_null() {
                notif.hIcon = new_icon;
            }

            let title = to_wstring(title);
            ptr::copy(title.as_ptr(), notif.szInfoTitle.as_mut_ptr(), title.len());

            let body = to_wstring(body);
            ptr::copy(body.as_ptr(), notif.szInfo.as_mut_ptr(), body.len());

            Shell_NotifyIconW(NIM_ADD, notif);
            Shell_NotifyIconW(NIM_DELETE, notif);
        }

        Ok(())
    }

    fn to_wstring(str : &str) -> Vec<u16> {
        OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect()
    }

    unsafe extern "system" fn window_proc(h_wnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT
    {
        if msg == WM_DESTROY {
            user32::PostQuitMessage(0);
        }

        user32::DefWindowProcW(h_wnd, msg, w_param, l_param)
    }
}

#[cfg(unix)]
use self::unix::create;

#[cfg(windows)]
use self::windows::create;

pub fn create_update(feed_idx: i32, max_feed_idx: i32, feed: &Feed, feed_delta: i32) ->
    Result<(), Box<Error>> {

    let alert = match feed.alert {
        Some(ref alert) => format!("\nAlert: {}", alert),
        None            => String::new(),
    };

    create(
        Icon::Update,
        &format!("Broadcastify Update ({} of {})", feed_idx, max_feed_idx),
        &format!("Name: {}\nListeners: {} (^{}){}\nLink: http://broadcastify.com/listen/feed/{}",
            feed.name,
            feed.listeners,
            feed_delta,
            &alert,
            feed.id))
}

pub fn create_error(body: &str) -> Result<(), Box<Error>> {
    create(
        Icon::Error,
        "Broadcastify Update Error",
        body)
}<commit_msg>Fix potential overflow crash on Windows<commit_after>use std::error::Error;
use feed::Feed;

#[derive(Debug)]
pub enum Icon {
    Update,
    Error,
}

#[cfg(unix)]
mod unix {
    extern crate notify_rust;
    use std::error::Error;
    use notification::Icon;
    use self::notify_rust::Notification;

    pub fn create(icon: Icon, title: &str, body: &str) -> Result<(), Box<Error>> {
        let icon = match icon {
            Icon::Update => "emblem-sound",
            Icon::Error  => "dialog-error",
        };

        Notification::new()
            .summary(title)
            .body(body)
            .icon(icon)
            .show()
            .map(|_| ())
            .map_err(|e| e.into())
    }
}

#[cfg(windows)]
mod windows {
    extern crate winapi;
    extern crate user32;
    extern crate kernel32;

    use std::cmp;
    use std::error::Error;
    use std::ffi::OsStr;
    use std::mem;
    use std::os::windows::ffi::OsStrExt;
    use std::ptr;
    use std::sync::Mutex;
    use self::winapi::winuser::{WNDCLASSW, HWND_MESSAGE, WM_USER, WM_DESTROY, IDI_INFORMATION, IDI_ERROR};
    use self::winapi::minwindef::{DWORD, UINT, LRESULT, LPARAM, HINSTANCE, WPARAM};
    use self::winapi::windef::{HWND, HICON};
    use self::winapi::winnt::WCHAR;
    use self::winapi::GUID;
    use notification::Icon;

    const NIF_ICON: UINT   = 0x2;
    const NIF_TIP: UINT    = 0x4;
    const NIF_INFO: UINT   = 0x10;
    const NIM_ADD: UINT    = 0;
    const NIM_DELETE: UINT = 2;
    const NIIF_USER: UINT = 0x4;

    const ERROR_CLASS_ALREADY_EXISTS: UINT = 1410;

    #[repr(C)]
    #[allow(non_snake_case)]
    struct NOTIFYICONDATA {
        cbSize:           DWORD,
        hWnd:             HWND,
        uID:              UINT,
        uFlags:           UINT,
        uCallbackMessage: UINT,
        hIcon:            HICON,
        szTip:            [WCHAR; 128],
        dwState:          DWORD,
        dwStateMask:      DWORD,
        szInfo:           [WCHAR; 256],
        uVersion:         UINT,
        szInfoTitle:      [WCHAR; 64],
        dwInfoFlags:      DWORD,
        guidItem:         GUID,
        hBalloonIcon:     HICON,
    }

    #[link(name = "shell32")]
    extern {
        fn Shell_NotifyIconW(mode: DWORD, iconData: *const NOTIFYICONDATA);
    }

    // We cannot implement the sync trait for arbitrary types (see E0117), so we use a struct to bypass it
    struct NotifData {
        data: NOTIFYICONDATA,
    }

    impl NotifData {
        unsafe fn new() -> NotifData {
            let class_name = to_wstring(env!("CARGO_PKG_NAME"));

            let window = WNDCLASSW {
                style:         0,
                lpfnWndProc:   Some(window_proc),
                cbClsExtra:    0,
                cbWndExtra:    0,
                hInstance:     ptr::null_mut(),
                hIcon:         ptr::null_mut(),
                hCursor:       ptr::null_mut(),
                hbrBackground: ptr::null_mut(),
                lpszMenuName:  ptr::null(),
                lpszClassName: class_name.as_ptr(),
            };

            if user32::RegisterClassW(&window) == 0 {
                let error = kernel32::GetLastError() as UINT;

                // No reason to block the user from opening multiple program instances
                if error != ERROR_CLASS_ALREADY_EXISTS {
                    panic!("unable to register notification window class: error {}", error);
                }
            }

            let h_wnd = user32::CreateWindowExW(
                            0,
                            class_name.as_ptr(),
                            class_name.as_ptr(),
                            0, 0, 0, 0, 0,
                            HWND_MESSAGE,
                            ptr::null_mut(),
                            ptr::null_mut(),
                            ptr::null_mut());

            if h_wnd.is_null() {
                panic!("unable to create notification window: error {}", kernel32::GetLastError());
            }

            let notif_data = NOTIFYICONDATA {
                cbSize:           mem::size_of::<NOTIFYICONDATA>() as DWORD,
                hWnd:             h_wnd,
                uID:              1,
                uFlags:           NIF_ICON | NIF_INFO | NIF_TIP,
                uCallbackMessage: WM_USER + 200,
                hIcon:            ptr::null_mut(),
                szTip:            [0; 128],
                dwState:          0,
                dwStateMask:      0,
                szInfo:           [0; 256],
                uVersion:         4,
                szInfoTitle:      [0; 64],
                dwInfoFlags:      NIIF_USER,
                guidItem:         winapi::GUID { Data1: 0, Data2: 0, Data3: 0, Data4: [0; 8] },
                hBalloonIcon:     0 as winapi::HICON,
            };

            NotifData {
                data: notif_data,
            }
        }
    }

    unsafe impl Send for NotifData {}
    unsafe impl Sync for NotifData {}

    pub fn create(icon: Icon, title: &str, body: &str) -> Result<(), Box<Error>> {
        lazy_static! {
            static ref NOTIF_DATA: Mutex<NotifData> = unsafe {
                Mutex::new(NotifData::new())
            };
        }

        let mut notif = NOTIF_DATA.lock().unwrap();
        let notif = &mut notif.data;

        let icon = match icon {
            Icon::Update => IDI_INFORMATION,
            Icon::Error  => IDI_ERROR,
        };

        unsafe {
            let new_icon = user32::LoadIconW(0 as HINSTANCE, icon);

            if !new_icon.is_null() {
                notif.hIcon = new_icon;
            }

            let title = to_wstring(title);
            ptr::copy(title.as_ptr(), notif.szInfoTitle.as_mut_ptr(), cmp::min(title.len(), notif.szInfoTitle.len()));

            let body = to_wstring(body);
            ptr::copy(body.as_ptr(), notif.szInfo.as_mut_ptr(), cmp::min(body.len(), notif.szInfo.len()));

            Shell_NotifyIconW(NIM_ADD, notif);
            Shell_NotifyIconW(NIM_DELETE, notif);
        }

        Ok(())
    }

    fn to_wstring(str : &str) -> Vec<u16> {
        OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect()
    }

    unsafe extern "system" fn window_proc(h_wnd: HWND, msg: UINT, w_param: WPARAM, l_param: LPARAM) -> LRESULT
    {
        if msg == WM_DESTROY {
            user32::PostQuitMessage(0);
        }

        user32::DefWindowProcW(h_wnd, msg, w_param, l_param)
    }
}

#[cfg(unix)]
use self::unix::create;

#[cfg(windows)]
use self::windows::create;

pub fn create_update(feed_idx: i32, max_feed_idx: i32, feed: &Feed, feed_delta: i32) ->
    Result<(), Box<Error>> {

    let alert = match feed.alert {
        Some(ref alert) => format!("\nAlert: {}", alert),
        None            => String::new(),
    };

    create(
        Icon::Update,
        &format!("Broadcastify Update ({} of {})", feed_idx, max_feed_idx),
        &format!("Name: {}\nListeners: {} (^{}){}\nLink: http://broadcastify.com/listen/feed/{}",
            feed.name,
            feed.listeners,
            feed_delta,
            &alert,
            feed.id))
}

pub fn create_error(body: &str) -> Result<(), Box<Error>> {
    create(
        Icon::Error,
        "Broadcastify Update Error",
        body)
}<|endoftext|>