<commit_before>//! Implements a database that stores results of previously performed
//! searches.
//!
//! Using such a database (called "transposition table") is a way to
//! greatly reduce the search space of a chess tree with little
//! negative impact. Chess programs, during their brute-force search,
//! encounter the same positions again and again, but from different
//! sequences of moves, which is called a transposition.
//!
//! When the search encounters a transposition, it is beneficial to
//! "remember" what was determined last time the position was
//! examined, rather than redoing the entire search again. For this
//! reason, chess programs have a transposition table, which is a
//! large hash table storing information about positions previously
//! searched, how deeply they were searched, and what we concluded
//! about them.

use std;
use std::cell::{UnsafeCell, Cell};
use std::mem::transmute;
use std::num::Wrapping;
use basetypes::Value;
use chess_move::MoveDigest;

/// `BOUND_EXACT`, `BOUND_LOWER`, `BOUND_UPPER`, or `BOUND_NONE`.
///
/// * `BOUND_EXACT` means that the evaluation is exact.
///
/// * `BOUND_LOWER` means that the real value is greater or equal to
///    the evaluation.
///
/// * `BOUND_UPPER` means that the real value is lesser or equal to
///   the evaluation.
///
/// * `BOUND_NONE` means that the real value might be lesser, equal,
///   or grater than the evaluation.
pub type BoundType = u8;

/// The evaluation is not bounded.
pub const BOUND_NONE: BoundType = 0;

/// The evaluation is an upper bound.
pub const BOUND_UPPER: BoundType = 0b10;

/// The evaluation is a lower bound.
pub const BOUND_LOWER: BoundType = 0b01;

/// The evaluation is exact.
pub const BOUND_EXACT: BoundType = BOUND_UPPER | BOUND_LOWER;

/// Stores information about a particular position.
#[derive(Copy, Clone)]
pub struct EntryData {
    move16: MoveDigest,
    value: Value,
    eval_value: Value,
    gen_bound: u8,
    depth: u8,
}

impl EntryData {
    /// Creates a new instance.
    ///
    /// * `value` -- the value assigned to the position;
    ///
    /// * `bound` -- the meaning of the assigned `value`;
    ///
    /// * `depth` -- the depth of search;
    ///
    /// * `move16` -- best or refutation move, or `0` if no move is
    /// available;
    ///
    /// * `eval_value` -- the calculated static evaluation for the
    ///   position.
    pub fn new(value: Value,
               bound: BoundType,
               depth: u8,
               move16: MoveDigest,
               eval_value: Value)
               -> EntryData {
        assert!(bound <= 0b11);
        assert!(depth < 127);
        EntryData {
            move16: move16,
            value: value,
            eval_value: eval_value,
            gen_bound: bound, // Stores the entry's generation and the bound.
            depth: depth,
        }
    }

    #[inline(always)]
    pub fn value(&self) -> Value {
        self.value
    }

    #[inline(always)]
    pub fn bound(&self) -> BoundType {
        self.gen_bound & 0b11
    }

    #[inline(always)]
    pub fn depth(&self) -> u8 {
        self.depth
    }

    #[inline(always)]
    pub fn move16(&self) -> MoveDigest {
        self.move16
    }

    #[inline(always)]
    pub fn eval_value(&self) -> Value {
        self.eval_value
    }
}

// Represents transposition table entry.
//
// It is 16 bytes, defined as below:
//
// * key        64 bit
// * move16     16 bit
// * value      16 bit
// * eval value 16 bit
// * generation  6 bit
// * bound type  2 bit
// * depth       8 bit
#[derive(Copy, Clone)]
struct Entry {
    key: u64,
    data: EntryData,
}

impl Default for Entry {
    fn default() -> Entry {
        Entry {
            key: 0,
            data: unsafe { transmute(0u64) },
        }
    }
}

impl Entry {
    // Returns the whole contained data as one `u64` value.
    //
    // This is needed for implementing the lock-less probing and
    // storing.
    #[inline(always)]
    fn data_u64(&self) -> u64 {
        unsafe { transmute(self.data) }
    }

    // Returns entry's generation.
    #[inline(always)]
    fn generation(&self) -> u8 {
        self.data.gen_bound & 0b11111100
    }

    // Updates entry's generation.
    //
    // Since the `key` is saved xored with the data, when we change
    // the data, we have to change the stored `key` as well.
    #[inline(always)]
    fn update_generation(&mut self, generation: u8) {
        assert_eq!(generation & 0b11, 0);
        let old_data_u64 = self.data_u64();
        self.data.gen_bound = generation | self.data.bound();
        self.key ^= old_data_u64 ^ self.data_u64();
    }
}

/// A large hash-table that stores results of previously performed
/// searches.
pub struct TranspositionTable {
    generation: Cell<u8>,
    cluster_count: usize,
    table: UnsafeCell<Vec<[Entry; 4]>>,
}

impl TranspositionTable {
    /// Creates a new transposition table.
    ///
    /// The newly created table has the minimum possible size. Before
    /// using the new table for anything, `resize()` should be called
    /// on it, specifying the desired size.
    pub fn new() -> TranspositionTable {
        TranspositionTable {
            generation: Cell::new(0),
            cluster_count: 1,
            table: UnsafeCell::new(vec![Default::default()]),
        }
    }

    /// Resizes the transpositon table. All entries in the table will
    /// be lost.
    ///
    /// `size_mb` is the desired new size in Mbytes. If `size_mb` is
    /// not in the form "2**n", the new size of the transposition
    /// table will be as close as possible, but less than `size_mb`.
    pub fn resize(&mut self, size_mb: usize) {
        // The cluster count should be in the form "2**n", so the best
        // we can do is to ensure that the new cluster count will be
        // as close as possible but no greater than the requested one.
        let requested_cluster_count = (size_mb * 1024 * 1024) / std::mem::size_of::<[Entry; 4]>();

        // First, make sure `requested_cluster_count` is exceeded.
        let mut new_cluster_count = 1;
        while requested_cluster_count >= new_cluster_count {
            new_cluster_count <<= 1;
        }

        // Then make one step back, being careful so that the new
        // cluster count is not zero.
        if new_cluster_count > 1 {
            new_cluster_count >>= 1;
        } else {
            new_cluster_count = 1;
        }

        // Finally, reallocate the vector of clusters.
        if new_cluster_count != self.cluster_count {
            self.cluster_count = new_cluster_count;
            self.table = UnsafeCell::new(vec![Default::default(); new_cluster_count]);
        }
    }

    /// Returns the size of the transposition table in Mbytes.
    pub fn size(&self) -> usize {
        unsafe { &*self.table.get() }.len() * std::mem::size_of::<[Entry; 4]>() / 1024 / 1024
    }

    /// Removes all entries in the table.
    pub fn clear(&self) {
        let table = unsafe { self.table.get().as_mut().unwrap() };
        for cluster in table {
            for entry in cluster.iter_mut() {
                *entry = Default::default();
            }
        }
    }

    /// Signals that a new search is about to begin.
    ///
    /// Internally, the transposition table maintains a generation
    /// counter that is used to implement an efficient replacement
    /// strategy. This method increases this counter.
    pub fn new_search(&self) {
        // The lowest 2 bits of the `generation` field are used by
        // bound type.
        self.generation.set((Wrapping(self.generation.get()) + Wrapping(0b100)).0);
        assert_eq!(self.generation.get() & 0b11, 0);
    }

    /// Probes for data by a specific key.
    #[inline]
    pub fn probe(&self, key: u64) -> Option<EntryData> {
        let cluster = unsafe { self.cluster_mut(key) };
        for entry in cluster.iter_mut() {
            if entry.key ^ entry.data_u64() == key {
                // If `key` and `data` were written simultaneously by
                // different search instances with different keys,
                // this will yield in a mismatch of the above
                // comparison (except for the rare and inherent key
                // collisions).
                entry.update_generation(self.generation.get());
                return Some(entry.data);
            }
        }
        None
    }

    /// Stores data by a specific key.
    ///
    /// After being stored, the data might be retrieved by
    /// `probe(key)`. This is not guaranteed though, because in the
    /// meantime it might have been overwritten.
    #[inline]
    pub fn store(&self, key: u64, mut data: EntryData) {
        // `store` and `probe` jointly implement a clever lock-less
        // hashing method. Rather than to store two disjoint items,
        // the key is stored xored with data, while data is stored
        // additionally as usual.

        data.gen_bound |= self.generation.get();  // Sets the generation.
        let mut cluster = unsafe { self.cluster_mut(key) };
        let mut replace_index = 0;
        let mut replace_score = 0xff;
        for (i, entry) in cluster.iter_mut().enumerate() {
            // Check if this is an empty slot, or an old entry for the
            // same key. If this this is the case we will use this
            // slot for the new entry.
            if entry.key == 0 || entry.key ^ entry.data_u64() == key {
                if data.move16 == 0 {
                    data.move16 = entry.data.move16;  // Preserve any existing move.
                }
                replace_index = i;
                break;
            }
            // If we can not find empty/old slot, the replaced entry
            // will be the entry with the lowest score.
            let entry_score = self.calc_score(entry);
            if entry_score < replace_score {
                replace_index = i;
                replace_score = entry_score;
            }
        }
        unsafe {
            cluster.get_unchecked_mut(replace_index).key = key ^ transmute::<EntryData, u64>(data);
            cluster.get_unchecked_mut(replace_index).data = data;
        }
    }

    // A helper method for `store`.
    //
    // Implements our replacement strategy. Should return higher
    // values for the entries that are move likely to save CPU work in
    // the future.
    #[inline]
    fn calc_score(&self, entry: &Entry) -> u8 {
        // Positions from the current generation are always scored
        // higher than positions from older generations.
        (if entry.generation() == self.generation.get() {
            128
        } else {
            0
        })

        // Positions with higher search depth are scored higher.
        + entry.data.depth()

        // Positions with exact evaluation are given slight advantage.
        + (if entry.data.bound() == BOUND_EXACT {
            1
        } else {
            0
        })
    }

    // A helper method for `probe` and `store`.
    #[inline]
    unsafe fn cluster_mut(&self, key: u64) -> &mut [Entry; 4] {
        let cluster_index = (key & (self.cluster_count - 1) as u64) as usize;
        self.table.get().as_mut().unwrap().get_unchecked_mut(cluster_index)
    }
}

unsafe impl Sync for TranspositionTable {}

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

    #[test]
    fn test_cluster_size() {
        assert_eq!(std::mem::size_of::<[Entry; 4]>(), 64);
        assert_eq!(std::mem::size_of::<Entry>(), 16);
    }

    #[test]
    fn test_tt_resize() {
        let mut tt = TranspositionTable::new();
        assert_eq!(unsafe { &*tt.table.get() }.capacity(), 1);
        tt.resize(1);
        assert_eq!(tt.size(), 1);
        tt.clear();
    }

    #[test]
    fn test_store_and_probe() {
        let tt = TranspositionTable::new();
        assert!(tt.probe(1).is_none());
        let data = EntryData::new(0, 0, 100, 666, 0);
        assert_eq!(data.depth(), 100);
        assert_eq!(data.move16(), 666);
        tt.store(1, data);
        assert_eq!(tt.probe(1).unwrap().depth(), 100);
        tt.store(1, EntryData::new(0, 0, 100, 666, 0));
        assert_eq!(tt.probe(1).unwrap().depth(), 100);
        assert_eq!(tt.probe(1).unwrap().move16(), 666);
        for i in 2..100 {
            tt.store(i, EntryData::new(i as i16, 0, i as u8, i as u16, i as i16));
        }
        assert_eq!(tt.probe(1).unwrap().depth(), 100);
        assert_eq!(tt.probe(99).unwrap().depth(), 99);
        assert_eq!(tt.probe(98).unwrap().depth(), 98);
        assert_eq!(tt.probe(97).unwrap().depth(), 97);
        tt.clear();
        assert!(tt.probe(1).is_none());
        tt.store(1, data);
        tt.new_search();
        tt.probe(1);
        assert!(tt.probe(1).is_some());
    }

    #[test]
    fn test_new_search() {
        let tt = TranspositionTable::new();
        assert_eq!(tt.generation.get(), 0 << 2);
        tt.new_search();
        assert_eq!(tt.generation.get(), 1 << 2);
        for _ in 0..64 {
            tt.new_search();
        }
        assert_eq!(tt.generation.get(), 1 << 2);
    }
}
<commit_msg>removed stupid inline<commit_after>//! Implements a database that stores results of previously performed
//! searches.
//!
//! Using such a database (called "transposition table") is a way to
//! greatly reduce the search space of a chess tree with little
//! negative impact. Chess programs, during their brute-force search,
//! encounter the same positions again and again, but from different
//! sequences of moves, which is called a transposition.
//!
//! When the search encounters a transposition, it is beneficial to
//! "remember" what was determined last time the position was
//! examined, rather than redoing the entire search again. For this
//! reason, chess programs have a transposition table, which is a
//! large hash table storing information about positions previously
//! searched, how deeply they were searched, and what we concluded
//! about them.

use std;
use std::cell::{UnsafeCell, Cell};
use std::mem::transmute;
use std::num::Wrapping;
use basetypes::Value;
use chess_move::MoveDigest;

/// `BOUND_EXACT`, `BOUND_LOWER`, `BOUND_UPPER`, or `BOUND_NONE`.
///
/// * `BOUND_EXACT` means that the evaluation is exact.
///
/// * `BOUND_LOWER` means that the real value is greater or equal to
///    the evaluation.
///
/// * `BOUND_UPPER` means that the real value is lesser or equal to
///   the evaluation.
///
/// * `BOUND_NONE` means that the real value might be lesser, equal,
///   or grater than the evaluation.
pub type BoundType = u8;

/// The evaluation is not bounded.
pub const BOUND_NONE: BoundType = 0;

/// The evaluation is an upper bound.
pub const BOUND_UPPER: BoundType = 0b10;

/// The evaluation is a lower bound.
pub const BOUND_LOWER: BoundType = 0b01;

/// The evaluation is exact.
pub const BOUND_EXACT: BoundType = BOUND_UPPER | BOUND_LOWER;

/// Stores information about a particular position.
#[derive(Copy, Clone)]
pub struct EntryData {
    move16: MoveDigest,
    value: Value,
    eval_value: Value,
    gen_bound: u8,
    depth: u8,
}

impl EntryData {
    /// Creates a new instance.
    ///
    /// * `value` -- the value assigned to the position;
    ///
    /// * `bound` -- the meaning of the assigned `value`;
    ///
    /// * `depth` -- the depth of search;
    ///
    /// * `move16` -- best or refutation move, or `0` if no move is
    /// available;
    ///
    /// * `eval_value` -- the calculated static evaluation for the
    ///   position.
    pub fn new(value: Value,
               bound: BoundType,
               depth: u8,
               move16: MoveDigest,
               eval_value: Value)
               -> EntryData {
        assert!(bound <= 0b11);
        assert!(depth < 127);
        EntryData {
            move16: move16,
            value: value,
            eval_value: eval_value,
            gen_bound: bound, // Stores the entry's generation and the bound.
            depth: depth,
        }
    }

    #[inline(always)]
    pub fn value(&self) -> Value {
        self.value
    }

    #[inline(always)]
    pub fn bound(&self) -> BoundType {
        self.gen_bound & 0b11
    }

    #[inline(always)]
    pub fn depth(&self) -> u8 {
        self.depth
    }

    #[inline(always)]
    pub fn move16(&self) -> MoveDigest {
        self.move16
    }

    #[inline(always)]
    pub fn eval_value(&self) -> Value {
        self.eval_value
    }
}

// Represents transposition table entry.
//
// It is 16 bytes, defined as below:
//
// * key        64 bit
// * move16     16 bit
// * value      16 bit
// * eval value 16 bit
// * generation  6 bit
// * bound type  2 bit
// * depth       8 bit
#[derive(Copy, Clone)]
struct Entry {
    key: u64,
    data: EntryData,
}

impl Default for Entry {
    fn default() -> Entry {
        Entry {
            key: 0,
            data: unsafe { transmute(0u64) },
        }
    }
}

impl Entry {
    // Returns the whole contained data as one `u64` value.
    //
    // This is needed for implementing the lock-less probing and
    // storing.
    #[inline(always)]
    fn data_u64(&self) -> u64 {
        unsafe { transmute(self.data) }
    }

    // Returns entry's generation.
    #[inline(always)]
    fn generation(&self) -> u8 {
        self.data.gen_bound & 0b11111100
    }

    // Updates entry's generation.
    //
    // Since the `key` is saved xored with the data, when we change
    // the data, we have to change the stored `key` as well.
    #[inline(always)]
    fn update_generation(&mut self, generation: u8) {
        assert_eq!(generation & 0b11, 0);
        let old_data_u64 = self.data_u64();
        self.data.gen_bound = generation | self.data.bound();
        self.key ^= old_data_u64 ^ self.data_u64();
    }
}

/// A large hash-table that stores results of previously performed
/// searches.
pub struct TranspositionTable {
    generation: Cell<u8>,
    cluster_count: usize,
    table: UnsafeCell<Vec<[Entry; 4]>>,
}

impl TranspositionTable {
    /// Creates a new transposition table.
    ///
    /// The newly created table has the minimum possible size. Before
    /// using the new table for anything, `resize()` should be called
    /// on it, specifying the desired size.
    pub fn new() -> TranspositionTable {
        TranspositionTable {
            generation: Cell::new(0),
            cluster_count: 1,
            table: UnsafeCell::new(vec![Default::default()]),
        }
    }

    /// Resizes the transpositon table. All entries in the table will
    /// be lost.
    ///
    /// `size_mb` is the desired new size in Mbytes. If `size_mb` is
    /// not in the form "2**n", the new size of the transposition
    /// table will be as close as possible, but less than `size_mb`.
    pub fn resize(&mut self, size_mb: usize) {
        // The cluster count should be in the form "2**n", so the best
        // we can do is to ensure that the new cluster count will be
        // as close as possible but no greater than the requested one.
        let requested_cluster_count = (size_mb * 1024 * 1024) / std::mem::size_of::<[Entry; 4]>();

        // First, make sure `requested_cluster_count` is exceeded.
        let mut new_cluster_count = 1;
        while requested_cluster_count >= new_cluster_count {
            new_cluster_count <<= 1;
        }

        // Then make one step back, being careful so that the new
        // cluster count is not zero.
        if new_cluster_count > 1 {
            new_cluster_count >>= 1;
        } else {
            new_cluster_count = 1;
        }

        // Finally, reallocate the vector of clusters.
        if new_cluster_count != self.cluster_count {
            self.cluster_count = new_cluster_count;
            self.table = UnsafeCell::new(vec![Default::default(); new_cluster_count]);
        }
    }

    /// Returns the size of the transposition table in Mbytes.
    pub fn size(&self) -> usize {
        unsafe { &*self.table.get() }.len() * std::mem::size_of::<[Entry; 4]>() / 1024 / 1024
    }

    /// Removes all entries in the table.
    pub fn clear(&self) {
        let table = unsafe { self.table.get().as_mut().unwrap() };
        for cluster in table {
            for entry in cluster.iter_mut() {
                *entry = Default::default();
            }
        }
    }

    /// Signals that a new search is about to begin.
    ///
    /// Internally, the transposition table maintains a generation
    /// counter that is used to implement an efficient replacement
    /// strategy. This method increases this counter.
    pub fn new_search(&self) {
        // The lowest 2 bits of the `generation` field are used by
        // bound type.
        self.generation.set((Wrapping(self.generation.get()) + Wrapping(0b100)).0);
        assert_eq!(self.generation.get() & 0b11, 0);
    }

    /// Probes for data by a specific key.
    #[inline]
    pub fn probe(&self, key: u64) -> Option<EntryData> {
        let cluster = unsafe { self.cluster_mut(key) };
        for entry in cluster.iter_mut() {
            if entry.key ^ entry.data_u64() == key {
                // If `key` and `data` were written simultaneously by
                // different search instances with different keys,
                // this will yield in a mismatch of the above
                // comparison (except for the rare and inherent key
                // collisions).
                entry.update_generation(self.generation.get());
                return Some(entry.data);
            }
        }
        None
    }

    /// Stores data by a specific key.
    ///
    /// After being stored, the data might be retrieved by
    /// `probe(key)`. This is not guaranteed though, because in the
    /// meantime it might have been overwritten.
    pub fn store(&self, key: u64, mut data: EntryData) {
        // `store` and `probe` jointly implement a clever lock-less
        // hashing method. Rather than to store two disjoint items,
        // the key is stored xored with data, while data is stored
        // additionally as usual.

        data.gen_bound |= self.generation.get();  // Sets the generation.
        let mut cluster = unsafe { self.cluster_mut(key) };
        let mut replace_index = 0;
        let mut replace_score = 0xff;
        for (i, entry) in cluster.iter_mut().enumerate() {
            // Check if this is an empty slot, or an old entry for the
            // same key. If this this is the case we will use this
            // slot for the new entry.
            if entry.key == 0 || entry.key ^ entry.data_u64() == key {
                if data.move16 == 0 {
                    data.move16 = entry.data.move16;  // Preserve any existing move.
                }
                replace_index = i;
                break;
            }
            // If we can not find empty/old slot, the replaced entry
            // will be the entry with the lowest score.
            let entry_score = self.calc_score(entry);
            if entry_score < replace_score {
                replace_index = i;
                replace_score = entry_score;
            }
        }
        unsafe {
            cluster.get_unchecked_mut(replace_index).key = key ^ transmute::<EntryData, u64>(data);
            cluster.get_unchecked_mut(replace_index).data = data;
        }
    }

    // A helper method for `store`.
    //
    // Implements our replacement strategy. Should return higher
    // values for the entries that are move likely to save CPU work in
    // the future.
    #[inline(always)]
    fn calc_score(&self, entry: &Entry) -> u8 {
        // Positions from the current generation are always scored
        // higher than positions from older generations.
        (if entry.generation() == self.generation.get() {
            128
        } else {
            0
        })

        // Positions with higher search depth are scored higher.
        + entry.data.depth()

        // Positions with exact evaluation are given slight advantage.
        + (if entry.data.bound() == BOUND_EXACT {
            1
        } else {
            0
        })
    }

    // A helper method for `probe` and `store`.
    #[inline]
    unsafe fn cluster_mut(&self, key: u64) -> &mut [Entry; 4] {
        let cluster_index = (key & (self.cluster_count - 1) as u64) as usize;
        self.table.get().as_mut().unwrap().get_unchecked_mut(cluster_index)
    }
}

unsafe impl Sync for TranspositionTable {}

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

    #[test]
    fn test_cluster_size() {
        assert_eq!(std::mem::size_of::<[Entry; 4]>(), 64);
        assert_eq!(std::mem::size_of::<Entry>(), 16);
    }

    #[test]
    fn test_tt_resize() {
        let mut tt = TranspositionTable::new();
        assert_eq!(unsafe { &*tt.table.get() }.capacity(), 1);
        tt.resize(1);
        assert_eq!(tt.size(), 1);
        tt.clear();
    }

    #[test]
    fn test_store_and_probe() {
        let tt = TranspositionTable::new();
        assert!(tt.probe(1).is_none());
        let data = EntryData::new(0, 0, 100, 666, 0);
        assert_eq!(data.depth(), 100);
        assert_eq!(data.move16(), 666);
        tt.store(1, data);
        assert_eq!(tt.probe(1).unwrap().depth(), 100);
        tt.store(1, EntryData::new(0, 0, 100, 666, 0));
        assert_eq!(tt.probe(1).unwrap().depth(), 100);
        assert_eq!(tt.probe(1).unwrap().move16(), 666);
        for i in 2..100 {
            tt.store(i, EntryData::new(i as i16, 0, i as u8, i as u16, i as i16));
        }
        assert_eq!(tt.probe(1).unwrap().depth(), 100);
        assert_eq!(tt.probe(99).unwrap().depth(), 99);
        assert_eq!(tt.probe(98).unwrap().depth(), 98);
        assert_eq!(tt.probe(97).unwrap().depth(), 97);
        tt.clear();
        assert!(tt.probe(1).is_none());
        tt.store(1, data);
        tt.new_search();
        tt.probe(1);
        assert!(tt.probe(1).is_some());
    }

    #[test]
    fn test_new_search() {
        let tt = TranspositionTable::new();
        assert_eq!(tt.generation.get(), 0 << 2);
        tt.new_search();
        assert_eq!(tt.generation.get(), 1 << 2);
        for _ in 0..64 {
            tt.new_search();
        }
        assert_eq!(tt.generation.get(), 1 << 2);
    }
}
<|endoftext|>


<commit_before>use getopts::{optflag,optopt,getopts,usage};
use std::os;
use std::collections::HashMap;
use std::io;
use std::io::fs::PathExtensions;

use item::parse_item;

fn get_env() -> HashMap<String, String> {
    let mut env = HashMap::new();
    env.extend(os::env().into_iter().filter(|&(ref key, ref value)| {
        key.as_slice().starts_with("MATES_") && value.len() > 0
    }));
    env
}

fn from_env<'a>(env: &'a HashMap<String, String>, key: &str) -> Option<&'a String> {
    env.find_equiv(&key)
}

fn expect_env<'a>(env: &'a HashMap<String, String>, key: &str) -> &'a String {
    from_env(env, key).expect(
        format!("The {} environment variable must be set.", key).as_slice()
    )
}

fn build_index(outfile: &Path, dir: &Path) {
    if !dir.is_dir() {
        fail!("MATES_DIR must be a directory.");
    };

    let mut outf = io::File::create(outfile);
    let entries = match io::fs::readdir(dir) {
        Ok(x) => x,
        Err(f) => { fail!(f.desc) }
    };
    for entry in entries.iter() {
        if !entry.is_file() {
            continue;
        }
        let itemstr = match io::File::open(entry).read_to_string() {
            Ok(x) => x,
            Err(f) => { fail!(format!("Failed to open {}: {}", entry.display(), f.desc)) }
        };
        let item = parse_item(&itemstr);
        let name = item.single_value(&"FN".into_string());
        match item.all_values(&"EMAIL".into_string()) {
            Some(emails) => {
                for email in emails.iter() {
                    match outf.write_str(
                        format!("{}\t{}\n", email.get_raw_value(), name).as_slice()
                    ) {
                        Ok(x) => (),
                        Err(f) => fail!(f.desc)
                    };
                };
            },
            None => ()
        };
    };
}

pub fn cli_main() {
    let args = os::args();

    let program = args[0].as_slice();
    let opts = [
        optflag("i", "index", "Create index."),
        optflag("h", "help", "Print help."),
        optopt("m", "mutt-search", "Search in index, for mutt search.", "")
    ];

    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(format!("Failed to parse arguments: {}", f.to_err_msg())) }
    };

    let env = get_env();

    let print_usage = || {
        println!("{}", usage(program, opts));
        println!("Environment variables:");
        println!("- MATES_INDEX: Path to index file, which is basically a cache of all");
        println!("               contacts.");
        println!("- MATES_DIR:   The vdir to use.");
        println!("- MATES_GREP:  The grep executable to use.");
    };

    if matches.opt_present("h") {
        print_usage();
    } else if matches.opt_present("index") {
        let index_file = expect_env(&env, "MATES_INDEX");
        let mates_dir = expect_env(&env, "MATES_DIR");
        println!("Rebuilding index file \"{}\"...", index_file);
        build_index(
            &Path::new(index_file.as_slice()),
            &Path::new(mates_dir.as_slice())
        );
    } else if matches.opt_present("mutt-search") {
        let index_file = expect_env(&env, "MATES_INDEX");
        let default_grep = "grep".into_string();
        let grep_cmd = match from_env(&env, "MATES_GREP") {
            Some(x) => x,
            None => &default_grep
        };

        // FIXME: Better way to write this? We already checked for presence of mutt-search before
        let query = matches.opt_str("mutt-search").expect("This should never happen and yet it did.");
        match match io::Command::new(grep_cmd.as_slice())
            .arg(query.as_slice())
            .arg(index_file.as_slice())
            .stdout(io::process::InheritFd(1))
            .stderr(io::process::InheritFd(2))
            .spawn() {
                Ok(child) => child,
                Err(e) => fail!("Failed to execute grep command: {}", e),
            }.wait() {
                Ok(code) => {
                    os::set_exit_status(match code {
                        io::process::ExitStatus(x) => x,
                        io::process::ExitSignal(x) => x,
                    });
                },
                Err(e) => fail!("Failed to execute grep command: {}", e),
            };
    } else {
        print_usage();
    };
}
<commit_msg>More refactoring<commit_after>use getopts::{optflag,optopt,getopts,usage};
use std::os;
use std::collections::HashMap;
use std::io;
use std::io::fs::PathExtensions;

use item::parse_item;

macro_rules! main_try {
    ($result: expr, $errmsg: expr) => (
        match $result {
            Ok(m) => m,
            Err(e) => {
                println!($errmsg, e);
                os::set_exit_status(1);
                return;
            }
        }
    )
}

fn get_env() -> HashMap<String, String> {
    let mut env = HashMap::new();
    env.extend(os::env().into_iter().filter(|&(ref key, ref value)| {
        key.as_slice().starts_with("MATES_") && value.len() > 0
    }));
    env
}

fn from_env<'a>(env: &'a HashMap<String, String>, key: &str) -> Option<&'a String> {
    env.find_equiv(&key)
}

fn expect_env<'a>(env: &'a HashMap<String, String>, key: &str) -> &'a String {
    from_env(env, key).expect(
        format!("The {} environment variable must be set.", key).as_slice()
    )
}

fn build_index(outfile: &Path, dir: &Path) -> io::IoResult<()> {
    if !dir.is_dir() {
        return Err(io::IoError {
            kind: io::MismatchedFileTypeForOperation,
            desc: "MATES_DIR must be a directory.",
            detail: None
        });
    };

    let mut outf = io::File::create(outfile);
    let entries = try!(io::fs::readdir(dir));
    for entry in entries.iter() {
        if !entry.is_file() {
            continue;
        }
        let itemstr = try!(io::File::open(entry).read_to_string());
        let item = parse_item(&itemstr);
        let name = item.single_value(&"FN".into_string());
        match item.all_values(&"EMAIL".into_string()) {
            Some(emails) => {
                for email in emails.iter() {
                    try!(outf.write_str(
                        format!("{}\t{}\n", email.get_raw_value(), name).as_slice()
                    ))
                };
            },
            None => ()
        };
    };
    return Ok(());
}

pub fn cli_main() {
    let args = os::args();

    let program = args[0].as_slice();
    let opts = [
        optflag("i", "index", "Create index."),
        optflag("h", "help", "Print help."),
        optopt("m", "mutt-search", "Search in index, for mutt search.", "")
    ];

    let matches = main_try!(getopts(args.tail(), opts), "Failed to parse arguments: {}");

    let env = get_env();

    let print_usage = || {
        println!("{}", usage(program, opts));
        println!("Environment variables:");
        println!("- MATES_INDEX: Path to index file, which is basically a cache of all");
        println!("               contacts.");
        println!("- MATES_DIR:   The vdir to use.");
        println!("- MATES_GREP:  The grep executable to use.");
    };

    if matches.opt_present("h") {
        print_usage();

    } else if matches.opt_present("index") {
        let index_file = expect_env(&env, "MATES_INDEX");
        let mates_dir = expect_env(&env, "MATES_DIR");
        println!("Rebuilding index file \"{}\"...", index_file);
        main_try!(build_index(
            &Path::new(index_file.as_slice()),
            &Path::new(mates_dir.as_slice())
        ), "Failed to build index: {}");

    } else if matches.opt_present("mutt-search") {
        let index_file = expect_env(&env, "MATES_INDEX");
        let default_grep = "grep".into_string();
        let grep_cmd = match from_env(&env, "MATES_GREP") {
            Some(x) => x,
            None => &default_grep
        };

        // FIXME: Better way to write this? We already checked for presence of mutt-search before
        let query = matches.opt_str("mutt-search").expect("This should never happen and yet it did.");
        let mut cmd = io::Command::new(grep_cmd.as_slice());
        cmd.arg(query.as_slice());
        cmd.arg(index_file.as_slice());
        cmd.stdout(io::process::InheritFd(1));
        cmd.stderr(io::process::InheritFd(2));

        let mut process = main_try!(cmd.spawn(), "Failed to execute grep command: {}");
        let code = main_try!(process.wait(), "Failed to execute grep command: {}");
        os::set_exit_status(match code {
            io::process::ExitStatus(x) => x,
            io::process::ExitSignal(x) => x,
        });

    } else {
        print_usage();
    };
}
<|endoftext|>


<commit_before>use serde::{Deserialize, Serialize};

use crate::components::*;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ServerMessage {
    updates: Vec<ServerUpdate>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
enum ServerUpdate {
    NewEntities(NewEntitiesUpdate),
    ChangedComponent(ChangedComponentUpdate),
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct NewEntitiesUpdate(Vec<(GameEntityId, Vec<Component>)>);

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct ChangedComponentUpdate(Vec<(GameEntityId, Component)>);
<commit_msg>Document messages<commit_after>use std::time::Duration;

use serde::{Deserialize, Serialize};

use crate::components::*;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ServerMessage {
    /// Server time since session start.
    session_time: Duration,
    /// List of entity updates.
    updates: Vec<ServerUpdate>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
enum ServerUpdate {
    NewEntities(NewEntitiesUpdate),
    ChangedComponent(ChangedComponentUpdate),
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct NewEntitiesUpdate(Vec<(GameEntityId, Vec<Component>)>);

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct ChangedComponentUpdate(Vec<(GameEntityId, Component)>);
<|endoftext|>


<commit_before>extern crate symbolserver;

use std::env;
use std::fs;
use std::path::Path;

use symbolserver::sdk::SdkProcessor;
use symbolserver::memdb::MemDbBuilder;
use symbolserver::Result;

fn do_main() -> Result<()> {
    let args : Vec<_> = env::args().collect();
    let p = Path::new(&args[1]);
    let out = fs::File::create("/tmp/test.memdb")?;
    let sdk_proc = SdkProcessor::new(p)?;
    let mut builder = MemDbBuilder::new(out, sdk_proc.info())?;

    for obj_res in sdk_proc.objects()? {
        let (filename, obj) = obj_res?;
        builder.write_object(&obj, Some(&filename))?;
    }
    builder.flush()?;

    Ok(())
}

fn main() {
    do_main().unwrap();
}
<commit_msg>Updated example<commit_after>extern crate uuid;
extern crate symbolserver;

use std::env;
use std::fs;
use std::path::Path;

use uuid::Uuid;

use symbolserver::sdk::SdkProcessor;
use symbolserver::memdb::{MemDbBuilder, MemDb};
use symbolserver::Result;

fn do_main() -> Result<()> {
    let args : Vec<_> = env::args().collect();
    if args.len() > 1 {
        let p = Path::new(&args[1]);
        let out = fs::File::create("/tmp/test.memdb")?;
        let sdk_proc = SdkProcessor::new(p)?;
        let mut builder = MemDbBuilder::new(out, sdk_proc.info())?;

        for obj_res in sdk_proc.objects()? {
            let (filename, obj) = obj_res?;
            builder.write_object(&obj, Some(&filename))?;
        }
        builder.flush()?;
    }

    let mdb = MemDb::from_path("/tmp/test.memdb")?;
    let sym = mdb.lookup_by_uuid(&"63d32ddb-095d-3974-afc9-8a6cf7c8bbd6".parse::<Uuid>().unwrap(), 6815851772);
    println!("{:?}", sym);

    Ok(())
}

fn main() {
    do_main().unwrap();
}
<|endoftext|>


<commit_before>use crate::hasher::{Digest, Size};
use crate::Error;
#[cfg(all(not(feature = "std"), feature = "scale-codec"))]
use alloc::vec;
use core::convert::TryFrom;
#[cfg(feature = "std")]
use core::convert::TryInto;
use core::fmt::Debug;
use generic_array::{ArrayLength, GenericArray};

/// Trait that implements hashing.
///
/// It is usually implemented by a custom code table enum that derives the [`Multihash` derive].
///
/// [`Multihash` derive]: crate::derive
pub trait MultihashDigest:
    TryFrom<u64> + Into<u64> + Send + Sync + Unpin + Copy + Eq + Debug + 'static
{
    /// The maximum size a hash will allocate.
    type AllocSize: Size;

    /// Calculate the hash of some input data.
    ///
    /// # Example
    ///
    /// ```
    /// // `Code` implements `MultihashDigest`
    /// use multihash::{Code, MultihashDigest};
    ///
    /// let hash = Code::Sha3_256.digest(b"Hello world!");
    /// println!("{:02x?}", hash);
    /// ```
    fn digest(&self, input: &[u8]) -> Multihash<Self::AllocSize>;

    /// Create a multihash from an existing [`Digest`].
    ///
    /// # Example
    ///
    /// ```
    /// use multihash::{Code, MultihashDigest, Sha3_256, StatefulHasher};
    ///
    /// let mut hasher = Sha3_256::default();
    /// hasher.update(b"Hello world!");
    /// let hash = Code::multihash_from_digest(&hasher.finalize());
    /// println!("{:02x?}", hash);
    /// ```
    #[allow(clippy::needless_lifetimes)]
    fn multihash_from_digest<'a, S, D>(digest: &'a D) -> Multihash<Self::AllocSize>
    where
        S: Size,
        D: Digest<S>,
        Self: From<&'a D>;
}

/// A Multihash instance that only supports the basic functionality and no hashing.
///
/// With this Multihash implementation you can operate on Multihashes in a generic way, but
/// no hasher implementation is associated with the code.
///
/// # Example
///
/// ```
/// use multihash::Multihash;
///
/// const Sha3_256: u64 = 0x16;
/// let digest_bytes = [
///     0x16, 0x20, 0x64, 0x4b, 0xcc, 0x7e, 0x56, 0x43, 0x73, 0x04, 0x09, 0x99, 0xaa, 0xc8, 0x9e,
///     0x76, 0x22, 0xf3, 0xca, 0x71, 0xfb, 0xa1, 0xd9, 0x72, 0xfd, 0x94, 0xa3, 0x1c, 0x3b, 0xfb,
///     0xf2, 0x4e, 0x39, 0x38,
/// ];
/// let mh = Multihash::from_bytes(&digest_bytes).unwrap();
/// assert_eq!(mh.code(), Sha3_256);
/// assert_eq!(mh.size(), 32);
/// assert_eq!(mh.digest(), &digest_bytes[2..]);
/// ```
#[cfg_attr(feature = "serde-codec", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde-codec", derive(serde::Serialize))]
#[cfg_attr(feature = "serde-codec", serde(bound = "S: Size"))]
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct Multihash<S: Size> {
    /// The code of the Multihash.
    code: u64,
    /// The actual size of the digest in bytes (not the allocated size).
    size: u8,
    /// The digest.
    digest: GenericArray<u8, S>,
}

impl<S: Size> Copy for Multihash<S> where <S as ArrayLength<u8>>::ArrayType: Copy {}

impl<S: Size> Multihash<S> {
    /// Wraps the digest in a multihash.
    pub fn wrap(code: u64, input_digest: &[u8]) -> Result<Self, Error> {
        if input_digest.len() > S::to_usize() {
            return Err(Error::InvalidSize(input_digest.len() as _));
        }
        let size = input_digest.len();
        let mut digest = GenericArray::default();
        digest[..size].copy_from_slice(input_digest);
        Ok(Self {
            code,
            size: size as u8,
            digest,
        })
    }

    /// Returns the code of the multihash.
    pub fn code(&self) -> u64 {
        self.code
    }

    /// Returns the size of the digest.
    pub fn size(&self) -> u8 {
        self.size
    }

    /// Returns the digest.
    pub fn digest(&self) -> &[u8] {
        &self.digest[..self.size as usize]
    }

    /// Reads a multihash from a byte stream.
    #[cfg(feature = "std")]
    pub fn read<R: std::io::Read>(r: R) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let (code, size, digest) = read_multihash(r)?;
        Ok(Self { code, size, digest })
    }

    /// Parses a multihash from a bytes.
    ///
    /// You need to make sure the passed in bytes have the correct length. The digest length
    /// needs to match the `size` value of the multihash.
    #[cfg(feature = "std")]
    pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let result = Self::read(&mut bytes)?;
        // There were more bytes supplied than read
        if !bytes.is_empty() {
            return Err(Error::InvalidSize(bytes.len().try_into().expect(
                "Currently the maximum size is 255, therefore always fits into usize",
            )));
        }

        Ok(result)
    }

    /// Writes a multihash to a byte stream.
    #[cfg(feature = "std")]
    pub fn write<W: std::io::Write>(&self, w: W) -> Result<(), Error> {
        write_multihash(w, self.code(), self.size(), self.digest())
    }

    /// Returns the bytes of a multihash.
    #[cfg(feature = "std")]
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(self.size().into());
        self.write(&mut bytes)
            .expect("writing to a vec should never fail");
        bytes
    }
}

// Don't hash the whole allocated space, but just the actual digest
#[allow(clippy::derive_hash_xor_eq)]
impl<S: Size> core::hash::Hash for Multihash<S> {
    fn hash<T: core::hash::Hasher>(&self, state: &mut T) {
        self.code.hash(state);
        self.digest().hash(state);
    }
}

#[cfg(feature = "std")]
impl<S: Size> From<Multihash<S>> for Vec<u8> {
    fn from(multihash: Multihash<S>) -> Self {
        multihash.to_bytes()
    }
}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Encode for Multihash<crate::U32> {
    fn encode_to<EncOut: parity_scale_codec::Output + ?Sized>(&self, dest: &mut EncOut) {
        let mut digest = [0; 32];
        digest.copy_from_slice(&self.digest);
        self.code.encode_to(dest);
        self.size.encode_to(dest);
        // **NOTE** We write the digest directly to dest, since we have known the size of digest.
        //
        // We do not choose to encode &[u8] directly, because it will add extra bytes (the compact length of digest).
        // For a valid multihash, the length of digest must equal to `size`.
        // Therefore, we can only read raw bytes whose length is equal to `size` when decoding.
        dest.write(&digest[..self.size as usize]);
    }
}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::EncodeLike for Multihash<crate::U32> {}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Decode for Multihash<crate::U32> {
    fn decode<DecIn: parity_scale_codec::Input>(
        input: &mut DecIn,
    ) -> Result<Self, parity_scale_codec::Error> {
        let code = parity_scale_codec::Decode::decode(input)?;
        let size = parity_scale_codec::Decode::decode(input)?;
        // For a valid multihash, the length of digest must equal to the size.
        let mut buf = vec![0u8; size as usize];
        input.read(&mut buf)?;
        Ok(Multihash {
            code,
            size,
            digest: {
                let mut digest = [0u8; 32];
                digest[..size as usize].copy_from_slice(&buf);
                GenericArray::clone_from_slice(&digest)
            },
        })
    }
}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Encode for Multihash<crate::U64> {
    fn encode_to<EncOut: parity_scale_codec::Output + ?Sized>(&self, dest: &mut EncOut) {
        let mut digest = [0; 64];
        digest.copy_from_slice(&self.digest);
        self.code.encode_to(dest);
        self.size.encode_to(dest);
        // **NOTE** We write the digest directly to dest, since we have known the size of digest.
        //
        // We do not choose to encode &[u8] directly, because it will add extra bytes (the compact length of digest).
        // For a valid multihash, the length of digest must equal to `size`.
        // Therefore, we can only read raw bytes whose length is equal to `size` when decoding.
        dest.write(&digest[..self.size as usize]);
    }
}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::EncodeLike for Multihash<crate::U64> {}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Decode for Multihash<crate::U64> {
    fn decode<DecIn: parity_scale_codec::Input>(
        input: &mut DecIn,
    ) -> Result<Self, parity_scale_codec::Error> {
        let code = parity_scale_codec::Decode::decode(input)?;
        let size = parity_scale_codec::Decode::decode(input)?;
        // For a valid multihash, the length of digest must equal to the size.
        let mut buf = vec![0u8; size as usize];
        input.read(&mut buf)?;
        Ok(Multihash {
            code,
            size,
            digest: {
                let mut digest = [0u8; 64];
                digest[..size as usize].copy_from_slice(&buf);
                GenericArray::clone_from_slice(&digest)
            },
        })
    }
}

/// Writes the multihash to a byte stream.
#[cfg(feature = "std")]
pub fn write_multihash<W>(mut w: W, code: u64, size: u8, digest: &[u8]) -> Result<(), Error>
where
    W: std::io::Write,
{
    use unsigned_varint::encode as varint_encode;

    let mut code_buf = varint_encode::u64_buffer();
    let code = varint_encode::u64(code, &mut code_buf);

    let mut size_buf = varint_encode::u8_buffer();
    let size = varint_encode::u8(size, &mut size_buf);

    w.write_all(code)?;
    w.write_all(size)?;
    w.write_all(digest)?;
    Ok(())
}

/// Reads a multihash from a byte stream that contains a full multihash (code, size and the digest)
///
/// Returns the code, size and the digest. The size is the actual size and not the
/// maximum/allocated size of the digest.
///
/// Currently the maximum size for a digest is 255 bytes.
#[cfg(feature = "std")]
pub fn read_multihash<R, S>(mut r: R) -> Result<(u64, u8, GenericArray<u8, S>), Error>
where
    R: std::io::Read,
    S: Size,
{
    use unsigned_varint::io::read_u64;

    let code = read_u64(&mut r)?;
    let size = read_u64(&mut r)?;

    if size > S::to_u64() || size > u8::MAX as u64 {
        return Err(Error::InvalidSize(size));
    }

    let mut digest = GenericArray::default();
    r.read_exact(&mut digest[..size as usize])?;
    Ok((code, size as u8, digest))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::multihash_impl::Code;

    #[test]
    fn roundtrip() {
        let hash = Code::Sha2_256.digest(b"hello world");
        let mut buf = [0u8; 35];
        hash.write(&mut buf[..]).unwrap();
        let hash2 = Multihash::read(&buf[..]).unwrap();
        assert_eq!(hash, hash2);
    }

    #[test]
    #[cfg(feature = "scale-codec")]
    fn test_scale() {
        use crate::{Hasher, Sha2_256};
        use parity_scale_codec::{Decode, Encode};

        let mh1 = Multihash::<crate::U32>::wrap(
            Code::Sha2_256.into(),
            Sha2_256::digest(b"hello world").as_ref(),
        )
        .unwrap();
        // println!("mh1: code = {}, size = {}, digest = {:?}", mh1.code(), mh1.size(), mh1.digest());
        let mh1_bytes = mh1.encode();
        // println!("Multihash<32>: {}", hex::encode(&mh1_bytes));
        let mh2: Multihash<crate::U32> = Decode::decode(&mut &mh1_bytes[..]).unwrap();
        assert_eq!(mh1, mh2);

        let mh3: Multihash<crate::U64> = Code::Sha2_256.digest(b"hello world");
        // println!("mh3: code = {}, size = {}, digest = {:?}", mh3.code(), mh3.size(), mh3.digest());
        let mh3_bytes = mh3.encode();
        // println!("Multihash<64>: {}", hex::encode(&mh3_bytes));
        let mh4: Multihash<crate::U64> = Decode::decode(&mut &mh3_bytes[..]).unwrap();
        assert_eq!(mh3, mh4);

        assert_eq!(mh1_bytes, mh3_bytes);
    }

    #[test]
    #[cfg(feature = "serde-codec")]
    fn test_serde() {
        let mh = Multihash::<crate::U32>::default();
        let bytes = serde_json::to_string(&mh).unwrap();
        let mh2 = serde_json::from_str(&bytes).unwrap();
        assert_eq!(mh, mh2);
    }
}
<commit_msg>fix: avoid allocating when decoding<commit_after>use crate::hasher::{Digest, Size};
use crate::Error;
use core::convert::TryFrom;
#[cfg(feature = "std")]
use core::convert::TryInto;
use core::fmt::Debug;
use generic_array::{ArrayLength, GenericArray};

/// Trait that implements hashing.
///
/// It is usually implemented by a custom code table enum that derives the [`Multihash` derive].
///
/// [`Multihash` derive]: crate::derive
pub trait MultihashDigest:
    TryFrom<u64> + Into<u64> + Send + Sync + Unpin + Copy + Eq + Debug + 'static
{
    /// The maximum size a hash will allocate.
    type AllocSize: Size;

    /// Calculate the hash of some input data.
    ///
    /// # Example
    ///
    /// ```
    /// // `Code` implements `MultihashDigest`
    /// use multihash::{Code, MultihashDigest};
    ///
    /// let hash = Code::Sha3_256.digest(b"Hello world!");
    /// println!("{:02x?}", hash);
    /// ```
    fn digest(&self, input: &[u8]) -> Multihash<Self::AllocSize>;

    /// Create a multihash from an existing [`Digest`].
    ///
    /// # Example
    ///
    /// ```
    /// use multihash::{Code, MultihashDigest, Sha3_256, StatefulHasher};
    ///
    /// let mut hasher = Sha3_256::default();
    /// hasher.update(b"Hello world!");
    /// let hash = Code::multihash_from_digest(&hasher.finalize());
    /// println!("{:02x?}", hash);
    /// ```
    #[allow(clippy::needless_lifetimes)]
    fn multihash_from_digest<'a, S, D>(digest: &'a D) -> Multihash<Self::AllocSize>
    where
        S: Size,
        D: Digest<S>,
        Self: From<&'a D>;
}

/// A Multihash instance that only supports the basic functionality and no hashing.
///
/// With this Multihash implementation you can operate on Multihashes in a generic way, but
/// no hasher implementation is associated with the code.
///
/// # Example
///
/// ```
/// use multihash::Multihash;
///
/// const Sha3_256: u64 = 0x16;
/// let digest_bytes = [
///     0x16, 0x20, 0x64, 0x4b, 0xcc, 0x7e, 0x56, 0x43, 0x73, 0x04, 0x09, 0x99, 0xaa, 0xc8, 0x9e,
///     0x76, 0x22, 0xf3, 0xca, 0x71, 0xfb, 0xa1, 0xd9, 0x72, 0xfd, 0x94, 0xa3, 0x1c, 0x3b, 0xfb,
///     0xf2, 0x4e, 0x39, 0x38,
/// ];
/// let mh = Multihash::from_bytes(&digest_bytes).unwrap();
/// assert_eq!(mh.code(), Sha3_256);
/// assert_eq!(mh.size(), 32);
/// assert_eq!(mh.digest(), &digest_bytes[2..]);
/// ```
#[cfg_attr(feature = "serde-codec", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde-codec", derive(serde::Serialize))]
#[cfg_attr(feature = "serde-codec", serde(bound = "S: Size"))]
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct Multihash<S: Size> {
    /// The code of the Multihash.
    code: u64,
    /// The actual size of the digest in bytes (not the allocated size).
    size: u8,
    /// The digest.
    digest: GenericArray<u8, S>,
}

impl<S: Size> Copy for Multihash<S> where <S as ArrayLength<u8>>::ArrayType: Copy {}

impl<S: Size> Multihash<S> {
    /// Wraps the digest in a multihash.
    pub fn wrap(code: u64, input_digest: &[u8]) -> Result<Self, Error> {
        if input_digest.len() > S::to_usize() {
            return Err(Error::InvalidSize(input_digest.len() as _));
        }
        let size = input_digest.len();
        let mut digest = GenericArray::default();
        digest[..size].copy_from_slice(input_digest);
        Ok(Self {
            code,
            size: size as u8,
            digest,
        })
    }

    /// Returns the code of the multihash.
    pub fn code(&self) -> u64 {
        self.code
    }

    /// Returns the size of the digest.
    pub fn size(&self) -> u8 {
        self.size
    }

    /// Returns the digest.
    pub fn digest(&self) -> &[u8] {
        &self.digest[..self.size as usize]
    }

    /// Reads a multihash from a byte stream.
    #[cfg(feature = "std")]
    pub fn read<R: std::io::Read>(r: R) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let (code, size, digest) = read_multihash(r)?;
        Ok(Self { code, size, digest })
    }

    /// Parses a multihash from a bytes.
    ///
    /// You need to make sure the passed in bytes have the correct length. The digest length
    /// needs to match the `size` value of the multihash.
    #[cfg(feature = "std")]
    pub fn from_bytes(mut bytes: &[u8]) -> Result<Self, Error>
    where
        Self: Sized,
    {
        let result = Self::read(&mut bytes)?;
        // There were more bytes supplied than read
        if !bytes.is_empty() {
            return Err(Error::InvalidSize(bytes.len().try_into().expect(
                "Currently the maximum size is 255, therefore always fits into usize",
            )));
        }

        Ok(result)
    }

    /// Writes a multihash to a byte stream.
    #[cfg(feature = "std")]
    pub fn write<W: std::io::Write>(&self, w: W) -> Result<(), Error> {
        write_multihash(w, self.code(), self.size(), self.digest())
    }

    /// Returns the bytes of a multihash.
    #[cfg(feature = "std")]
    pub fn to_bytes(&self) -> Vec<u8> {
        let mut bytes = Vec::with_capacity(self.size().into());
        self.write(&mut bytes)
            .expect("writing to a vec should never fail");
        bytes
    }
}

// Don't hash the whole allocated space, but just the actual digest
#[allow(clippy::derive_hash_xor_eq)]
impl<S: Size> core::hash::Hash for Multihash<S> {
    fn hash<T: core::hash::Hasher>(&self, state: &mut T) {
        self.code.hash(state);
        self.digest().hash(state);
    }
}

#[cfg(feature = "std")]
impl<S: Size> From<Multihash<S>> for Vec<u8> {
    fn from(multihash: Multihash<S>) -> Self {
        multihash.to_bytes()
    }
}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Encode for Multihash<crate::U32> {
    fn encode_to<EncOut: parity_scale_codec::Output + ?Sized>(&self, dest: &mut EncOut) {
        let mut digest = [0; 32];
        digest.copy_from_slice(&self.digest);
        self.code.encode_to(dest);
        self.size.encode_to(dest);
        // **NOTE** We write the digest directly to dest, since we have known the size of digest.
        //
        // We do not choose to encode &[u8] directly, because it will add extra bytes (the compact length of digest).
        // For a valid multihash, the length of digest must equal to `size`.
        // Therefore, we can only read raw bytes whose length is equal to `size` when decoding.
        dest.write(&digest[..self.size as usize]);
    }
}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::EncodeLike for Multihash<crate::U32> {}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Decode for Multihash<crate::U32> {
    fn decode<DecIn: parity_scale_codec::Input>(
        input: &mut DecIn,
    ) -> Result<Self, parity_scale_codec::Error> {
        let code = parity_scale_codec::Decode::decode(input)?;
        let size = parity_scale_codec::Decode::decode(input)?;
        if size > 32 {
            return Err(parity_scale_codec::Error::from("invalid size"));
        }
        // For a valid multihash, the length of digest must equal to the size.
        let mut digest = GenericArray::default();
        input.read(&mut digest[..size as usize])?;
        Ok(Multihash { code, size, digest })
    }
}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Encode for Multihash<crate::U64> {
    fn encode_to<EncOut: parity_scale_codec::Output + ?Sized>(&self, dest: &mut EncOut) {
        let mut digest = [0; 64];
        digest.copy_from_slice(&self.digest);
        self.code.encode_to(dest);
        self.size.encode_to(dest);
        // **NOTE** We write the digest directly to dest, since we have known the size of digest.
        //
        // We do not choose to encode &[u8] directly, because it will add extra bytes (the compact length of digest).
        // For a valid multihash, the length of digest must equal to `size`.
        // Therefore, we can only read raw bytes whose length is equal to `size` when decoding.
        dest.write(&digest[..self.size as usize]);
    }
}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::EncodeLike for Multihash<crate::U64> {}

#[cfg(feature = "scale-codec")]
impl parity_scale_codec::Decode for Multihash<crate::U64> {
    fn decode<DecIn: parity_scale_codec::Input>(
        input: &mut DecIn,
    ) -> Result<Self, parity_scale_codec::Error> {
        let code = parity_scale_codec::Decode::decode(input)?;
        let size = parity_scale_codec::Decode::decode(input)?;
        if size > 64 {
            return Err(parity_scale_codec::Error::from("invalid size"));
        }
        // For a valid multihash, the length of digest must equal to the size.
        let mut digest = GenericArray::default();
        input.read(&mut digest[..size as usize])?;
        Ok(Multihash { code, size, digest })
    }
}

/// Writes the multihash to a byte stream.
#[cfg(feature = "std")]
pub fn write_multihash<W>(mut w: W, code: u64, size: u8, digest: &[u8]) -> Result<(), Error>
where
    W: std::io::Write,
{
    use unsigned_varint::encode as varint_encode;

    let mut code_buf = varint_encode::u64_buffer();
    let code = varint_encode::u64(code, &mut code_buf);

    let mut size_buf = varint_encode::u8_buffer();
    let size = varint_encode::u8(size, &mut size_buf);

    w.write_all(code)?;
    w.write_all(size)?;
    w.write_all(digest)?;
    Ok(())
}

/// Reads a multihash from a byte stream that contains a full multihash (code, size and the digest)
///
/// Returns the code, size and the digest. The size is the actual size and not the
/// maximum/allocated size of the digest.
///
/// Currently the maximum size for a digest is 255 bytes.
#[cfg(feature = "std")]
pub fn read_multihash<R, S>(mut r: R) -> Result<(u64, u8, GenericArray<u8, S>), Error>
where
    R: std::io::Read,
    S: Size,
{
    use unsigned_varint::io::read_u64;

    let code = read_u64(&mut r)?;
    let size = read_u64(&mut r)?;

    if size > S::to_u64() || size > u8::MAX as u64 {
        return Err(Error::InvalidSize(size));
    }

    let mut digest = GenericArray::default();
    r.read_exact(&mut digest[..size as usize])?;
    Ok((code, size as u8, digest))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::multihash_impl::Code;

    #[test]
    fn roundtrip() {
        let hash = Code::Sha2_256.digest(b"hello world");
        let mut buf = [0u8; 35];
        hash.write(&mut buf[..]).unwrap();
        let hash2 = Multihash::read(&buf[..]).unwrap();
        assert_eq!(hash, hash2);
    }

    #[test]
    #[cfg(feature = "scale-codec")]
    fn test_scale() {
        use crate::{Hasher, Sha2_256};
        use parity_scale_codec::{Decode, Encode};

        let mh1 = Multihash::<crate::U32>::wrap(
            Code::Sha2_256.into(),
            Sha2_256::digest(b"hello world").as_ref(),
        )
        .unwrap();
        // println!("mh1: code = {}, size = {}, digest = {:?}", mh1.code(), mh1.size(), mh1.digest());
        let mh1_bytes = mh1.encode();
        // println!("Multihash<32>: {}", hex::encode(&mh1_bytes));
        let mh2: Multihash<crate::U32> = Decode::decode(&mut &mh1_bytes[..]).unwrap();
        assert_eq!(mh1, mh2);

        let mh3: Multihash<crate::U64> = Code::Sha2_256.digest(b"hello world");
        // println!("mh3: code = {}, size = {}, digest = {:?}", mh3.code(), mh3.size(), mh3.digest());
        let mh3_bytes = mh3.encode();
        // println!("Multihash<64>: {}", hex::encode(&mh3_bytes));
        let mh4: Multihash<crate::U64> = Decode::decode(&mut &mh3_bytes[..]).unwrap();
        assert_eq!(mh3, mh4);

        assert_eq!(mh1_bytes, mh3_bytes);
    }

    #[test]
    #[cfg(feature = "serde-codec")]
    fn test_serde() {
        let mh = Multihash::<crate::U32>::default();
        let bytes = serde_json::to_string(&mh).unwrap();
        let mh2 = serde_json::from_str(&bytes).unwrap();
        assert_eq!(mh, mh2);
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::{
    collections::{hash_map::RandomState, HashSet},
    iter::FromIterator,
    path::{Path, PathBuf},
};

use crate::{
    engine::{
        engine::Pool,
        types::{CreateAction, DevUuid, PoolUuid, SetCreateAction},
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

/// Called when the name of a requested pool coincides with the name of an
/// existing pool. Returns an error if the specifications of the requested
/// pool differ from the specifications of the existing pool, otherwise
/// returns Ok(CreateAction::Identity).
pub fn create_pool_idempotent_or_err(
    pool: &dyn Pool,
    pool_name: &str,
    blockdev_paths: &[&Path],
) -> StratisResult<CreateAction<PoolUuid>> {
    let input_devices: HashSet<PathBuf, RandomState> =
        blockdev_paths.iter().map(|p| p.to_path_buf()).collect();

    let existing_paths: HashSet<PathBuf, _> = pool
        .blockdevs()
        .iter()
        .map(|(_, _, bd)| bd.devnode().physical_path().to_owned())
        .collect();

    if input_devices == existing_paths {
        Ok(CreateAction::Identity)
    } else {
        let in_input = input_devices
            .difference(&existing_paths)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        let in_pool = existing_paths
            .difference(&input_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        Err(StratisError::Engine(
            ErrorEnum::Invalid,
            create_pool_generate_error_string!(pool_name, in_input, in_pool),
        ))
    }
}

/// Called when the name of a requested pool coincides with the name of an
/// existing pool. Returns an error if the specifications of the requested
/// pool differ from the specifications of the existing pool, otherwise
/// returns Ok(CreateAction::Identity).
pub fn init_cache_idempotent_or_err<I>(
    blockdev_paths: &[&Path],
    existing_iter: I,
) -> StratisResult<SetCreateAction<DevUuid>>
where
    I: Iterator<Item = PathBuf>,
{
    let input_devices = HashSet::from_iter(blockdev_paths.iter().map(|p| p.to_path_buf()));
    let existing_devices = HashSet::<_, RandomState>::from_iter(existing_iter);
    if input_devices == existing_devices {
        Ok(SetCreateAction::empty())
    } else {
        let in_input = input_devices
            .difference(&existing_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        let in_pool = existing_devices
            .difference(&input_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        Err(StratisError::Engine(
            ErrorEnum::Invalid,
            init_cache_generate_error_string!(in_input, in_pool),
        ))
    }
}
<commit_msg>Fix bug in data device calculation<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::{
    collections::{hash_map::RandomState, HashSet},
    iter::FromIterator,
    path::{Path, PathBuf},
};

use crate::{
    engine::{
        engine::Pool,
        types::{BlockDevTier, CreateAction, DevUuid, PoolUuid, SetCreateAction},
    },
    stratis::{ErrorEnum, StratisError, StratisResult},
};

/// Called when the name of a requested pool coincides with the name of an
/// existing pool. Returns an error if the specifications of the requested
/// pool differ from the specifications of the existing pool, otherwise
/// returns Ok(CreateAction::Identity).
pub fn create_pool_idempotent_or_err(
    pool: &dyn Pool,
    pool_name: &str,
    blockdev_paths: &[&Path],
) -> StratisResult<CreateAction<PoolUuid>> {
    let input_devices: HashSet<PathBuf, RandomState> =
        blockdev_paths.iter().map(|p| p.to_path_buf()).collect();

    let existing_paths: HashSet<PathBuf, _> = pool
        .blockdevs()
        .iter()
        .filter_map(|(_, tier, bd)| {
            if *tier == BlockDevTier::Data {
                Some(bd.devnode().physical_path().to_owned())
            } else {
                None
            }
        })
        .collect();

    if input_devices == existing_paths {
        Ok(CreateAction::Identity)
    } else {
        let in_input = input_devices
            .difference(&existing_paths)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        let in_pool = existing_paths
            .difference(&input_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        Err(StratisError::Engine(
            ErrorEnum::Invalid,
            create_pool_generate_error_string!(pool_name, in_input, in_pool),
        ))
    }
}

/// Called when the name of a requested pool coincides with the name of an
/// existing pool. Returns an error if the specifications of the requested
/// pool differ from the specifications of the existing pool, otherwise
/// returns Ok(CreateAction::Identity).
pub fn init_cache_idempotent_or_err<I>(
    blockdev_paths: &[&Path],
    existing_iter: I,
) -> StratisResult<SetCreateAction<DevUuid>>
where
    I: Iterator<Item = PathBuf>,
{
    let input_devices = HashSet::from_iter(blockdev_paths.iter().map(|p| p.to_path_buf()));
    let existing_devices = HashSet::<_, RandomState>::from_iter(existing_iter);
    if input_devices == existing_devices {
        Ok(SetCreateAction::empty())
    } else {
        let in_input = input_devices
            .difference(&existing_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        let in_pool = existing_devices
            .difference(&input_devices)
            .map(|path| path.display().to_string())
            .collect::<Vec<_>>();
        Err(StratisError::Engine(
            ErrorEnum::Invalid,
            init_cache_generate_error_string!(in_input, in_pool),
        ))
    }
}
<|endoftext|>


<commit_before>extern crate polydraw;
extern crate toml;

use polydraw::{Renderer, Application, Frame};
use polydraw::devel::{Scene, Poly, DevelRenderer};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

const NODE_DEFS: &'static str = r#"

   [frame]
   type = "frame"

   [poly-points]
   type = "[(i64, i64)]"
   value = [ [0, 0], [90, 1200], [261, 1735], [1443, 410] ]

   [translate-point]
   type = "pair"
   in-1 = "frame"
   in-2 = { type = "i64", value = 0 }

   [add-operator]
   type = "add"
   in-1 = "poly-points"
   in-2 = "translate-point"

   [poly]
   type = "poly"
   in-1 = "add-operator"
   in-2 = { type = "(u8, u8, u8)", value = [0, 127, 255] }

   [layer]
   type = "layer"
   in-1 = "poly"

   [doc]
   type = "doc"
   in-1 = "layer"

"#;

type TPoint = (i64, i64);
type TPoly = Vec<Vec<TPoint>>;

#[derive(Debug)]
#[allow(dead_code)]
enum Data {
   None,
   I64(i64),
   F64(f64),
   Point(TPoint),
   Poly(TPoly),
}

const NONE: Data = Data::None;

trait Node {
   fn new(init1: Data, init2: Data, init3: Data, init4: Data) -> Self;

   fn process(&self, arg1: &Data, arg2: &Data, arg3: &Data, arg4: &Data) -> Data;
}

struct AddNode {
   init1: Data,
   init2: Data,
}

impl Node for AddNode {
   #[inline]
   fn new(init1: Data, init2: Data, _: Data, _: Data) -> Self {
      AddNode {
         init1: init1,
         init2: init2,
      }
   }

   #[inline]
   fn process(&self, arg1: &Data, arg2: &Data, _: &Data, _: &Data) -> Data {
      let in1 = actual(arg1, &self.init1);
      let in2 = actual(arg2, &self.init2);

      match (in1, in2) {
         (&Data::I64(ref v1), &Data::I64(ref v2)) => <(i64, i64)>::add(v1, v2),

         (&Data::F64(ref v1), &Data::I64(ref v2)) => <(f64, i64)>::add(v1, v2),
         (&Data::I64(ref v1), &Data::F64(ref v2)) => <(f64, i64)>::add(v2, v1),

         (&Data::Point(ref v1), &Data::I64(ref v2)) => <(TPoint, i64)>::add(v1, v2),
         (&Data::I64(ref v1), &Data::Point(ref v2)) => <(TPoint, i64)>::add(v2, v1),

         (&Data::Poly(ref v1), &Data::Point(ref v2)) => <(TPoly, TPoint)>::add(v1, v2),
         (&Data::Point(ref v1), &Data::Poly(ref v2)) => <(TPoly, TPoint)>::add(v2, v1),

         _ => NONE
      }
   }
}

trait Add<T1, T2> {
   fn add(v1: &T1, v2: &T2) -> Data;
}

impl Add<i64, i64> for (i64, i64) {
   #[inline]
   fn add(v1: &i64, v2: &i64) -> Data {
      Data::I64(*v1 + *v2)
   }
}

impl Add<f64, i64> for (f64, i64) {
   #[inline]
   fn add(v1: &f64, v2: &i64) -> Data {
      Data::F64(*v1 + *v2 as f64)
   }
}

impl Add<TPoint, i64> for (TPoint, i64) {
   #[inline]
   fn add(v1: &TPoint, v2: &i64) -> Data {
      Data::Point((v1.0 + *v2, v1.1 + *v2))
   }
}

impl Add<TPoly, TPoint> for (TPoly, TPoint) {
   #[inline]
   fn add(v1: &TPoly, v2: &TPoint) -> Data {
      let mut outer = Vec::with_capacity(v1.len());

      for src in v1 {
         let mut inner = Vec::with_capacity(src.len());

         for tuple in src {
            inner.push((tuple.0 + v2.0, tuple.1 + v2.1));
         }

         outer.push(inner);
      }

      Data::Poly(outer)
   }
}

#[inline]
fn actual<'a>(passed: &'a Data, initial: &'a Data) -> &'a Data {
   match passed {
      &Data::None => initial,
      _ => passed
   }
}

#[inline]
fn _poly_from_data(data: &TPoly) -> Poly {
   let outer = _points_from_coords(&data[0]);

   let mut inner = Vec::new();

   for inner_data in &data[1..] {
      inner.push(
         _points_from_coords(inner_data)
      );
   }

   let poly = Poly::new_with_holes(
      outer, inner, RGB::new(81, 180, 200),
   );

   poly
}

#[inline]
fn _points_from_coords(coords: &[(i64, i64)]) -> Vec<Point> {
   let mut points = Vec::new();

   for &(x, y) in coords.iter() {
      points.push(Point::new(x + 120, y + 120))
   }

   points
}

struct NodeRenderer {
   renderer: DevelRenderer,
   frame: i64,
}

impl NodeRenderer {
   #[inline]
   pub fn new() -> Self {
      NodeRenderer {
         renderer: DevelRenderer::new(Scene::new()),
         frame: 0,
      }
   }

   #[inline]
   pub fn parse_nodes(&mut self) {
      let mut parser = toml::Parser::new(NODE_DEFS);

      match parser.parse() {
          Some(value) => println!("toml parsed: {:?}", value),
          None => {
              println!("parse errors: {:?}", parser.errors);
          }
      }
   }
}

impl Renderer for NodeRenderer {
   #[inline]
   fn init(&mut self, width: u32, height: u32) {
      self.parse_nodes();

      self.renderer.init(width, height);
   }

   #[inline]
   fn render(&mut self, frame: &mut Frame) {
      let mut scene = Scene::new();

      let source = vec![vec![
         (90, 1200),
         (261, 1735),
         (1443, 410),
         (493, 174),
      ]];

      let add = AddNode::new(NONE, NONE, NONE, NONE);

      let destination = add.process(
         &Data::Poly(source), &Data::Point((self.frame, 0)), &NONE, &NONE
      );

      match destination {
         Data::Poly(data) => scene.push(_poly_from_data(&data)),
         _ => {}
      }

      self.renderer.set_scene(scene);

      self.renderer.render(frame);

      self.frame += 1;
   }
}

fn main() {
   let mut renderer = NodeRenderer::new();

   Application::new()
      .renderer(&mut renderer)
      .title("Nodes")
      .size(1200, 800)
      .run();
}

<commit_msg>Generic names for the Data enum<commit_after>extern crate polydraw;
extern crate toml;

use polydraw::{Renderer, Application, Frame};
use polydraw::devel::{Scene, Poly, DevelRenderer};
use polydraw::geom::point::Point;
use polydraw::draw::RGB;

const NODE_DEFS: &'static str = r#"

   [frame]
   type = "frame"

   [poly-points]
   type = "[(i64, i64)]"
   value = [ [0, 0], [90, 1200], [261, 1735], [1443, 410] ]

   [translate-point]
   type = "pair"
   in-1 = "frame"
   in-2 = { type = "i64", value = 0 }

   [add-operator]
   type = "add"
   in-1 = "poly-points"
   in-2 = "translate-point"

   [poly]
   type = "poly"
   in-1 = "add-operator"
   in-2 = { type = "(u8, u8, u8)", value = [0, 127, 255] }

   [layer]
   type = "layer"
   in-1 = "poly"

   [doc]
   type = "doc"
   in-1 = "layer"

"#;

type I64I64 = (i64, i64);
type VVI64I64 = Vec<Vec<I64I64>>;

#[derive(Debug)]
#[allow(dead_code)]
enum Data {
   None,
   I64(i64),
   F64(f64),
   I64I64(I64I64),
   VVI64I64(VVI64I64),
}

const NONE: Data = Data::None;

trait Node {
   fn new(init1: Data, init2: Data, init3: Data, init4: Data) -> Self;

   fn process(&self, arg1: &Data, arg2: &Data, arg3: &Data, arg4: &Data) -> Data;
}

struct AddNode {
   init1: Data,
   init2: Data,
}

impl Node for AddNode {
   #[inline]
   fn new(init1: Data, init2: Data, _: Data, _: Data) -> Self {
      AddNode {
         init1: init1,
         init2: init2,
      }
   }

   #[inline]
   fn process(&self, arg1: &Data, arg2: &Data, _: &Data, _: &Data) -> Data {
      let in1 = actual(arg1, &self.init1);
      let in2 = actual(arg2, &self.init2);

      match (in1, in2) {
         (&Data::I64(ref v1), &Data::I64(ref v2)) => <(i64, i64)>::add(v1, v2),

         (&Data::F64(ref v1), &Data::I64(ref v2)) => <(f64, i64)>::add(v1, v2),
         (&Data::I64(ref v1), &Data::F64(ref v2)) => <(f64, i64)>::add(v2, v1),

         (&Data::I64I64(ref v1), &Data::I64(ref v2)) => <(I64I64, i64)>::add(v1, v2),
         (&Data::I64(ref v1), &Data::I64I64(ref v2)) => <(I64I64, i64)>::add(v2, v1),

         (&Data::VVI64I64(ref v1), &Data::I64I64(ref v2)) => <(VVI64I64, I64I64)>::add(v1, v2),
         (&Data::I64I64(ref v1), &Data::VVI64I64(ref v2)) => <(VVI64I64, I64I64)>::add(v2, v1),

         _ => NONE
      }
   }
}

trait Add<T1, T2> {
   fn add(v1: &T1, v2: &T2) -> Data;
}

impl Add<i64, i64> for (i64, i64) {
   #[inline]
   fn add(v1: &i64, v2: &i64) -> Data {
      Data::I64(*v1 + *v2)
   }
}

impl Add<f64, i64> for (f64, i64) {
   #[inline]
   fn add(v1: &f64, v2: &i64) -> Data {
      Data::F64(*v1 + *v2 as f64)
   }
}

impl Add<I64I64, i64> for (I64I64, i64) {
   #[inline]
   fn add(v1: &I64I64, v2: &i64) -> Data {
      Data::I64I64((v1.0 + *v2, v1.1 + *v2))
   }
}

impl Add<VVI64I64, I64I64> for (VVI64I64, I64I64) {
   #[inline]
   fn add(v1: &VVI64I64, v2: &I64I64) -> Data {
      let mut outer = Vec::with_capacity(v1.len());

      for src in v1 {
         let mut inner = Vec::with_capacity(src.len());

         for tuple in src {
            inner.push((tuple.0 + v2.0, tuple.1 + v2.1));
         }

         outer.push(inner);
      }

      Data::VVI64I64(outer)
   }
}

#[inline]
fn actual<'a>(passed: &'a Data, initial: &'a Data) -> &'a Data {
   match passed {
      &Data::None => initial,
      _ => passed
   }
}

#[inline]
fn _poly_from_data(data: &VVI64I64) -> Poly {
   let outer = _points_from_coords(&data[0]);

   let mut inner = Vec::new();

   for inner_data in &data[1..] {
      inner.push(
         _points_from_coords(inner_data)
      );
   }

   let poly = Poly::new_with_holes(
      outer, inner, RGB::new(81, 180, 200),
   );

   poly
}

#[inline]
fn _points_from_coords(coords: &[(i64, i64)]) -> Vec<Point> {
   let mut points = Vec::new();

   for &(x, y) in coords.iter() {
      points.push(Point::new(x + 120, y + 120))
   }

   points
}

struct NodeRenderer {
   renderer: DevelRenderer,
   frame: i64,
}

impl NodeRenderer {
   #[inline]
   pub fn new() -> Self {
      NodeRenderer {
         renderer: DevelRenderer::new(Scene::new()),
         frame: 0,
      }
   }

   #[inline]
   pub fn parse_nodes(&mut self) {
      let mut parser = toml::Parser::new(NODE_DEFS);

      match parser.parse() {
          Some(value) => println!("toml parsed: {:?}", value),
          None => {
              println!("parse errors: {:?}", parser.errors);
          }
      }
   }
}

impl Renderer for NodeRenderer {
   #[inline]
   fn init(&mut self, width: u32, height: u32) {
      self.parse_nodes();

      self.renderer.init(width, height);
   }

   #[inline]
   fn render(&mut self, frame: &mut Frame) {
      let mut scene = Scene::new();

      let source = vec![vec![
         (90, 1200),
         (261, 1735),
         (1443, 410),
         (493, 174),
      ]];

      let add = AddNode::new(NONE, NONE, NONE, NONE);

      let destination = add.process(
         &Data::VVI64I64(source), &Data::I64I64((self.frame, 0)), &NONE, &NONE
      );

      match destination {
         Data::VVI64I64(data) => scene.push(_poly_from_data(&data)),
         _ => {}
      }

      self.renderer.set_scene(scene);

      self.renderer.render(frame);

      self.frame += 1;
   }
}

fn main() {
   let mut renderer = NodeRenderer::new();

   Application::new()
      .renderer(&mut renderer)
      .title("Nodes")
      .size(1200, 800)
      .run();
}

<|endoftext|>


<commit_before>
use rsfml::graphics::RenderWindow;
use rsfml::window::{event, keyboard};
use rsfml::window::keyboard::Key;
use rsfml::window::mouse::*;

pub struct EventHandler {
    pub events : Vec<event::Event>
}

impl EventHandler {
    pub fn new() -> EventHandler {
        EventHandler {
            events : Vec::new()
        }
    }

    pub fn is_key_pressed(&self, key : Key) -> bool {
        keyboard::is_key_pressed(key)
    }

    pub fn has_closed_event(&self) -> bool {
        for ev in self.events.iter() {
            match *ev {
                event::Closed   => return true,
                _               => {}
            }
        }
        false
    }

    pub fn has_gained_focus_event(&self) -> bool {
        for ev in self.events.iter() {
            match *ev {
                event::GainedFocus  => return true,
                _                   => {}
            }
        }
        false
    }

    pub fn has_lost_focus_event(&self) -> bool {
        for ev in self.events.iter() {
            match *ev {
                event::LostFocus    => return true,
                _                   => {}
            }
        }
        false
    }

    pub fn has_text_entered(&self) -> Option<char> {
        for ev in self.events.iter() {
            match *ev {
                event::TextEntered { code }     => return Some(code),
                _                               => {}
            }
        }
        None
    }

    pub fn has_key_pressed_event(&self, key : Key) -> Option<(Key, bool, bool, bool, bool)> {
     for ev in self.events.iter()  {
         match *ev {
             event::KeyPressed { code, alt, ctrl, shift, system }   =>  {
                 if code as int == key as int {
                     return Some((code, alt, ctrl, shift, system))
                 }
             },
             _                                                      => {}
         }
     }
     None
    }

    pub fn has_key_released_event(&self, key : Key) -> Option<(Key, bool, bool, bool, bool)> {
     for ev in self.events.iter() {
         match *ev {
             event::KeyReleased { code, alt, ctrl, shift, system }  =>  {
                 if code as int == key as int {
                     return Some((code, alt, ctrl, shift, system))
                 }
             },
             _                                                      => {}
         }
     }
     None
    }

    pub fn has_mouse_wheel_moved_event(&self) -> Option<(int, int, int)> {
     for ev in self.events.iter() {
         match *ev {
             event::MouseWheelMoved { delta, x, y }     => return Some((delta, x, y)),
             _                                          => {}
         }
     }
     None
    }

    pub fn has_mouse_button_pressed_event(&self, mouse_button : MouseButton) -> Option<(MouseButton, int, int)> {
     for ev in self.events.iter() {
         match *ev {
             event::MouseButtonPressed { button, x, y }     =>  {
                 if mouse_button as int == button as int {
                     return Some((button, x, y))
                 }
             },
             _                                              => {}
         }
     }
     None
    }

    pub fn has_mouse_button_released_event(&self, mouse_button : MouseButton) -> Option<(MouseButton, int, int)> {
     for ev in self.events.iter() {
         match *ev {
             event::MouseButtonReleased { button, x, y }    =>  {
                 if mouse_button as int == button as int {
                     return Some((button, x, y))
                 }
             },
             _                                              => {}
         }
     }
     None
    }

    pub fn has_mouse_moved_event(&self) -> Option<(int, int)> {
     for ev in self.events.iter() {
         match *ev {
             event::MouseMoved { x, y }     => return Some((x, y)),
             _                           => {}
         }
     }
     None
    }

    pub fn has_mouse_entered_event(&self) -> bool {
        for ev in self.events.iter() {
            match *ev {
                event::MouseEntered     => return true,
                _                       => {}
            }
        }
        false
    }

    pub fn has_mouse_left_event(&self) -> bool {
        for ev in self.events.iter() {
            match *ev {
                event::MouseLeft    => return true,
                _                   => {}
            }
        }
        false
    }

    // pub fn get_mouse_position(&self) -> Vector2i {
    //     self.render_window.get_mouse_position()
    // }

    pub fn get_events(&self) -> Vec<event::Event> {
        let mut r_events = Vec::new();
        for ev in self.events.iter() {
            r_events.push(*ev)
        }
        r_events
    }

    pub fn update_events(&mut self, render_window: &mut RenderWindow) -> () {
        self.events.clear();
        let mut ev;
        loop {
            ev = render_window.poll_event();
            match ev {
                event::NoEvent => break,
                _ => self.events.push(ev)
            }
        }
    }
}

// TODO IMPLEMENT FUNCTION FOR JOYSTICK HANDLE
// JoystickButtonPressed { joystickid : int, button : int },
// JoystickButtonReleased { joystickid : int, button : int },
// JoystickMoved { joystickid : uint, axis : Axis, position : float },
// JoystickConnected { joystickid : uint },
// JoystickDisconnected { joystickid : uint },
<commit_msg>fix last rsfml change<commit_after>
use rsfml::graphics::RenderWindow;
use rsfml::window::{event, keyboard};
use rsfml::window::keyboard::Key;
use rsfml::window::mouse::*;

pub struct EventHandler {
    pub events : Vec<event::Event>
}

impl EventHandler {
    pub fn new() -> EventHandler {
        EventHandler {
            events : Vec::new()
        }
    }

    pub fn is_key_pressed(&self, key : Key) -> bool {
        keyboard::is_key_pressed(key)
    }

    pub fn has_closed_event(&self) -> bool {
        for ev in self.events.iter() {
            match *ev {
                event::Closed   => return true,
                _               => {}
            }
        }
        false
    }

    pub fn has_gained_focus_event(&self) -> bool {
        for ev in self.events.iter() {
            match *ev {
                event::GainedFocus  => return true,
                _                   => {}
            }
        }
        false
    }

    pub fn has_lost_focus_event(&self) -> bool {
        for ev in self.events.iter() {
            match *ev {
                event::LostFocus    => return true,
                _                   => {}
            }
        }
        false
    }

    pub fn has_text_entered(&self) -> Option<char> {
        for ev in self.events.iter() {
            match *ev {
                event::TextEntered { code }     => return Some(code),
                _                               => {}
            }
        }
        None
    }

    pub fn has_key_pressed_event(&self, key : Key) -> Option<(Key, bool, bool, bool, bool)> {
     for ev in self.events.iter()  {
         match *ev {
             event::KeyPressed { code, alt, ctrl, shift, system }   =>  {
                 if code as int == key as int {
                     return Some((code, alt, ctrl, shift, system))
                 }
             },
             _                                                      => {}
         }
     }
     None
    }

    pub fn has_key_released_event(&self, key : Key) -> Option<(Key, bool, bool, bool, bool)> {
     for ev in self.events.iter() {
         match *ev {
             event::KeyReleased { code, alt, ctrl, shift, system }  =>  {
                 if code as int == key as int {
                     return Some((code, alt, ctrl, shift, system))
                 }
             },
             _                                                      => {}
         }
     }
     None
    }

    pub fn has_mouse_wheel_moved_event(&self) -> Option<(i32, i32, i32)> {
     for ev in self.events.iter() {
         match *ev {
             event::MouseWheelMoved { delta, x, y }     => return Some((delta, x, y)),
             _                                          => {}
         }
     }
     None
    }

    pub fn has_mouse_button_pressed_event(&self, mouse_button : MouseButton) -> Option<(MouseButton, i32, i32)> {
     for ev in self.events.iter() {
         match *ev {
             event::MouseButtonPressed { button, x, y }     =>  {
                 if mouse_button as int == button as int {
                     return Some((button, x, y))
                 }
             },
             _                                              => {}
         }
     }
     None
    }

    pub fn has_mouse_button_released_event(&self, mouse_button : MouseButton) -> Option<(MouseButton, i32, i32)> {
     for ev in self.events.iter() {
         match *ev {
             event::MouseButtonReleased { button, x, y }    =>  {
                 if mouse_button as int == button as int {
                     return Some((button, x, y))
                 }
             },
             _                                              => {}
         }
     }
     None
    }

    pub fn has_mouse_moved_event(&self) -> Option<(i32, i32)> {
     for ev in self.events.iter() {
         match *ev {
             event::MouseMoved { x, y }     => return Some((x, y)),
             _                           => {}
         }
     }
     None
    }

    pub fn has_mouse_entered_event(&self) -> bool {
        for ev in self.events.iter() {
            match *ev {
                event::MouseEntered     => return true,
                _                       => {}
            }
        }
        false
    }

    pub fn has_mouse_left_event(&self) -> bool {
        for ev in self.events.iter() {
            match *ev {
                event::MouseLeft    => return true,
                _                   => {}
            }
        }
        false
    }

    // pub fn get_mouse_position(&self) -> Vector2i {
    //     self.render_window.get_mouse_position()
    // }

    pub fn get_events(&self) -> Vec<event::Event> {
        let mut r_events = Vec::new();
        for ev in self.events.iter() {
            r_events.push(*ev)
        }
        r_events
    }

    pub fn update_events(&mut self, render_window: &mut RenderWindow) -> () {
        self.events.clear();
        let mut ev;
        loop {
            ev = render_window.poll_event();
            match ev {
                event::NoEvent => break,
                _ => self.events.push(ev)
            }
        }
    }
}

// TODO IMPLEMENT FUNCTION FOR JOYSTICK HANDLE
// JoystickButtonPressed { joystickid : int, button : int },
// JoystickButtonReleased { joystickid : int, button : int },
// JoystickMoved { joystickid : uint, axis : Axis, position : float },
// JoystickConnected { joystickid : uint },
// JoystickDisconnected { joystickid : uint },
<|endoftext|>