<commit_before>extern crate bincode;
extern crate rustc_serialize;
extern crate rand;

mod wal_file;
mod multi_map;

use wal_file::{KeyValuePair, WALFile, WALIterator};
// use multi_map::{MultiMap, MultiMapIterator};

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, decode};
use rustc_serialize::{Encodable, Decodable};

use std::cmp::max;
use std::convert::From;
use std::collections::{BTreeMap, BTreeSet};
use std::error::Error;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::{Read, Write, Seek, SeekFrom, ErrorKind};
use std::mem::{size_of};
use std::str;

const NUM_CHILDREN: usize = 32;
const FILE_HEADER: &'static str = "B+Tree\0";
const CURRENT_VERSION: u8 = 0x01;

// specify the types for the keys & values
pub trait KeyType: Ord + Encodable + Decodable + Clone {}
pub trait ValueType: Ord + Encodable + Decodable + Clone  {}

// provide generic implementations

impl<T> KeyType for T where T: Ord + Encodable + Decodable + Clone {}
impl<T> ValueType for T where T: Ord + Encodable + Decodable + Clone {}

#[derive(RustcEncodable, RustcDecodable, PartialEq)]
enum Payload<K: KeyType, V: ValueType> {
        Value(V),
        Children([(K,u64); NUM_CHILDREN]),
    }

#[derive(RustcEncodable, RustcDecodable, PartialEq)]
struct Node<K: KeyType, V: ValueType> {
    key: K,
    parent: u64,
    payload: Payload<K,V>, // either children, or actual values
}

/// This struct represents an on-disk B+Tree. There are NUM_CHILDREN keys at each
/// level in the tree. The on-disk format is as follows where VV is the version
/// number:
/// |-------------------------------------------|
/// | 0x42 0x2b 0x54 0x72 | 0x65 0x65 0x00 0xVV |
/// | B    +    T    r    | e    e    \0   0xVV |
/// |-------------------------------------------|
/// | smallest record in bincode format         |
/// |-------------------------------------------|
/// | ...                                       |
/// |-------------------------------------------|
/// | largest record in bincode format          |
/// |-------------------------------------------|
/// | internal nodes ...                        |
/// |-------------------------------------------|
/// | root node                                 |
/// |-------------------------------------------|
pub struct BTree<K: KeyType, V: ValueType> {
    tree_file_path: String,         // the path to the tree file
    tree_file: File,                // the file backing the whole thing
    wal_file: WALFile<K,V>,              // write-ahead log for in-memory items
    root: Option<Node<K,V>>,        // optional in-memory copy of the root node
    max_key_size: usize,            // the size of the key in bytes
    max_value_size: usize,          // the size of the value in bytes
    mem_tree: BTreeMap<K, BTreeSet<V>>,  // the in-memory BTree that gets merged with the on-disk one
}

impl <K: KeyType, V: ValueType> BTree<K, V> {
    pub fn new(tree_file_path: String, max_key_size: usize, max_value_size: usize) -> Result<BTree<K,V>, Box<Error>> {
        // create our mem_tree
        let mut mem_tree = BTreeMap::<K, BTreeSet<V>>::new();

        let wal_file_path = tree_file_path.to_owned() + ".wal";

        let mut wal_file = try!(WALFile::<K,V>::new(wal_file_path.to_owned(), max_key_size, max_value_size));

        let record_size = max_key_size + max_value_size;

        // if we have a WAL file, replay it into the mem_tree
        if try!(wal_file.is_new()) {
            for kv in &mut wal_file {
                mem_tree.entry(kv.key).or_insert(BTreeSet::<V>::new()).insert(kv.value);
            }
        }

        // compute the size of a on-disk Node
        let node_size: usize = (max_key_size + size_of::<u64>() + max(max_value_size, (max_key_size + size_of::<u64>()) * NUM_CHILDREN)) as usize;

        // open the data file
        let mut tree_file = try!(OpenOptions::new().read(true).write(true).create(true).open(tree_file_path.to_owned()));

        let metadata = try!(tree_file.metadata());

        // check to see if this is a new file
        if metadata.len() == 0 {
            // write out our header
            try!(tree_file.write(FILE_HEADER.as_bytes()));

            // write out our version
            try!(tree_file.write(&[CURRENT_VERSION]));

            // construct and return our BTree object
            Ok(BTree{tree_file_path: tree_file_path,
                     tree_file: tree_file,
                     wal_file: wal_file,
                     root: None,
                     max_key_size: max_key_size,
                     max_value_size: max_value_size,
                     mem_tree: mem_tree
            })
        } else {
            let mut version_string = vec![0; 8];

            try!(tree_file.read_exact(&mut version_string));

            // make sure we've opened a proper file
            if try!(str::from_utf8(&version_string[0..FILE_HEADER.len()])) != FILE_HEADER ||
               version_string[FILE_HEADER.len()] != CURRENT_VERSION {
                return Err(From::from(std::io::Error::new(ErrorKind::InvalidData, "Invalid BTree file or BTree version")));
            }

            let mut buff = vec![0; node_size];

            // make sure we have a root node to read
            if metadata.len() < (version_string.len() + node_size) as u64 {
                // if we don't have a root node yet, just return
                return Ok(BTree{tree_file_path: tree_file_path,
                                tree_file: tree_file,
                                wal_file: wal_file,
                                root: None,
                                max_key_size: max_key_size,
                                max_value_size: max_value_size,
                                mem_tree: mem_tree
                });
            }

            // seek node_size in from the end of the file to read the root node
            try!(tree_file.seek(SeekFrom::End((node_size as isize * -1) as i64)));
            try!(tree_file.read_exact(&mut buff));

            let root_node: Node<K,V> = try!(decode(&buff[..]));

            Ok(BTree{tree_file_path: tree_file_path,
                     tree_file: tree_file,
                     wal_file: wal_file,
                     root: Some(root_node),
                     max_key_size: max_key_size,
                     max_value_size: max_value_size,
                     mem_tree: mem_tree
            })
        }
    }

    /// Inserts a key into the BTree
    pub fn insert(&mut self, key: K, value: V) -> Result<(), Box<Error>> {
        let record = KeyValuePair{key: key, value: value};

        try!(self.wal_file.write_record(&record));

        let KeyValuePair{key, value} = record;

        self.mem_tree.entry(key).or_insert(BTreeSet::<V>::new()).insert(value);

        Ok( () )
    }

    pub fn get(&self, key: &K) -> Option<&BTreeSet<V>> {
        self.mem_tree.get(key)
    }
/*
    /// Merges the records on disk with the records in memory
    fn compact(&mut self) -> Result<(), Box<Error>>{
        let mut new_tree_file = try!(OpenOptions::new().read(true).write(true).create(true).truncate(true).open(self.tree_file_path + ".new"));

        let mut mem_iter = self.mem_tree.iter().fuse();  // get an iterator that always returns None when done

        loop {
            let mem_item = mem_iter.next();

        }
    }
*/
}

#[cfg(test)]
mod tests {
    use std::fs;
    use std::fs::{OpenOptions, Metadata};
    use ::BTree;
    use rand::{thread_rng, Rng};
    use std::collections::{BTreeSet};

    pub fn gen_temp_name() -> String {
        let file_name: String = thread_rng().gen_ascii_chars().take(10).collect();

        return String::from("/tmp/") + &file_name + &String::from(".btr");
    }

    fn remove_files(file_path: String) {
        fs::remove_file(&file_path);
        fs::remove_file(file_path + ".wal");
    }

    #[test]
    fn new_blank_file() {
        let file_path = gen_temp_name();

        BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        // make sure our two files were created
        let btf = OpenOptions::new().read(true).write(false).create(false).open(&file_path).unwrap();
        assert!(btf.metadata().unwrap().len() == 8);

        let wal = OpenOptions::new().read(true).write(false).create(false).open(file_path.to_owned() + ".wal").unwrap();
        assert!(wal.metadata().unwrap().len() == 0);

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn new_existing_file() {
        let file_path = gen_temp_name();

        {
            BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();
        }

        let btree = BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        // check our file lengths from the struct
        assert!(btree.tree_file.metadata().unwrap().len() == 8);
        assert!(btree.wal_file.len().unwrap() == 0);

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_new_u8() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        let len = btree.insert(2, 3).unwrap(); // insert into a new file

        assert!(btree.wal_file.len().unwrap() == 2);
        assert!(btree.mem_tree.contains_key(&2));

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_new_str() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<String, String>::new(file_path.to_owned(), 15, 15).unwrap();

        // insert into a new file
        btree.insert("Hello".to_owned(), "World".to_owned()).unwrap();

        assert!(! btree.wal_file.is_new().unwrap());
        assert!(btree.mem_tree.contains_key(&String::from("Hello")));

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn get_returns_a_set() {
        let file_path = gen_temp_name();

        // setup tree
        let mut btree = BTree::<String, String>::new(file_path.to_owned(), 15, 15).unwrap();

        // expected return set
        let mut expected: BTreeSet<String> = BTreeSet::new();
        expected.insert("World".to_string());

        btree.insert("Hello".to_owned(), "World".to_owned());

        // get the set at the hello key
        let set_at_hello: &BTreeSet<String> = btree.get(&"Hello".to_string()).unwrap();

        assert_eq!(&expected, set_at_hello);

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_multiple() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<String, String>::new(file_path.to_owned(), 15, 15).unwrap();

        // insert into a new file
        btree.insert("Hello".to_owned(), "World".to_owned()).unwrap();
        assert!(! btree.wal_file.is_new().unwrap());

        btree.insert("Hello".to_owned(), "Everyone".to_owned()).unwrap();
        assert!(! btree.wal_file.is_new().unwrap());

        remove_files(file_path); // remove files assuming it all went well
    }
}
<commit_msg>use better option handling semantics<commit_after>extern crate bincode;
extern crate rustc_serialize;
extern crate rand;

mod wal_file;
mod multi_map;

use wal_file::{KeyValuePair, WALFile, WALIterator};
// use multi_map::{MultiMap, MultiMapIterator};

use bincode::SizeLimit;
use bincode::rustc_serialize::{encode, decode};
use rustc_serialize::{Encodable, Decodable};

use std::cmp::max;
use std::convert::From;
use std::collections::{BTreeMap, BTreeSet};
use std::error::Error;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::{Read, Write, Seek, SeekFrom, ErrorKind};
use std::mem::{size_of};
use std::str;

const NUM_CHILDREN: usize = 32;
const FILE_HEADER: &'static str = "B+Tree\0";
const CURRENT_VERSION: u8 = 0x01;

// specify the types for the keys & values
pub trait KeyType: Ord + Encodable + Decodable + Clone {}
pub trait ValueType: Ord + Encodable + Decodable + Clone  {}

// provide generic implementations

impl<T> KeyType for T where T: Ord + Encodable + Decodable + Clone {}
impl<T> ValueType for T where T: Ord + Encodable + Decodable + Clone {}

#[derive(RustcEncodable, RustcDecodable, PartialEq)]
enum Payload<K: KeyType, V: ValueType> {
        Value(V),
        Children([(K,u64); NUM_CHILDREN]),
    }

#[derive(RustcEncodable, RustcDecodable, PartialEq)]
struct Node<K: KeyType, V: ValueType> {
    key: K,
    parent: u64,
    payload: Payload<K,V>, // either children, or actual values
}

/// This struct represents an on-disk B+Tree. There are NUM_CHILDREN keys at each
/// level in the tree. The on-disk format is as follows where VV is the version
/// number:
/// |-------------------------------------------|
/// | 0x42 0x2b 0x54 0x72 | 0x65 0x65 0x00 0xVV |
/// | B    +    T    r    | e    e    \0   0xVV |
/// |-------------------------------------------|
/// | smallest record in bincode format         |
/// |-------------------------------------------|
/// | ...                                       |
/// |-------------------------------------------|
/// | largest record in bincode format          |
/// |-------------------------------------------|
/// | internal nodes ...                        |
/// |-------------------------------------------|
/// | root node                                 |
/// |-------------------------------------------|
pub struct BTree<K: KeyType, V: ValueType> {
    tree_file_path: String,         // the path to the tree file
    tree_file: File,                // the file backing the whole thing
    wal_file: WALFile<K,V>,              // write-ahead log for in-memory items
    root: Option<Node<K,V>>,        // optional in-memory copy of the root node
    max_key_size: usize,            // the size of the key in bytes
    max_value_size: usize,          // the size of the value in bytes
    mem_tree: BTreeMap<K, BTreeSet<V>>,  // the in-memory BTree that gets merged with the on-disk one
}

impl <K: KeyType, V: ValueType> BTree<K, V> {
    pub fn new(tree_file_path: String, max_key_size: usize, max_value_size: usize) -> Result<BTree<K,V>, Box<Error>> {
        // create our mem_tree
        let mut mem_tree = BTreeMap::<K, BTreeSet<V>>::new();

        let wal_file_path = tree_file_path.to_owned() + ".wal";

        let mut wal_file = try!(WALFile::<K,V>::new(wal_file_path.to_owned(), max_key_size, max_value_size));

        let record_size = max_key_size + max_value_size;

        // if we have a WAL file, replay it into the mem_tree
        if try!(wal_file.is_new()) {
            for kv in &mut wal_file {
                mem_tree.entry(kv.key).or_insert(BTreeSet::<V>::new()).insert(kv.value);
            }
        }

        // compute the size of a on-disk Node
        let node_size: usize = (max_key_size + size_of::<u64>() + max(max_value_size, (max_key_size + size_of::<u64>()) * NUM_CHILDREN)) as usize;

        // open the data file
        let mut tree_file = try!(OpenOptions::new().read(true).write(true).create(true).open(tree_file_path.to_owned()));

        let metadata = try!(tree_file.metadata());

        // check to see if this is a new file
        if metadata.len() == 0 {
            // write out our header
            try!(tree_file.write(FILE_HEADER.as_bytes()));

            // write out our version
            try!(tree_file.write(&[CURRENT_VERSION]));

            // construct and return our BTree object
            Ok(BTree{tree_file_path: tree_file_path,
                     tree_file: tree_file,
                     wal_file: wal_file,
                     root: None,
                     max_key_size: max_key_size,
                     max_value_size: max_value_size,
                     mem_tree: mem_tree
            })
        } else {
            let mut version_string = vec![0; 8];

            try!(tree_file.read_exact(&mut version_string));

            // make sure we've opened a proper file
            if try!(str::from_utf8(&version_string[0..FILE_HEADER.len()])) != FILE_HEADER ||
               version_string[FILE_HEADER.len()] != CURRENT_VERSION {
                return Err(From::from(std::io::Error::new(ErrorKind::InvalidData, "Invalid BTree file or BTree version")));
            }

            let mut buff = vec![0; node_size];

            // make sure we have a root node to read
            if metadata.len() < (version_string.len() + node_size) as u64 {
                // if we don't have a root node yet, just return
                return Ok(BTree{tree_file_path: tree_file_path,
                                tree_file: tree_file,
                                wal_file: wal_file,
                                root: None,
                                max_key_size: max_key_size,
                                max_value_size: max_value_size,
                                mem_tree: mem_tree
                });
            }

            // seek node_size in from the end of the file to read the root node
            try!(tree_file.seek(SeekFrom::End((node_size as isize * -1) as i64)));
            try!(tree_file.read_exact(&mut buff));

            let root_node: Node<K,V> = try!(decode(&buff[..]));

            Ok(BTree{tree_file_path: tree_file_path,
                     tree_file: tree_file,
                     wal_file: wal_file,
                     root: Some(root_node),
                     max_key_size: max_key_size,
                     max_value_size: max_value_size,
                     mem_tree: mem_tree
            })
        }
    }

    /// Inserts a key into the BTree
    pub fn insert(&mut self, key: K, value: V) -> Result<(), Box<Error>> {
        let record = KeyValuePair{key: key, value: value};

        try!(self.wal_file.write_record(&record));

        let KeyValuePair{key, value} = record;

        self.mem_tree.entry(key).or_insert(BTreeSet::<V>::new()).insert(value);

        Ok( () )
    }

    pub fn get(&self, key: &K) -> Option<std::collections::btree_set::Iter<V>> {
        self.mem_tree.get(key).map(|btree| btree.iter())
    }
/*
    /// Merges the records on disk with the records in memory
    fn compact(&mut self) -> Result<(), Box<Error>>{
        let mut new_tree_file = try!(OpenOptions::new().read(true).write(true).create(true).truncate(true).open(self.tree_file_path + ".new"));

        let mut mem_iter = self.mem_tree.iter().fuse();  // get an iterator that always returns None when done

        loop {
            let mem_item = mem_iter.next();

        }
    }
*/
}

#[cfg(test)]
mod tests {
    use std::fs;
    use std::fs::{OpenOptions, Metadata};
    use ::BTree;
    use rand::{thread_rng, Rng};
    use std::collections::{BTreeMap, BTreeSet};

    pub fn gen_temp_name() -> String {
        let file_name: String = thread_rng().gen_ascii_chars().take(10).collect();

        return String::from("/tmp/") + &file_name + &String::from(".btr");
    }

    fn remove_files(file_path: String) {
        fs::remove_file(&file_path);
        fs::remove_file(file_path + ".wal");
    }

    #[test]
    fn new_blank_file() {
        let file_path = gen_temp_name();

        BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        // make sure our two files were created
        let btf = OpenOptions::new().read(true).write(false).create(false).open(&file_path).unwrap();
        assert!(btf.metadata().unwrap().len() == 8);

        let wal = OpenOptions::new().read(true).write(false).create(false).open(file_path.to_owned() + ".wal").unwrap();
        assert!(wal.metadata().unwrap().len() == 0);

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn new_existing_file() {
        let file_path = gen_temp_name();

        {
            BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();
        }

        let btree = BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        // check our file lengths from the struct
        assert!(btree.tree_file.metadata().unwrap().len() == 8);
        assert!(btree.wal_file.len().unwrap() == 0);

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_new_u8() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<u8, u8>::new(file_path.to_owned(), 1, 1).unwrap();

        let len = btree.insert(2, 3).unwrap(); // insert into a new file

        assert!(btree.wal_file.len().unwrap() == 2);
        assert!(btree.mem_tree.contains_key(&2));

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_new_str() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<String, String>::new(file_path.to_owned(), 15, 15).unwrap();

        // insert into a new file
        btree.insert("Hello".to_owned(), "World".to_owned()).unwrap();

        assert!(! btree.wal_file.is_new().unwrap());
        assert!(btree.mem_tree.contains_key(&String::from("Hello")));

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn get_returns_an_iter() {
        let file_path = gen_temp_name();

        // setup tree
        let mut btree = BTree::<String, String>::new(file_path.to_owned(), 15, 15).unwrap();

        // expected return set
        let mut expected: BTreeSet<String> = BTreeSet::new();
        expected.insert("World".to_string());

        btree.insert("Hello".to_owned(), "World".to_owned());

        // get the set at the hello key
        let set_at_hello: Vec<String> = btree.get(&"Hello".to_string()).unwrap().cloned().collect();

        assert_eq!(set_at_hello, ["World".to_string()]);

        remove_files(file_path); // remove files assuming it all went well
    }

    #[test]
    fn insert_multiple() {
        let file_path = gen_temp_name();

        let mut btree = BTree::<String, String>::new(file_path.to_owned(), 15, 15).unwrap();

        // insert into a new file
        btree.insert("Hello".to_owned(), "World".to_owned()).unwrap();
        assert!(! btree.wal_file.is_new().unwrap());

        btree.insert("Hello".to_owned(), "Everyone".to_owned()).unwrap();
        assert!(! btree.wal_file.is_new().unwrap());

        remove_files(file_path); // remove files assuming it all went well
    }
}
<|endoftext|>


<commit_before>// 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.

use address::Address;
use basicblock::{BasicBlock, BasicBlockEdge, EdgeType};
use instruction::Instruction;
use petgraph::graph::{Graph, NodeIndex};
use std::collections::BTreeMap;

/// A [control flow graph].
///
/// [control flow graph]: https://en.wikipedia.org/wiki/Control_flow_graph
pub struct CFG<'f> {
    /// The [`Graph`] that stores the actual CFG
    ///
    /// [`Graph`]: ../petgraph/graph/struct.Graph.html
    pub graph: Graph<BasicBlock<'f>, BasicBlockEdge>,
    /// The [`NodeIndex`] for the entry [`BasicBlock`] for this function.
    ///
    /// [`BasicBlock`]: struct.BasicBlock.html
    /// [`NodeIndex`]: ../petgraph/graph/struct.NodeIndex.html
    pub entry_block: Option<NodeIndex>,
    /// Map address to the corresponding basic block.
    pub block_finder: BTreeMap<Address, NodeIndex>,
}

impl<'f> CFG<'f> {
    /// Construct a new CFG
    pub fn new() -> Self {
        CFG {
            graph: Graph::new(),
            entry_block: None,
            block_finder: BTreeMap::new(),
        }
    }

    /// Build the CFG from the [`instructions`].
    ///
    /// This is conducted in a 2 step process:
    ///
    /// * First, each instruction is examined to identify block boundaries.
    /// * Then, we go through each instruction again, looking for the
    ///   previously identified block boundaries and build the edges.
    ///
    /// This two step process prevents us from having to construct and then
    /// subsequently split blocks as we find backward edges.
    ///
    /// [`instructions`]: trait.Instruction.html
    pub fn build(&mut self, instructions: &Vec<Box<Instruction>>) {
        if instructions.is_empty() {
            return;
        }
        self.identify_blocks(instructions);
        self.build_edges(instructions);
    }

    /// Identify basic blocks by their boundaries.
    ///
    /// Inspect each instruction to see if it is a 'leader' or the start of
    /// a new basic block. An instruction is a leader if:
    ///
    /// * It is the first instruction in the basic block.
    /// * It comes after a branch of any sort. We identify these as things
    ///   for which `Instruction::is_block_terminator` returns `true`.
    /// * It is the target of a jump (conditional or unconditional) within
    ///   the function.
    fn identify_blocks(&mut self, instructions: &Vec<Box<Instruction>>) {
        let start_addr = instructions[0].address();
        let end_addr = instructions.last().map(|i| i.address()).unwrap();
        let mut next_is_leader: bool = true;
        for inst in instructions {
            if next_is_leader {
                let idx = self.graph.add_node(BasicBlock::new(inst.address()));
                self.block_finder.insert(inst.address(), idx);
                next_is_leader = false;
            }
            if inst.is_block_terminator() {
                if let Some(target_addr) = inst.target_address() {
                    if !self.block_finder.contains_key(&target_addr) &&
                       target_addr >= start_addr && target_addr <= end_addr {
                        let idx = self.graph.add_node(BasicBlock::new(target_addr));
                        self.block_finder.insert(target_addr, idx);
                    }
                }
                // The next instruction, if any, will be the start of a new block.
                next_is_leader = true;
            }
        }
        self.entry_block = Some(self.block_finder.get(&instructions[0].address()).unwrap().clone());
    }

    /// Build an edge between 2 basic blocks.
    fn build_edge(&mut self,
                  current_block_idx: NodeIndex,
                  next_block_idx: NodeIndex,
                  current_inst: &Box<Instruction>) {
        if current_inst.is_local_conditional_jump() {
            // We have one edge for the jump target and one for the fallthrough.
            // We jump through some hoops here to keep the borrow checker happy.
            if let Some(target_addr) = current_inst.target_address() {
                if let Some(target_block_idx) = self.block_finder.get(&target_addr) {
                    let edge = BasicBlockEdge { edge_type: EdgeType::ConditionalTaken };
                    self.graph.add_edge(current_block_idx, target_block_idx.clone(), edge);
                }
                let edge = BasicBlockEdge { edge_type: EdgeType::ConditionalFallthrough };
                self.graph.add_edge(current_block_idx, next_block_idx, edge);
            }
        } else if current_inst.is_local_jump() || current_inst.is_call() {
            let edge = BasicBlockEdge { edge_type: EdgeType::Unconditional };
            self.graph.add_edge(current_block_idx, next_block_idx, edge);
        } else if current_inst.is_return() {
            // Do we want to record this exit anywhere?
        }
    }

    /// Build edges after having identified the basic blocks.
    ///
    /// After the basic blocks have been identified, we can go back and
    /// connect the basic blocks with their appropriate edges.
    ///
    /// We do this by iterating through the instructions looking for
    /// boundaries between the basic blocks and then setting up the
    /// new edges.
    fn build_edges(&mut self, instructions: &Vec<Box<Instruction>>) {
        // Here, we're going to walk through the instructions again in
        // pairs of (i, Some(i + 1)):
        // [1,2..9] -> [Some(1),Some(2)..Some(9),None]
        let current_inst_iter = instructions.iter();
        let mut next_inst_iter = current_inst_iter.clone().map(Some).chain(Some(None).into_iter());
        // Skip the first one so that we're actually working with the next one.
        next_inst_iter.next();
        let mut current_block_idx = self.entry_block.unwrap();
        for ij in current_inst_iter.zip(next_inst_iter) {
            if let (current_inst, Some(next_inst)) = ij {
                // Does the next instruction begin a basic block?
                let next_block_idx = self.block_finder
                    .get(&next_inst.address())
                    .unwrap_or(&current_block_idx)
                    .clone();
                // If we're at a block boundary, create an edge between the
                // current and next blocks. The type of the edge is determined
                // by looking at the current instruction.
                if next_block_idx != current_block_idx {
                    self.build_edge(current_block_idx, next_block_idx, current_inst);
                    current_block_idx = next_block_idx;
                }
            } else {
                // No next instruction, so we're at the end.
                // Do we want to record this exit anywhere?
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use instruction::Instruction;
    use super::CFG;
    use tests::*;

    #[test]
    fn construct() {
        let cfg = CFG::new();
        assert!(cfg.entry_block.is_none());
        assert_eq!(cfg.graph.node_count(), 0);
    }

    #[test]
    fn build_one_basic_block() {
        let mut cfg = CFG::new();
        let insts: Vec<Box<Instruction>> = vec![
            TestInstruction::new(0, Opcode::Add),
            TestInstruction::new(1, Opcode::Add),
            TestInstruction::new(2, Opcode::Ret),
        ];
        cfg.build(&insts);
        assert!(cfg.entry_block.is_some());
        assert_eq!(cfg.graph.node_count(), 1);
    }
}
<commit_msg>Fix a clippy warning. Use slice rather than vector.<commit_after>// 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.

use address::Address;
use basicblock::{BasicBlock, BasicBlockEdge, EdgeType};
use instruction::Instruction;
use petgraph::graph::{Graph, NodeIndex};
use std::collections::BTreeMap;

/// A [control flow graph].
///
/// [control flow graph]: https://en.wikipedia.org/wiki/Control_flow_graph
pub struct CFG<'f> {
    /// The [`Graph`] that stores the actual CFG
    ///
    /// [`Graph`]: ../petgraph/graph/struct.Graph.html
    pub graph: Graph<BasicBlock<'f>, BasicBlockEdge>,
    /// The [`NodeIndex`] for the entry [`BasicBlock`] for this function.
    ///
    /// [`BasicBlock`]: struct.BasicBlock.html
    /// [`NodeIndex`]: ../petgraph/graph/struct.NodeIndex.html
    pub entry_block: Option<NodeIndex>,
    /// Map address to the corresponding basic block.
    pub block_finder: BTreeMap<Address, NodeIndex>,
}

impl<'f> CFG<'f> {
    /// Construct a new CFG
    pub fn new() -> Self {
        CFG {
            graph: Graph::new(),
            entry_block: None,
            block_finder: BTreeMap::new(),
        }
    }

    /// Build the CFG from the [`instructions`].
    ///
    /// This is conducted in a 2 step process:
    ///
    /// * First, each instruction is examined to identify block boundaries.
    /// * Then, we go through each instruction again, looking for the
    ///   previously identified block boundaries and build the edges.
    ///
    /// This two step process prevents us from having to construct and then
    /// subsequently split blocks as we find backward edges.
    ///
    /// [`instructions`]: trait.Instruction.html
    pub fn build(&mut self, instructions: &[Box<Instruction>]) {
        if instructions.is_empty() {
            return;
        }
        self.identify_blocks(instructions);
        self.build_edges(instructions);
    }

    /// Identify basic blocks by their boundaries.
    ///
    /// Inspect each instruction to see if it is a 'leader' or the start of
    /// a new basic block. An instruction is a leader if:
    ///
    /// * It is the first instruction in the basic block.
    /// * It comes after a branch of any sort. We identify these as things
    ///   for which `Instruction::is_block_terminator` returns `true`.
    /// * It is the target of a jump (conditional or unconditional) within
    ///   the function.
    fn identify_blocks(&mut self, instructions: &[Box<Instruction>]) {
        let start_addr = instructions[0].address();
        let end_addr = instructions.last().map(|i| i.address()).unwrap();
        let mut next_is_leader: bool = true;
        for inst in instructions {
            if next_is_leader {
                let idx = self.graph.add_node(BasicBlock::new(inst.address()));
                self.block_finder.insert(inst.address(), idx);
                next_is_leader = false;
            }
            if inst.is_block_terminator() {
                if let Some(target_addr) = inst.target_address() {
                    if !self.block_finder.contains_key(&target_addr) &&
                       target_addr >= start_addr && target_addr <= end_addr {
                        let idx = self.graph.add_node(BasicBlock::new(target_addr));
                        self.block_finder.insert(target_addr, idx);
                    }
                }
                // The next instruction, if any, will be the start of a new block.
                next_is_leader = true;
            }
        }
        self.entry_block = Some(self.block_finder.get(&instructions[0].address()).unwrap().clone());
    }

    /// Build an edge between 2 basic blocks.
    fn build_edge(&mut self,
                  current_block_idx: NodeIndex,
                  next_block_idx: NodeIndex,
                  current_inst: &Box<Instruction>) {
        if current_inst.is_local_conditional_jump() {
            // We have one edge for the jump target and one for the fallthrough.
            // We jump through some hoops here to keep the borrow checker happy.
            if let Some(target_addr) = current_inst.target_address() {
                if let Some(target_block_idx) = self.block_finder.get(&target_addr) {
                    let edge = BasicBlockEdge { edge_type: EdgeType::ConditionalTaken };
                    self.graph.add_edge(current_block_idx, target_block_idx.clone(), edge);
                }
                let edge = BasicBlockEdge { edge_type: EdgeType::ConditionalFallthrough };
                self.graph.add_edge(current_block_idx, next_block_idx, edge);
            }
        } else if current_inst.is_local_jump() || current_inst.is_call() {
            let edge = BasicBlockEdge { edge_type: EdgeType::Unconditional };
            self.graph.add_edge(current_block_idx, next_block_idx, edge);
        } else if current_inst.is_return() {
            // Do we want to record this exit anywhere?
        }
    }

    /// Build edges after having identified the basic blocks.
    ///
    /// After the basic blocks have been identified, we can go back and
    /// connect the basic blocks with their appropriate edges.
    ///
    /// We do this by iterating through the instructions looking for
    /// boundaries between the basic blocks and then setting up the
    /// new edges.
    fn build_edges(&mut self, instructions: &[Box<Instruction>]) {
        // Here, we're going to walk through the instructions again in
        // pairs of (i, Some(i + 1)):
        // [1,2..9] -> [Some(1),Some(2)..Some(9),None]
        let current_inst_iter = instructions.iter();
        let mut next_inst_iter = current_inst_iter.clone().map(Some).chain(Some(None).into_iter());
        // Skip the first one so that we're actually working with the next one.
        next_inst_iter.next();
        let mut current_block_idx = self.entry_block.unwrap();
        for ij in current_inst_iter.zip(next_inst_iter) {
            if let (current_inst, Some(next_inst)) = ij {
                // Does the next instruction begin a basic block?
                let next_block_idx = self.block_finder
                    .get(&next_inst.address())
                    .unwrap_or(&current_block_idx)
                    .clone();
                // If we're at a block boundary, create an edge between the
                // current and next blocks. The type of the edge is determined
                // by looking at the current instruction.
                if next_block_idx != current_block_idx {
                    self.build_edge(current_block_idx, next_block_idx, current_inst);
                    current_block_idx = next_block_idx;
                }
            } else {
                // No next instruction, so we're at the end.
                // Do we want to record this exit anywhere?
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use instruction::Instruction;
    use super::CFG;
    use tests::*;

    #[test]
    fn construct() {
        let cfg = CFG::new();
        assert!(cfg.entry_block.is_none());
        assert_eq!(cfg.graph.node_count(), 0);
    }

    #[test]
    fn build_one_basic_block() {
        let mut cfg = CFG::new();
        let insts: Vec<Box<Instruction>> = vec![
            TestInstruction::new(0, Opcode::Add),
            TestInstruction::new(1, Opcode::Add),
            TestInstruction::new(2, Opcode::Ret),
        ];
        cfg.build(&insts);
        assert!(cfg.entry_block.is_some());
        assert_eq!(cfg.graph.node_count(), 1);
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate bitflags;
extern crate gl as gl_lib;
#[cfg(feature = "cgmath")] extern crate cgmath as cgmath_lib;
#[cfg(feature = "image")] extern crate image as image_lib;

mod to_ref;

#[macro_use] mod macros;
pub mod context;
pub mod buffer;
pub mod shader;
pub mod program;
pub mod framebuffer;
pub mod renderbuffer;
pub mod texture;
pub mod image_data;
pub mod vertex_data;
pub mod vertex_buffer;
pub mod index_data;
pub mod uniform_data;
pub mod types;

#[cfg(feature = "cgmath")] mod cgmath_features;
#[cfg(feature = "image")] mod image_features;

pub use gl_lib as gl;
#[cfg(feature = "cgmath")] pub use cgmath_lib as cgmath;
#[cfg(feature = "image")] pub use image_lib as image;

pub use context::*;
pub use buffer::*;
pub use shader::*;
pub use program::*;
pub use framebuffer::*;
pub use renderbuffer::*;
pub use texture::*;
pub use image_data::*;
pub use vertex_data::*;
pub use vertex_buffer::*;
pub use index_data::*;
pub use uniform_data::*;
pub use types::*;

pub mod prelude {
    pub use context::{AContext, BufferContext,
                      ArrayBufferContext, ElementArrayBufferContext,
                      FramebufferContext, ContextFramebufferBuilderExt,
                      ProgramContext, ContextProgramBuilderExt,
                      RenderbufferContext, ContextRenderbufferBuilderExt,
                      TextureBinding, ContextTextureBuilderExt,
                      TextureUnit, TextureUnitBinding, ATextureUnitBinding,
                      TextureUnitBinding2d, TextureUnitBindingCubeMap,
                      TextureUnit0Context, TextureUnit1Context,
                      TextureUnit2Context, TextureUnit3Context,
                      TextureUnit4Context, TextureUnit5Context,
                      TextureUnit6Context, TextureUnit7Context};
    pub use context::ext::*;
    pub use shader::ContextShaderBuilderExt;
    pub use vertex_buffer::{VertexBufferContext, IndexBufferContext,
                            ContextVertexBufferExt};
}
<commit_msg>Don't publicly re-export optional external crates<commit_after>#[macro_use] extern crate bitflags;
extern crate gl as gl_lib;
#[cfg(feature = "cgmath")] extern crate cgmath;
#[cfg(feature = "image")] extern crate image;

mod to_ref;

#[macro_use] mod macros;
pub mod context;
pub mod buffer;
pub mod shader;
pub mod program;
pub mod framebuffer;
pub mod renderbuffer;
pub mod texture;
pub mod image_data;
pub mod vertex_data;
pub mod vertex_buffer;
pub mod index_data;
pub mod uniform_data;
pub mod types;

#[cfg(feature = "cgmath")] mod cgmath_features;
#[cfg(feature = "image")] mod image_features;

pub use gl_lib as gl;

pub use context::*;
pub use buffer::*;
pub use shader::*;
pub use program::*;
pub use framebuffer::*;
pub use renderbuffer::*;
pub use texture::*;
pub use image_data::*;
pub use vertex_data::*;
pub use vertex_buffer::*;
pub use index_data::*;
pub use uniform_data::*;
pub use types::*;

pub mod prelude {
    pub use context::{AContext, BufferContext,
                      ArrayBufferContext, ElementArrayBufferContext,
                      FramebufferContext, ContextFramebufferBuilderExt,
                      ProgramContext, ContextProgramBuilderExt,
                      RenderbufferContext, ContextRenderbufferBuilderExt,
                      TextureBinding, ContextTextureBuilderExt,
                      TextureUnit, TextureUnitBinding, ATextureUnitBinding,
                      TextureUnitBinding2d, TextureUnitBindingCubeMap,
                      TextureUnit0Context, TextureUnit1Context,
                      TextureUnit2Context, TextureUnit3Context,
                      TextureUnit4Context, TextureUnit5Context,
                      TextureUnit6Context, TextureUnit7Context};
    pub use context::ext::*;
    pub use shader::ContextShaderBuilderExt;
    pub use vertex_buffer::{VertexBufferContext, IndexBufferContext,
                            ContextVertexBufferExt};
}
<|endoftext|>


<commit_before>//! Read and write Elma level files.

use std::io::{ Cursor, Read, Write };
use std::fs::File;
use std::ffi::CString;
use byteorder::{ ReadBytesExt, WriteBytesExt, LittleEndian };
use super::{ cstring_read, Position, read_n };
use super::rand::Rng;

// Magic arbitrary number; signifies end-of-data. Followed by Top10 list(s).
const EOD: i32 = 0x0067103A;
// Magic arbitrary number; signifies end-of-file.
const EOF: i32 = 0x00845D52;

/// Type of object.
pub enum ObjectType {
    Apple,
    Exit,
    Killer,
    Player
}

/// Object struct. Every level requires one ObjectType::Player Object and at least one ObjectType::Exit Object.
pub struct Object {
    /// Position. See Position struct.
    pub position: Position<f64>,
    /// Type of Object, see ObjectType.
    pub object_type: ObjectType,
    /// Applies to ObjectType::Apple only.
    ///
    /// 0 = normal
    /// 1 = gravity up
    /// 2 = gravity down
    /// 3 = gravity left
    /// 4 = gravity right
    // TODO: enum with gravity
    pub gravity: i32,
    /// Applies to ObjectType::Apple only. Valid values are 1 to 9.
    pub animation: i32
}

/// Polygon struct.
#[derive(Debug, PartialEq)]
pub struct Polygon {
    /// Grass polygon.
    pub grass: bool,
    /// Vector with all vertices, see Position struct.
    pub vertices: Vec<Position<f64>>
}

impl Polygon {
    pub fn new () -> Polygon {
        Polygon {
            grass: false,
            vertices: vec![]
        }
    }
}

/// Picture struct.
pub struct Picture {
    /// Picture name.
    pub name: CString,
    /// Texture name.
    pub texture: CString,
    /// Mask name.
    pub mask: CString,
    /// Position. See Position struct.
    pub position: Position<f64>,
    /// Z-distance
    pub distance: i32,
    /// Clipping.
    ///
    /// 0 = unclipped
    /// 1 = ground
    /// 2 = sky
    // TODO: make enum
    pub clip: i32
}

/// Top10 list entry struct.
pub struct ListEntry {
    /// Player name.
    pub name: CString,
    /// Time.
    pub time: f64
}

/// Level struct that contains all level information.
pub struct Level {
    /// Elma or Across level.
    pub version: String,
    /// Raw binary data of a loaded or finalized constructed level.
    raw: Vec<u8>,
    /// Random number that links level file to replay files.
    pub link: i32,
    /// Contains four integrity checks (See create_integrity()).
    pub integrity: [f64; 4],
    /// Level name.
    pub name: CString,
    /// LGR file name.
    pub lgr: CString,
    /// Ground texture name.
    pub ground: CString,
    /// Sky texture name.
    pub sky: CString,
    /// Vector with all polygons (See Polygon).
    pub polygons: Vec<Polygon>,
    /// Vector with all objects (See Object).
    pub objects: Vec<Object>,
    /// Vector with all pictures (See Picture).
    pub pictures: Vec<Picture>,
    /// Vector of Top10 single-player names and times.
    pub top10_single: Vec<ListEntry>,
    /// Vector of Top10 multi-player names and times.
    pub top10_multi: Vec<ListEntry>
}

impl Level {
    /// Returns a new Level struct.
    ///
    /// # Examples
    ///
    /// ```
    /// let level = elma::lev::Level::new();
    /// ```
    pub fn new () -> Level {
        Level {
            version: "Elma".to_string(),
            raw: vec![],
            link: 0,
            integrity: [0.0f64; 4],
            name: CString::new("").unwrap(),
            lgr: CString::new("default").unwrap(),
            ground: CString::new("ground").unwrap(),
            sky: CString::new("sky").unwrap(),
            polygons: vec![],
            objects: vec![],
            pictures: vec![],
            top10_single: vec![],
            top10_multi: vec![]
        }
    }

    /// Loads a level file and returns a Level struct.
    ///
    /// # Examples
    ///
    /// ```
    /// let level = elma::lev::Level::load_level("tests/test.lev");
    /// ```
    pub fn load_level (filename: &str) -> Level {
        let mut level = Level::new();
        let mut file = File::open(filename).unwrap();
        let mut buffer = vec![];
        file.read_to_end(&mut buffer).unwrap();
        level.raw = buffer;
        level.parse_level();
        level
    }

    /// Parses the raw binary data into Level struct fields.
    fn parse_level (&mut self) {
        let mut buffer = Cursor::new(&self.raw);
        let mut _data :Vec<u8>;

        // Elma = POT14, Across = POT06.
        // TODO: make Across compatible in 2025.
        let version = read_n(&mut buffer, 5);
        self.version = match version.as_slice() {
            [80, 79, 84, 49, 52] => "Elma".to_string(),
            [80, 79, 84, 48, 54] => "Across".to_string(),
            _ => panic!("Not a valid level file.")
        };

        // Link.
        _data = read_n(&mut buffer, 2); // Never used
        self.link = buffer.read_i32::<LittleEndian>().unwrap();

        // Integrity checksums.
        for i in 0..4 {
            self.integrity[i] = buffer.read_f64::<LittleEndian>().unwrap();
        }

        // Level name.
        self.name = cstring_read(read_n(&mut buffer, 51));
        // LGR name.
        self.lgr = cstring_read(read_n(&mut buffer, 16));
        // Ground texture name.
        self.ground = cstring_read(read_n(&mut buffer, 10));
        // Sky texture name.
        self.sky = cstring_read(read_n(&mut buffer, 10));

        // Polygons.
        let poly_count = (buffer.read_f64::<LittleEndian>().unwrap() - 0.4643643).round() as u16;
        for _ in 0..poly_count {
            let grass = buffer.read_i32::<LittleEndian>().unwrap() > 0;
            let vertex_count = buffer.read_i32::<LittleEndian>().unwrap();
            let mut vertices: Vec<Position<f64>> = vec![];
            for _ in 0..vertex_count {
                let x = buffer.read_f64::<LittleEndian>().unwrap();
                let y = buffer.read_f64::<LittleEndian>().unwrap();
                vertices.push(Position {
                    x: x,
                    y: y
                });
            }
            self.polygons.push(Polygon {
                grass: grass,
                vertices: vertices
            });
        }

        // Objects.
        let object_count = (buffer.read_f64::<LittleEndian>().unwrap() - 0.4643643).round() as u16;
        for _ in 0..object_count {
            let x = buffer.read_f64::<LittleEndian>().unwrap();
            let y = buffer.read_f64::<LittleEndian>().unwrap();
            let position = Position { x: x, y: y };
            let object_type = match buffer.read_i32::<LittleEndian>().unwrap() {
                1 => ObjectType::Exit,
                2 => ObjectType::Apple,
                3 => ObjectType::Killer,
                4 => ObjectType::Player,
                _ => panic!("Not a valid object type")
            };
            let gravity = buffer.read_i32::<LittleEndian>().unwrap();
            let animation = buffer.read_i32::<LittleEndian>().unwrap();

            self.objects.push(Object {
                position: position,
                object_type: object_type,
                gravity: gravity,
                animation: animation
            });
        }

        // Pictures.
        let picture_count = (buffer.read_f64::<LittleEndian>().unwrap() - 0.2345672).round() as u16;
        for _ in 0..picture_count {
            let name = cstring_read(read_n(&mut buffer, 10));
            let texture = cstring_read(read_n(&mut buffer, 10));
            let mask = cstring_read(read_n(&mut buffer, 10));
            let x = buffer.read_f64::<LittleEndian>().unwrap();
            let y = buffer.read_f64::<LittleEndian>().unwrap();
            let distance = buffer.read_i32::<LittleEndian>().unwrap();
            let clip = buffer.read_i32::<LittleEndian>().unwrap();

            self.pictures.push(Picture {
                name: name,
                texture: texture,
                mask: mask,
                position: Position { x: x, y: y },
                distance: distance,
                clip: clip
            });
        }

        // EOD marker expected at this point.
        let expected = buffer.read_i32::<LittleEndian>().unwrap();
        if expected != EOD { panic!("EOD marker mismatch: x0{:x} != x0{:x}", expected, EOD); }

        // First decrypt the top10 blocks.
        let decrypted_top10_data = crypt_top10(read_n(&mut buffer, 688));

        // Single-player list.
        // TODO: do it

        // Multi-player list.
        // TODO: do it

        // EOF marker expected at this point.
        let expected = buffer.read_i32::<LittleEndian>().unwrap();
        if expected != EOF { panic!("EOF marker mismatch: x0{:x} != x0{:x}", expected, EOF); }
    }

    /// Combines the Level struct fields to generate the raw binary data.
    fn convert_to_raw (&self) {
        // TODO: convert
    }

    /// Converts all struct fields into raw binary form and returns it.
    pub fn get_raw (self) -> Vec<u8> {
        self.convert_to_raw();
        self.raw
    }

    /// Saves level as a file.
    pub fn save_lev (self, filename: &str) {
        self.convert_to_raw();
        let mut file = File::create(&filename).unwrap();
        // TODO: write stuff.
    }
}

/// Decrypt and encrypt top10 list data. Same algorithm for both.
pub fn crypt_top10 (mut top10: Vec<u8>) -> Vec<u8> {
    // Who knows
    let mut ebp8: i16 = 0x15;
    let mut ebp10: i16 = 0x2637;

    for n in 0..688 {
        top10[n] ^= (ebp8 & 0xFF) as u8;
        ebp10 = ebp10.wrapping_add((ebp8.wrapping_rem(0xD3D)).wrapping_mul(0xD3D));
        ebp8 = ebp10.wrapping_mul(0x1F).wrapping_add(0xD3D);
    }

    top10
}
<commit_msg>Parse top10 lists<commit_after>//! Read and write Elma level files.

use std::io::{ Cursor, Read, Write };
use std::fs::File;
use std::ffi::CString;
use byteorder::{ ByteOrder, ReadBytesExt, WriteBytesExt, LittleEndian };
use super::{ cstring_read, Position, read_n };
use super::rand::Rng;

// Magic arbitrary number; signifies end-of-data. Followed by Top10 list(s).
const EOD: i32 = 0x0067103A;
// Magic arbitrary number; signifies end-of-file.
const EOF: i32 = 0x00845D52;

/// Type of object.
pub enum ObjectType {
    Apple,
    Exit,
    Killer,
    Player
}

/// Object struct. Every level requires one ObjectType::Player Object and at least one ObjectType::Exit Object.
pub struct Object {
    /// Position. See Position struct.
    pub position: Position<f64>,
    /// Type of Object, see ObjectType.
    pub object_type: ObjectType,
    /// Applies to ObjectType::Apple only.
    ///
    /// 0 = normal
    /// 1 = gravity up
    /// 2 = gravity down
    /// 3 = gravity left
    /// 4 = gravity right
    // TODO: enum with gravity
    pub gravity: i32,
    /// Applies to ObjectType::Apple only. Valid values are 1 to 9.
    pub animation: i32
}

/// Polygon struct.
#[derive(Debug, PartialEq)]
pub struct Polygon {
    /// Grass polygon.
    pub grass: bool,
    /// Vector with all vertices, see Position struct.
    pub vertices: Vec<Position<f64>>
}

impl Polygon {
    pub fn new () -> Polygon {
        Polygon {
            grass: false,
            vertices: vec![]
        }
    }
}

/// Picture struct.
pub struct Picture {
    /// Picture name.
    pub name: CString,
    /// Texture name.
    pub texture: CString,
    /// Mask name.
    pub mask: CString,
    /// Position. See Position struct.
    pub position: Position<f64>,
    /// Z-distance
    pub distance: i32,
    /// Clipping.
    ///
    /// 0 = unclipped
    /// 1 = ground
    /// 2 = sky
    // TODO: make enum
    pub clip: i32
}

/// Top10 list entry struct.
#[derive(Debug)]
pub struct ListEntry {
    /// Player 1 name.
    pub name_1: CString,
    /// Player 2 name.
    pub name_2: CString,
    /// Time.
    pub time: i32
}

/// Level struct that contains all level information.
pub struct Level {
    /// Elma or Across level.
    pub version: String,
    /// Raw binary data of a loaded or finalized constructed level.
    raw: Vec<u8>,
    /// Random number that links level file to replay files.
    pub link: i32,
    /// Contains four integrity checks (See create_integrity()).
    pub integrity: [f64; 4],
    /// Level name.
    pub name: CString,
    /// LGR file name.
    pub lgr: CString,
    /// Ground texture name.
    pub ground: CString,
    /// Sky texture name.
    pub sky: CString,
    /// Vector with all polygons (See Polygon).
    pub polygons: Vec<Polygon>,
    /// Vector with all objects (See Object).
    pub objects: Vec<Object>,
    /// Vector with all pictures (See Picture).
    pub pictures: Vec<Picture>,
    /// Vector of Top10 single-player names and times.
    pub top10_single: Vec<ListEntry>,
    /// Vector of Top10 multi-player names and times.
    pub top10_multi: Vec<ListEntry>
}

impl Level {
    /// Returns a new Level struct.
    ///
    /// # Examples
    ///
    /// ```
    /// let level = elma::lev::Level::new();
    /// ```
    pub fn new () -> Level {
        Level {
            version: "Elma".to_string(),
            raw: vec![],
            link: 0,
            integrity: [0.0f64; 4],
            name: CString::new("").unwrap(),
            lgr: CString::new("default").unwrap(),
            ground: CString::new("ground").unwrap(),
            sky: CString::new("sky").unwrap(),
            polygons: vec![],
            objects: vec![],
            pictures: vec![],
            top10_single: vec![],
            top10_multi: vec![]
        }
    }

    /// Loads a level file and returns a Level struct.
    ///
    /// # Examples
    ///
    /// ```
    /// let level = elma::lev::Level::load_level("tests/test.lev");
    /// ```
    pub fn load_level (filename: &str) -> Level {
        let mut level = Level::new();
        let mut file = File::open(filename).unwrap();
        let mut buffer = vec![];
        file.read_to_end(&mut buffer).unwrap();
        level.raw = buffer;
        level.parse_level();
        level
    }

    /// Parses the raw binary data into Level struct fields.
    fn parse_level (&mut self) {
        let mut buffer = Cursor::new(&self.raw);
        let mut _data :Vec<u8>;

        // Elma = POT14, Across = POT06.
        // TODO: make Across compatible in 2025.
        let version = read_n(&mut buffer, 5);
        self.version = match version.as_slice() {
            [80, 79, 84, 49, 52] => "Elma".to_string(),
            [80, 79, 84, 48, 54] => "Across".to_string(),
            _ => panic!("Not a valid level file.")
        };

        // Link.
        _data = read_n(&mut buffer, 2); // Never used
        self.link = buffer.read_i32::<LittleEndian>().unwrap();

        // Integrity checksums.
        for i in 0..4 {
            self.integrity[i] = buffer.read_f64::<LittleEndian>().unwrap();
        }

        // Level name.
        self.name = cstring_read(read_n(&mut buffer, 51));
        // LGR name.
        self.lgr = cstring_read(read_n(&mut buffer, 16));
        // Ground texture name.
        self.ground = cstring_read(read_n(&mut buffer, 10));
        // Sky texture name.
        self.sky = cstring_read(read_n(&mut buffer, 10));

        // Polygons.
        let poly_count = (buffer.read_f64::<LittleEndian>().unwrap() - 0.4643643).round() as u16;
        for _ in 0..poly_count {
            let grass = buffer.read_i32::<LittleEndian>().unwrap() > 0;
            let vertex_count = buffer.read_i32::<LittleEndian>().unwrap();
            let mut vertices: Vec<Position<f64>> = vec![];
            for _ in 0..vertex_count {
                let x = buffer.read_f64::<LittleEndian>().unwrap();
                let y = buffer.read_f64::<LittleEndian>().unwrap();
                vertices.push(Position {
                    x: x,
                    y: y
                });
            }
            self.polygons.push(Polygon {
                grass: grass,
                vertices: vertices
            });
        }

        // Objects.
        let object_count = (buffer.read_f64::<LittleEndian>().unwrap() - 0.4643643).round() as u16;
        for _ in 0..object_count {
            let x = buffer.read_f64::<LittleEndian>().unwrap();
            let y = buffer.read_f64::<LittleEndian>().unwrap();
            let position = Position { x: x, y: y };
            let object_type = match buffer.read_i32::<LittleEndian>().unwrap() {
                1 => ObjectType::Exit,
                2 => ObjectType::Apple,
                3 => ObjectType::Killer,
                4 => ObjectType::Player,
                _ => panic!("Not a valid object type")
            };
            let gravity = buffer.read_i32::<LittleEndian>().unwrap();
            let animation = buffer.read_i32::<LittleEndian>().unwrap() + 1;

            self.objects.push(Object {
                position: position,
                object_type: object_type,
                gravity: gravity,
                animation: animation
            });
        }

        // Pictures.
        let picture_count = (buffer.read_f64::<LittleEndian>().unwrap() - 0.2345672).round() as u16;
        for _ in 0..picture_count {
            let name = cstring_read(read_n(&mut buffer, 10));
            let texture = cstring_read(read_n(&mut buffer, 10));
            let mask = cstring_read(read_n(&mut buffer, 10));
            let x = buffer.read_f64::<LittleEndian>().unwrap();
            let y = buffer.read_f64::<LittleEndian>().unwrap();
            let distance = buffer.read_i32::<LittleEndian>().unwrap();
            let clip = buffer.read_i32::<LittleEndian>().unwrap();

            self.pictures.push(Picture {
                name: name,
                texture: texture,
                mask: mask,
                position: Position { x: x, y: y },
                distance: distance,
                clip: clip
            });
        }

        // EOD marker expected at this point.
        let expected = buffer.read_i32::<LittleEndian>().unwrap();
        if expected != EOD { panic!("EOD marker mismatch: x0{:x} != x0{:x}", expected, EOD); }

        // First decrypt the top10 blocks.
        let decrypted_top10_data = crypt_top10(read_n(&mut buffer, 688));

        // Single-player list.
        let single = &decrypted_top10_data[0..344];
        let times = LittleEndian::read_i32(&single[0..4]);
        for n in 0..times {
            let time_offset: usize = (4 + n * 4) as usize;
            let time_end: usize = time_offset + 4;
            let name_offset: usize = (44 + n * 15) as usize;
            let name_end: usize = name_offset + 15;
            // All of this pains me even though I don't understand it...
            let mut name = Vec::new();
            name.extend_from_slice(&single[name_offset..name_end]);
            self.top10_single.push(ListEntry {
                time: LittleEndian::read_i32(&single[time_offset..time_end]),
                name_1: cstring_read(name),
                name_2: CString::new("").unwrap()
            });
        }

        // Multi-player list.
        let multi = &decrypted_top10_data[344..688];
        let times = LittleEndian::read_i32(&multi[0..4]);
        for n in 0..times {
            let time_offset: usize = (4 + n * 4) as usize;
            let time_end: usize = time_offset + 4;
            let name_offset: usize = (44 + n * 15) as usize;
            let name_end: usize = name_offset + 15;
            let name2_offset: usize = (194 + n * 15) as usize;
            let name2_end: usize = name2_offset + 15;
            // All of this pains me even though I don't understand it...
            let mut name = Vec::new();
            let mut name2 = Vec::new();
            name.extend_from_slice(&multi[name_offset..name_end]);
            name2.extend_from_slice(&multi[name2_offset..name2_end]);
            self.top10_multi.push(ListEntry {
                time: LittleEndian::read_i32(&multi[time_offset..time_end]),
                name_1: cstring_read(name),
                name_2: cstring_read(name2)
            });
        }

        // EOF marker expected at this point.
        let expected = buffer.read_i32::<LittleEndian>().unwrap();
        if expected != EOF { panic!("EOF marker mismatch: x0{:x} != x0{:x}", expected, EOF); }
    }

    /// Combines the Level struct fields to generate the raw binary data.
    fn convert_to_raw (&self) {
        // TODO: convert
    }

    /// Converts all struct fields into raw binary form and returns it.
    pub fn get_raw (self) -> Vec<u8> {
        self.convert_to_raw();
        self.raw
    }

    /// Saves level as a file.
    pub fn save_lev (self, filename: &str) {
        self.convert_to_raw();
        let mut file = File::create(&filename).unwrap();
        // TODO: write stuff.
    }
}

/// Decrypt and encrypt top10 list data. Same algorithm for both.
pub fn crypt_top10 (mut top10: Vec<u8>) -> Vec<u8> {
    // Who knows
    let mut ebp8: i16 = 0x15;
    let mut ebp10: i16 = 0x2637;

    for n in 0..688 {
        top10[n] ^= (ebp8 & 0xFF) as u8;
        ebp10 = ebp10.wrapping_add((ebp8.wrapping_rem(0xD3D)).wrapping_mul(0xD3D));
        ebp8 = ebp10.wrapping_mul(0x1F).wrapping_add(0xD3D);
    }

    top10
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;
extern crate uuid;
use std::ffi::CStr;
use std::os::raw::c_char;

use nom::{le_u64,le_u32,le_i32};
use nom::IResult;

pub use constants::*;

mod constants;

// These are all integer_t, aka int
#[allow(non_camel_case_types)]
pub type cpu_type_t = i32;
#[allow(non_camel_case_types)]
pub type cpu_subtype_t = u32;
#[allow(non_camel_case_types)]
pub type vm_prot_t = i32;

fn to_string(buf: &[u8]) -> String {
    let slice = unsafe { CStr::from_ptr(buf.as_ptr() as *const c_char) };
    std::str::from_utf8(slice.to_bytes()).unwrap().to_string()
}

#[derive(Debug)]
pub struct MachHeader<'a> {
    pub header: Header,
    pub uuid: Option<uuid::Uuid>,
    pub segments: Vec<SegmentCommand>,
    pub commands: Vec<LoadCommand<'a>>,
}

#[derive(Debug)]
pub struct Header {
    pub magic: u32,
    pub cputype: cpu_type_t,
    pub cpusubtype: cpu_subtype_t,
    pub filetype: u32,
    pub ncmds: u32,
    pub sizeofcmds: u32,
    pub flags: u32,
    reserved: u32,
}

pub const LC_SEGMENT_64: u32 = 25;

impl<'a> MachHeader<'a> {
    pub fn parse(bytes: &'a [u8]) -> Option<MachHeader> {
        if let IResult::Done(_rest, header) = mach_header(bytes) {
            let mut rest = _rest;
            let mut uuid = None;
            let mut commands = vec![];
            let mut segments = vec![];
            for _ in 0.. header.ncmds {
                // Thanks to nom being zero copy, we can actually parse the same memory twice.
                // We have one attempt to see what type it is, then we have another go to get that
                // object.
                if let IResult::Done(_, cmd) = load_command(rest) {
                    let len = cmd.cmdsize;
                    match cmd.cmd {
                        c if c == LcType::LC_SEGMENT_64 as u32 => {
                            if let IResult::Done(_rest, mut segment) = segment_command(rest) {
                                let sections_slice = &_rest[.. (segment.cmdsize - 72) as usize];
                                if let IResult::Done(leftover, sections) = sections(sections_slice) {
                                    assert_eq!(sections.len(), segment.nsects as usize);
                                    assert_eq!(leftover.len(), 0);
                                    segment.sections.extend(sections)
                                } else {
                                    return None
                                }
                                segments.push(segment);
                            } else {
                                return None
                            }
                        },
                        c if c == LcType::LC_UUID as u32 => {
                            if let Ok(_uuid) = uuid::Uuid::from_bytes(cmd.data) {
                                uuid = Some(_uuid);
                            }
                        },
                        _ => {
                            commands.push(cmd)
                        }
                    }
                    rest = &rest[len as usize..];
                }
            }

            Some(MachHeader {
                    header: header,
                    uuid: uuid,
                    commands: commands,
                    segments: segments,
            })
        } else {
            return None
        }
    }
}

#[derive(Debug)]
pub struct SegmentCommand {
    pub cmd: u32,
    pub cmdsize: u32,
    pub segname: String,
    pub vmaddr: u64,
    pub vmsize: u64,
    pub fileoff: u64,
    pub filesize: u64,
    pub maxprot: vm_prot_t,
    pub initprot: vm_prot_t,
    pub nsects: u32,
    pub flags: u32,
    pub sections: Vec<Section>,
}

#[derive(Debug)]
pub struct LoadCommand<'a> {
    pub cmd: u32,
    pub cmdsize: u32,
    pub data: &'a [u8],
}

#[derive(Debug)]
pub struct Section {
	pub sectname: String, /* name of this section */
    pub segname: String, /* segment this section goes in */
    pub addr: u64, /* memory address of this section */
    pub size: u64, /* size in bytes of this section */
    pub offset: u32, /* file offset of this section */
    pub align: u32, /* section alignment (power of 2) */
    pub reloff: u32, /* file offset of relocation entries */
    pub nreloc: u32, /* number of relocation entries */
    pub flags: u32, /* flags (section type and attributes)*/
    reserved1: u32, /* reserved (for offset or index) */
    reserved2: u32, /* reserved (for count or sizeof) */
    reserved3: u32, /* reserved */
}

named!(sections<&[u8], Vec<Section> >,
       many0!(section));
named!(section<&[u8], Section>,
       chain!(
           sectname: take!(16) ~
           segname: take!(16) ~
           addr: le_u64 ~
           size: le_u64 ~
           offset: le_u32 ~
           align: le_u32 ~
           reloff: le_u32 ~
           nreloc: le_u32 ~
           flags: le_u32 ~
           reserved1: le_u32 ~
           reserved2: le_u32 ~
           reserved3: le_u32 ,

           || {
               // assert_eq!(size, 80);
               Section {
                   sectname: to_string(sectname),
                   segname: to_string(segname),
                   addr: addr,
                   size: size,
                   offset: offset,
                   align: align,
                   reloff: reloff,
                   nreloc: nreloc,
                   flags: flags,
                   reserved1: reserved1,
                   reserved2: reserved2,
                   reserved3: reserved3,
               }
           }
           )
       );

named!(load_command<&[u8], LoadCommand>,
       chain!(
           cmd: le_u32 ~
           cmdsize: le_u32 ~
           data: take!(cmdsize - 8),

           || {
               LoadCommand {
                   cmd: cmd,
                   cmdsize: cmdsize,
                   data: data,
               }
           }
           )
       );

named!(mach_header<&[u8], Header>,
       chain!(
           magic: le_u32 ~
           cputype: le_i32 ~
           cpusubtype: le_u32 ~
           filetype: le_u32 ~
           ncmds: le_u32 ~
           sizeofcmds: le_u32 ~
           flags: le_u32 ~
           reserved: le_u32,

           || {
               assert_eq!(MH_MAGIC_64, magic);
               Header {
                   magic: magic,
                   cputype: cputype,
                   // This value needs to be masked to match otool -h
                   cpusubtype: cpusubtype,
                   filetype: filetype,
                   ncmds: ncmds,
                   sizeofcmds: sizeofcmds,
                   flags: flags,
                   reserved: reserved,
               }
           }
           )
       );

named!(segment_command<&[u8], SegmentCommand>,
       chain!(
           cmd: le_u32 ~
           cmdsize: le_u32 ~
           segname: take!(16) ~
           vmaddr: le_u64 ~
           vmsize: le_u64 ~
           fileoff: le_u64 ~
           filesize: le_u64 ~
           maxprot: le_i32 ~
           initprot: le_i32 ~
           nsects: le_u32 ~
           flags: le_u32 ,

           || {
               SegmentCommand {
                   cmd: cmd,
                   cmdsize: cmdsize,
                   segname: to_string(segname),
                   vmaddr: vmaddr,
                   vmsize: vmsize,
                   fileoff: fileoff,
                   filesize: filesize,
                   maxprot: maxprot,
                   initprot: initprot,
                   nsects: nsects,
                   flags: flags,
                   sections: vec![],
               }
           }
        )
    );

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_parses_lol() {
        let binary = include_bytes!("../test/lol");
        let header = MachHeader::parse(binary).unwrap();
        assert_eq!(header.header.ncmds, 14);
    }

    #[test]
    fn test_parses_uuid() {
        let binary = include_bytes!("../test/dwarfdump");
        let header = MachHeader::parse(binary).unwrap();

        let expected = "1b1a1ba2-c94d-3dc9-b55c-97a296ff0a35";
        let uuid = format!("{}", header.uuid.unwrap());

        assert_eq!(expected, uuid);
    }
}
<commit_msg>Remove unused constant<commit_after>#[macro_use]
extern crate nom;
extern crate uuid;
use std::ffi::CStr;
use std::os::raw::c_char;

use nom::{le_u64,le_u32,le_i32};
use nom::IResult;

pub use constants::*;

mod constants;

// These are all integer_t, aka int
#[allow(non_camel_case_types)]
pub type cpu_type_t = i32;
#[allow(non_camel_case_types)]
pub type cpu_subtype_t = u32;
#[allow(non_camel_case_types)]
pub type vm_prot_t = i32;

fn to_string(buf: &[u8]) -> String {
    let slice = unsafe { CStr::from_ptr(buf.as_ptr() as *const c_char) };
    std::str::from_utf8(slice.to_bytes()).unwrap().to_string()
}

#[derive(Debug)]
pub struct MachHeader<'a> {
    pub header: Header,
    pub uuid: Option<uuid::Uuid>,
    pub segments: Vec<SegmentCommand>,
    pub commands: Vec<LoadCommand<'a>>,
}

#[derive(Debug)]
pub struct Header {
    pub magic: u32,
    pub cputype: cpu_type_t,
    pub cpusubtype: cpu_subtype_t,
    pub filetype: u32,
    pub ncmds: u32,
    pub sizeofcmds: u32,
    pub flags: u32,
    reserved: u32,
}

impl<'a> MachHeader<'a> {
    pub fn parse(bytes: &'a [u8]) -> Option<MachHeader> {
        if let IResult::Done(_rest, header) = mach_header(bytes) {
            let mut rest = _rest;
            let mut uuid = None;
            let mut commands = vec![];
            let mut segments = vec![];
            for _ in 0.. header.ncmds {
                // Thanks to nom being zero copy, we can actually parse the same memory twice.
                // We have one attempt to see what type it is, then we have another go to get that
                // object.
                if let IResult::Done(_, cmd) = load_command(rest) {
                    let len = cmd.cmdsize;
                    match cmd.cmd {
                        c if c == LcType::LC_SEGMENT_64 as u32 => {
                            if let IResult::Done(_rest, mut segment) = segment_command(rest) {
                                let sections_slice = &_rest[.. (segment.cmdsize - 72) as usize];
                                if let IResult::Done(leftover, sections) = sections(sections_slice) {
                                    assert_eq!(sections.len(), segment.nsects as usize);
                                    assert_eq!(leftover.len(), 0);
                                    segment.sections.extend(sections)
                                } else {
                                    return None
                                }
                                segments.push(segment);
                            } else {
                                return None
                            }
                        },
                        c if c == LcType::LC_UUID as u32 => {
                            if let Ok(_uuid) = uuid::Uuid::from_bytes(cmd.data) {
                                uuid = Some(_uuid);
                            }
                        },
                        _ => {
                            commands.push(cmd)
                        }
                    }
                    rest = &rest[len as usize..];
                }
            }

            Some(MachHeader {
                    header: header,
                    uuid: uuid,
                    commands: commands,
                    segments: segments,
            })
        } else {
            return None
        }
    }
}

#[derive(Debug)]
pub struct SegmentCommand {
    pub cmd: u32,
    pub cmdsize: u32,
    pub segname: String,
    pub vmaddr: u64,
    pub vmsize: u64,
    pub fileoff: u64,
    pub filesize: u64,
    pub maxprot: vm_prot_t,
    pub initprot: vm_prot_t,
    pub nsects: u32,
    pub flags: u32,
    pub sections: Vec<Section>,
}

#[derive(Debug)]
pub struct LoadCommand<'a> {
    pub cmd: u32,
    pub cmdsize: u32,
    pub data: &'a [u8],
}

#[derive(Debug)]
pub struct Section {
	pub sectname: String, /* name of this section */
    pub segname: String, /* segment this section goes in */
    pub addr: u64, /* memory address of this section */
    pub size: u64, /* size in bytes of this section */
    pub offset: u32, /* file offset of this section */
    pub align: u32, /* section alignment (power of 2) */
    pub reloff: u32, /* file offset of relocation entries */
    pub nreloc: u32, /* number of relocation entries */
    pub flags: u32, /* flags (section type and attributes)*/
    reserved1: u32, /* reserved (for offset or index) */
    reserved2: u32, /* reserved (for count or sizeof) */
    reserved3: u32, /* reserved */
}

named!(sections<&[u8], Vec<Section> >,
       many0!(section));
named!(section<&[u8], Section>,
       chain!(
           sectname: take!(16) ~
           segname: take!(16) ~
           addr: le_u64 ~
           size: le_u64 ~
           offset: le_u32 ~
           align: le_u32 ~
           reloff: le_u32 ~
           nreloc: le_u32 ~
           flags: le_u32 ~
           reserved1: le_u32 ~
           reserved2: le_u32 ~
           reserved3: le_u32 ,

           || {
               // assert_eq!(size, 80);
               Section {
                   sectname: to_string(sectname),
                   segname: to_string(segname),
                   addr: addr,
                   size: size,
                   offset: offset,
                   align: align,
                   reloff: reloff,
                   nreloc: nreloc,
                   flags: flags,
                   reserved1: reserved1,
                   reserved2: reserved2,
                   reserved3: reserved3,
               }
           }
           )
       );

named!(load_command<&[u8], LoadCommand>,
       chain!(
           cmd: le_u32 ~
           cmdsize: le_u32 ~
           data: take!(cmdsize - 8),

           || {
               LoadCommand {
                   cmd: cmd,
                   cmdsize: cmdsize,
                   data: data,
               }
           }
           )
       );

named!(mach_header<&[u8], Header>,
       chain!(
           magic: le_u32 ~
           cputype: le_i32 ~
           cpusubtype: le_u32 ~
           filetype: le_u32 ~
           ncmds: le_u32 ~
           sizeofcmds: le_u32 ~
           flags: le_u32 ~
           reserved: le_u32,

           || {
               assert_eq!(MH_MAGIC_64, magic);
               Header {
                   magic: magic,
                   cputype: cputype,
                   // This value needs to be masked to match otool -h
                   cpusubtype: cpusubtype,
                   filetype: filetype,
                   ncmds: ncmds,
                   sizeofcmds: sizeofcmds,
                   flags: flags,
                   reserved: reserved,
               }
           }
           )
       );

named!(segment_command<&[u8], SegmentCommand>,
       chain!(
           cmd: le_u32 ~
           cmdsize: le_u32 ~
           segname: take!(16) ~
           vmaddr: le_u64 ~
           vmsize: le_u64 ~
           fileoff: le_u64 ~
           filesize: le_u64 ~
           maxprot: le_i32 ~
           initprot: le_i32 ~
           nsects: le_u32 ~
           flags: le_u32 ,

           || {
               SegmentCommand {
                   cmd: cmd,
                   cmdsize: cmdsize,
                   segname: to_string(segname),
                   vmaddr: vmaddr,
                   vmsize: vmsize,
                   fileoff: fileoff,
                   filesize: filesize,
                   maxprot: maxprot,
                   initprot: initprot,
                   nsects: nsects,
                   flags: flags,
                   sections: vec![],
               }
           }
        )
    );

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_parses_lol() {
        let binary = include_bytes!("../test/lol");
        let header = MachHeader::parse(binary).unwrap();
        assert_eq!(header.header.ncmds, 14);
    }

    #[test]
    fn test_parses_uuid() {
        let binary = include_bytes!("../test/dwarfdump");
        let header = MachHeader::parse(binary).unwrap();

        let expected = "1b1a1ba2-c94d-3dc9-b55c-97a296ff0a35";
        let uuid = format!("{}", header.uuid.unwrap());

        assert_eq!(expected, uuid);
    }
}
<|endoftext|>


<commit_before>//! Conversion between integers and roman numerals.

static ROMAN: [(char, i32); 7] = [
    ('I', 1), ('V', 5), ('X', 10), ('L', 50), ('C', 100), ('D', 500), ('M', 1000) ];
static ROMAN_PAIRS: [(&'static str, i32); 13] = [
    ("M", 1000), ("CM", 900), ("D",  500), ("CD", 400),
    ("C", 100),  ("XC", 90),  ("L",  50),  ("XL", 40),
    ("X", 10),   ("IX", 9),   ("V",  5),   ("IV", 4), ("I",  1) ];

/// The largest number representable as a roman numeral.
pub static MAX: i32 = 3999;

/// Converts an integer into a roman numeral.
///
/// Works for integer between 1 and 3999 inclusive, returns None otherwise.
///
/// # Example
///
/// ```
/// let x = roman::to(14);
/// assert_eq!(x.unwrap(), "XIV");
/// ```
///
pub fn to(n: i32) -> Option<String> {
    if n <= 0 || n > MAX { return None }
    let mut out = String::new();
    let mut n = n;
    for &(name, value) in ROMAN_PAIRS.iter() {
        while n >= value {
            n -= value;
            out.push_str(name);
        }
    }
    assert!(n == 0);
    Some(out)
}

#[test]
fn test_to_roman() {
    let roman = ["I","II","III","IV","V","VI","VII","VIII","IX","X",
                 "XI","XII","XIII","XIV","XV","XVI","XVII","XVIII","XIX","XX",
                 "XXI","XXII"];
    for (i, x) in roman.iter().enumerate() {
        let n = (i+1) as i32;
        assert_eq!(to(n).unwrap(), *x);
    }
    assert_eq!(to(1984).unwrap(), "MCMLXXXIV");
}

/// Converts a roman numeral to an integer.
///
/// Works for integer between 1 and 3999 inclusive, returns None otherwise.
///
/// # Example
///
/// ```
/// let x = roman::from("XIV");
/// assert_eq!(x.unwrap(), 14);
/// ```
///
pub fn from(txt: &str) -> Option<i32> {
    let n = match from_lax(txt) {
        Some(n) => n,
        None    => return None
    };
    match to(n) {
        Some(ref x) if *x == txt => Some(n),
        _ => None
    }
}

fn from_lax(txt: &str) -> Option<i32> {
    let (mut n, mut max) = (0, 0);
    for c in txt.chars().rev() {
        let it = ROMAN.iter().find(|x| { let &(ch, _) = *x; ch == c } );
        if it.is_none() { return None }
        let &(_, val) = it.unwrap();
        if val < max {
            n -= val;
        } else {
            n += val;
            max = val;
        }
    }
    Some(n)
}

#[test]
fn test_from() {
    assert!(from("i").is_none());
}

#[test]
fn test_to_from() {
    for n in 1..MAX {
        assert_eq!(from(&to(n).unwrap()).unwrap(), n);
    }
}
<commit_msg>Revert to using split()<commit_after>//! Conversion between integers and roman numerals.

static ROMAN: [(char, i32); 7] = [
    ('I', 1), ('V', 5), ('X', 10), ('L', 50), ('C', 100), ('D', 500), ('M', 1000) ];
static ROMAN_PAIRS: [(&'static str, i32); 13] = [
    ("M", 1000), ("CM", 900), ("D",  500), ("CD", 400),
    ("C", 100),  ("XC", 90),  ("L",  50),  ("XL", 40),
    ("X", 10),   ("IX", 9),   ("V",  5),   ("IV", 4), ("I",  1) ];

/// The largest number representable as a roman numeral.
pub static MAX: i32 = 3999;

/// Converts an integer into a roman numeral.
///
/// Works for integer between 1 and 3999 inclusive, returns None otherwise.
///
/// # Example
///
/// ```
/// let x = roman::to(14);
/// assert_eq!(x.unwrap(), "XIV");
/// ```
///
pub fn to(n: i32) -> Option<String> {
    if n <= 0 || n > MAX { return None }
    let mut out = String::new();
    let mut n = n;
    for &(name, value) in ROMAN_PAIRS.iter() {
        while n >= value {
            n -= value;
            out.push_str(name);
        }
    }
    assert!(n == 0);
    Some(out)
}

#[test]
fn test_to_roman() {
    let roman = "I II III IV V VI VII VIII IX X XI XII XIII XIV XV XVI XVII XVIII XIX XX XXI XXII".split(' ');
    for (i, x) in roman.enumerate() {
        let n = (i+1) as i32;
        assert_eq!(to(n).unwrap(), x);
    }
    assert_eq!(to(1984).unwrap(), "MCMLXXXIV");
}

/// Converts a roman numeral to an integer.
///
/// Works for integer between 1 and 3999 inclusive, returns None otherwise.
///
/// # Example
///
/// ```
/// let x = roman::from("XIV");
/// assert_eq!(x.unwrap(), 14);
/// ```
///
pub fn from(txt: &str) -> Option<i32> {
    let n = match from_lax(txt) {
        Some(n) => n,
        None    => return None
    };
    match to(n) {
        Some(ref x) if *x == txt => Some(n),
        _ => None
    }
}

fn from_lax(txt: &str) -> Option<i32> {
    let (mut n, mut max) = (0, 0);
    for c in txt.chars().rev() {
        let it = ROMAN.iter().find(|x| { let &(ch, _) = *x; ch == c } );
        if it.is_none() { return None }
        let &(_, val) = it.unwrap();
        if val < max {
            n -= val;
        } else {
            n += val;
            max = val;
        }
    }
    Some(n)
}

#[test]
fn test_from() {
    assert!(from("i").is_none());
}

#[test]
fn test_to_from() {
    for n in 1..MAX {
        assert_eq!(from(&to(n).unwrap()).unwrap(), n);
    }
}
<|endoftext|>


<commit_before>//! RethinkDB protocol implementation in Rust

extern crate protobuf;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate quick_error;
extern crate r2d2;
extern crate scram;
extern crate byteorder;
extern crate bufstream;
extern crate uuid;

pub mod conn;
pub mod proto;
pub mod types;
pub mod errors;

pub type Result<T> = ::std::result::Result<T, errors::Error>;
<commit_msg>Fix boolean encoding<commit_after>//! RethinkDB protocol implementation in Rust

extern crate protobuf;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate quick_error;
extern crate r2d2;
extern crate scram;
extern crate byteorder;
extern crate bufstream;
extern crate uuid;

pub mod conn;
pub mod proto;
pub mod types;
pub mod errors;

use std::collections::BTreeMap;

use serde_json::value::{Value, Map};
use protobuf::repeated::RepeatedField;
use protobuf::ProtobufEnum;
use proto::{
    Term, Datum,
    Term_TermType as TT,
    Term_AssocPair as TA,
    Datum_DatumType as DT,
};

pub type Result<T> = ::std::result::Result<T, errors::Error>;

type Array = Vec<Term>;

type Object = Map<String, Term>;

pub trait IsDatum {
    fn is_datum(&self) -> bool;
}

pub trait IsEmpty {
    fn is_empty(&self) -> bool;
}

pub trait Encode {
    fn encode(&self) -> String;
}

pub trait Decode {
    fn decode(&self) -> Value;
}

#[derive(Debug, Clone)]
pub struct QueryInfo {
    db_set: bool,
    query: String,
    op: String,
}

impl QueryInfo {
    pub fn db_set(&self) -> bool {
        self.db_set
    }

    /*
    fn query(&self) -> String {
        self.query.to_string()
    }

    fn cmd_type(&self) -> String {
        self.op.to_string()
    }
    */
}

pub trait Info {
    fn info(&self) -> QueryInfo;
}

impl Info for Term {
    fn info(&self) -> QueryInfo {
        let mut inf = QueryInfo {
            db_set: false,
            query: String::new(),
            op: String::from("read"),
        };
        let cmd = self.get_field_type()
            .descriptor()
            .name();
        if cmd == "INSERT" {
            inf.op = String::from("INSERT");
        } else if cmd == "CHANGES" {
            inf.op = String::from("CHANGES");
        } else if cmd == "DB" {
            inf.db_set = true;
        }
        inf.query.push_str(&format!(".{}(arg)", cmd));
        let terms = self.get_args();
        if !terms.is_empty() {
            for term in terms {
                let tinf = term.info();
                if tinf.db_set {
                    inf.db_set = true;
                }
                inf.query.push_str(&tinf.query);
                if tinf.op != "read" {
                    inf.op = tinf.op;
                }
            }
        }
        inf
    }
}

impl IsDatum for Term {
    fn is_datum(&self) -> bool {
        self.get_field_type() == TT::DATUM
    }
}

impl IsEmpty for Term {
    fn is_empty(&self) -> bool {
        *self == Term::new()
    }
}

impl Encode for Vec<TA> {
    fn encode(&self) -> String {
        let mut opts = String::from("{");
        for term in self {
            opts.push_str(&format!("\"{}\":{},", term.get_key(), term.get_val().encode()));
        }
        opts = opts.trim_right_matches(",").to_string();
        opts.push_str("}");
        opts
    }
}

impl Encode for Datum {
    fn encode(&self) -> String {
        match self.get_field_type() {
            DT::R_NULL => {
                String::from("null")
            },
            DT::R_BOOL => {
                format!("{}", self.get_r_bool())
            },
            DT::R_NUM => {
                format!("{}", self.get_r_num())
            },
            DT::R_STR => {
                format!("\"{}\"", self.get_r_str())
            },
            DT::R_ARRAY => {
                let mut args = format!("[{},[", TT::MAKE_ARRAY.value());
                for term in self.get_r_array() {
                    args.push_str(&format!("{},", term.encode()));
                }
                args = args.trim_right_matches(",").to_string();
                args.push_str("]]");
                args
            },
            DT::R_OBJECT => {
                let mut args = String::from("{");
                for term in self.get_r_object() {
                    args.push_str(&format!("\"{}\":{},", term.get_key(), term.get_val().encode()));
                }
                args = args.trim_right_matches(",").to_string();
                args.push_str("}");
                args
            },
            DT::R_JSON => {
                unimplemented!();
            },
        }
    }
}

impl Encode for Term {
    fn encode(&self) -> String {
        let mut res = Vec::new();
        if !self.is_datum() {
            res.push(format!("[{}", self.get_field_type().value()));
        }
        if self.has_datum() {
            let datum = self.get_datum();
            res.push(datum.encode());
        }
        let terms = self.get_args();
        if !terms.is_empty() {
            let mut args = String::from("[");
            for term in terms {
                args.push_str(&format!("{},", term.encode()));
            }
            args = args.trim_right_matches(",").to_string();
            args.push_str("]");
            res.push(args);
        }
        let opts = self.clone().take_optargs().into_vec();
        if !opts.is_empty() {
            res.push(format!("{}", opts.encode()));
        }
        let mut res = res.join(",");
        if !self.is_datum() {
            res.push_str("]");
        }
        res
    }
}

impl From<BTreeMap<String, Term>> for Term {
    fn from(t: BTreeMap<String, Term>) -> Term {
        let mut term = Term::new();
        let mut args = Vec::new();
        for (name, arg) in t.into_iter() {
            let mut obj = TA::new();
            obj.set_key(name.into());
            obj.set_val(arg);
            args.push(obj);
        }
        let obj = RepeatedField::from_vec(args);
        term.set_optargs(obj);
        term
    }
}

impl From<Vec<Term>> for Term {
    fn from(t: Vec<Term>) -> Term {
        let mut term = Term::new();
        let arr = RepeatedField::from_vec(t);
        term.set_args(arr);
        term
    }
}
<|endoftext|>