<commit_before>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_assignments)]
extern crate json;

use std::cmp::Ordering;

pub struct PhoneticParser<'a> {
    patterns: &'a json::JsonValue,
    vowel: String,
    consonant: String,
    numbers: String,
    case_sensitive: String,
}

impl<'a> PhoneticParser<'a> {
    pub fn new(rule: &json::JsonValue) -> PhoneticParser {
        PhoneticParser {
            patterns: &rule["patterns"],
            vowel: rule["vowel"].as_str().unwrap().to_string(),
            consonant: rule["consonant"].as_str().unwrap().to_string(),
            numbers: rule["number"].as_str().unwrap().to_string(),
            case_sensitive: rule["casesensitive"].as_str().unwrap().to_string()
        }
    }

    pub fn convert(&self, input: String) -> String {
        let fixed = self.fix_string(input);
        let mut output = String::new();

        let _find = &self.patterns[0]["find"].to_string();
        let max_pattern_len = _find.len();

        let len = fixed.len();

        for mut cur in 0..len {
            let start = cur as i32;
            let mut end: i32 = 0;
            let mut matched = false;

            for chunk_len in (1..max_pattern_len+1).rev() {
                end = start + chunk_len as i32;
                if end <= len as i32 {
                    let chunk = fixed.substring(start as usize, chunk_len as usize);

                    // Binary Search
                    let mut left: i32 = 0;
                    let mut right = self.patterns.len() as i32 - 1;
                    let mut mid: i32 = 0;
                    while right >= left {
                        mid = (right + left) / 2;
                        let pattern = &self.patterns[mid as usize];
                        let find = pattern["find"].as_str().unwrap();
                        //println!("chunk {} find {}", chunk, find);
                        if find == chunk {
                            let rules = &pattern["rules"];
                            if !rules.is_empty() {
                                for rule in rules.members() {
                                    let mut replace = true;
                                    let mut chk = 0;
                                    let matches = &rule["matches"];
                                    for _match in matches.members() {
                                        let value = _match["value"].as_str().unwrap_or("");
                                        let _type = _match["type"].as_str().unwrap();
                                        let mut scope = _match["scope"].as_str().unwrap();
                                        let mut is_negative = false;

                                        // Handle Negative
                                        if &scope[0..1] == "!" {
                                            is_negative = true;
                                            scope = &scope[1..];
                                        }

                                        if _find == "suffix" {
                                            chk = end;
                                        } else {
                                            chk = start - 1;
                                        }

                                        // Beginning
                                        if scope == "punctuation" {
                                            if
                                              ! (
                                              (chk < 0 && (_type == "prefix")) ||
                                              (chk >= len as i32 && (_type == "suffix")) ||
                                               self.is_punctuation(fixed.at(chk as usize))
                                              ) ^ is_negative
                                            {
                                                replace = false;
                                                break;
                                            }
                                        } else if scope == "vowel" {
                                            if
                                              !(
                                               (
                                               (chk >= 0 && (_type == "prefix")) ||
                                               (chk < len as i32 && (_type == "suffix"))
                                               ) &&
                                               self.is_vowel(fixed.at(chk as usize))
                                               ) ^ is_negative
                                            {
                                                replace = false;
                                                break;
                                            }
                                        } else if scope == "consonant" {
                                            if
                                                !(
                                                (
                                                (chk >= 0 && (_type == "prefix")) ||
                                                (chk < len as i32 && (_type == "suffix"))
                                                ) &&
                                                self.is_consonant(fixed.at(chk as usize))
                                                ) ^ is_negative
                                            {
                                                replace = false;
                                                break;
                                            }
                                        } else if scope == "number" {
                                            if
                                              !(
                                               (
                                               (chk >= 0 && (_type == "prefix")) ||
                                               (chk < len as i32 && (_type == "suffix"))
                                               ) &&
                                               self.is_number(fixed.at(chk as usize))
                                               ) ^ is_negative
                                            {
                                                replace = false;
                                                break;
                                            }
                                        } else if scope == "exact" {
                                            let mut s: i32 = 0;
                                            let mut e: i32 = 0;
                                            if _type == "suffix" {
                                                s = end;
                                                e = end + value.len() as i32;
                                            } else {
                                                // Prefix
                                                s = start - value.len() as i32;
                                                e = start;
                                            }
                                            if !self.is_exact(value, &fixed, s, e, is_negative) {
                                                replace = false;
                                                break;
                                            }
                                        }
                                    }

                                    if replace {
                                        let rl = rule["replace"].as_str().unwrap();
                                        output += rl;
                                        cur = (end - 1) as usize;
                                        matched = true;
                                        break;
                                    }
                                }
                            }

                            if matched { break; }

                            // Default
                            let rl = pattern["replace"].as_str().unwrap();
                            output += rl;
                            cur = (end - 1) as usize;
                            matched = true;
                            break;
                        } else if find.len() > chunk.len() ||
                                  (find.len() == chunk.len() && find.to_string().cmp(&chunk.to_string()) == Ordering::Less) {
                            left = mid + 1;
                        } else {
                            right = mid - 1;
                        }
                    }
                    if matched { break; }
                }
            }

            if !matched {
                output += &fixed[cur..cur+1];
            }
        }

        output
    }

    fn fix_string(&self, string: String) -> String {
        string.chars().map(|character| {
            if self.is_case_sensitive(character) {
                character
            } else {
                character.to_lowercase().next().unwrap()
            }
        }).collect()
    }

    fn is_vowel(&self, string: &str) -> bool {
        self.vowel.contains(&string.to_lowercase())
    }

    fn is_consonant(&self, string: &str) -> bool {
        self.consonant.contains(&string.to_lowercase())
    }

    fn is_case_sensitive(&self, character: char) -> bool {
        self.case_sensitive.contains(&character.to_lowercase().to_string())
    }

    fn is_number(&self, character: &str) -> bool {
        self.numbers.contains(character)
    }

    fn is_exact(&self, needle: &str, heystack: &str, start: i32, end: i32, not: bool) -> bool {
        let len = end - start;
        //return ((start >= 0 && end < heystack.length() && (heystack.mid(start, len) == needle)) ^ strnot);
        let b = (start >= 0 && end < heystack.len() as i32 && (heystack.substring(start as usize, len as usize) == needle)) != not;
        println!("needle {} heystack {} start {} end {} not {} exact {}", needle, heystack, start, end, not, b);
        b
    }

    fn is_punctuation(&self, character: &str) -> bool {
        !(self.is_vowel(character) || self.is_consonant(character))
    }
}

trait Substring {
    fn substring(&self, start: usize, length: usize) -> &str;
    fn at(&self, pos: usize) -> &str;
}

impl Substring for std::string::String {
    fn substring(&self, start: usize, length: usize) -> &str {
        &self[start..(start+length)]
    }

    fn at(&self, pos: usize) -> &str {
        &self[pos..pos+1]
    }
}

impl Substring for str {
    fn substring(&self, start: usize, length: usize) -> &str {
        &self[start..(start+length)]
    }

    fn at(&self, pos: usize) -> &str {
        &self[pos..pos+1]
    }
}

#[cfg(test)]
mod tests {
    use std::env;
    use std::fs::File;
    use std::io::Read;
    use json;
    use super::PhoneticParser;

    fn get_rule() -> json::JsonValue {
        // Get the rule file
        let mut p = env::current_dir().unwrap();
        p.push("AvroPhonetic.json");
        let path = p.to_str().unwrap();

        let mut grammer = String::new();

        let _ = File::open(path).unwrap().read_to_string(&mut grammer);

        json::parse(&grammer).unwrap()
    }

    #[test]
    fn test_helpers() {
        let json = get_rule();
        let parser = PhoneticParser::new(&json);

        assert!(parser.is_vowel("A"));
        assert_eq!(parser.is_vowel("b"), false);
        assert!(parser.is_consonant("B"));
        assert_eq!(parser.is_consonant("e"), false);
        assert_eq!(parser.fix_string("ODEr AMAr".to_string()), "ODer amar");
        assert!(parser.is_number("1"));
    }
}
<commit_msg>Simplify compare function<commit_after>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_assignments)]
extern crate json;

use std::cmp::Ordering;

pub struct PhoneticParser<'a> {
    patterns: &'a json::JsonValue,
    vowel: String,
    consonant: String,
    numbers: String,
    case_sensitive: String,
}

impl<'a> PhoneticParser<'a> {
    pub fn new(rule: &json::JsonValue) -> PhoneticParser {
        PhoneticParser {
            patterns: &rule["patterns"],
            vowel: rule["vowel"].as_str().unwrap().to_string(),
            consonant: rule["consonant"].as_str().unwrap().to_string(),
            numbers: rule["number"].as_str().unwrap().to_string(),
            case_sensitive: rule["casesensitive"].as_str().unwrap().to_string()
        }
    }

    pub fn convert(&self, input: String) -> String {
        let fixed = self.fix_string(input);
        let mut output = String::new();

        let _find = &self.patterns[0]["find"].to_string();
        let max_pattern_len = _find.len();

        let len = fixed.len();

        for mut cur in 0..len {
            let start = cur as i32;
            let mut end: i32 = 0;
            let mut matched = false;

            for chunk_len in (1..max_pattern_len+1).rev() {
                end = start + chunk_len as i32;
                if end <= len as i32 {
                    let chunk = fixed.substring(start as usize, chunk_len as usize);

                    // Binary Search
                    let mut left: i32 = 0;
                    let mut right = self.patterns.len() as i32 - 1;
                    let mut mid: i32 = 0;
                    while right >= left {
                        mid = (right + left) / 2;
                        let pattern = &self.patterns[mid as usize];
                        let find = pattern["find"].as_str().unwrap();
                        //println!("chunk {} find {}", chunk, find);
                        if find == chunk {
                            let rules = &pattern["rules"];
                            if !rules.is_empty() {
                                for rule in rules.members() {
                                    let mut replace = true;
                                    let mut chk = 0;
                                    let matches = &rule["matches"];
                                    for _match in matches.members() {
                                        let value = _match["value"].as_str().unwrap_or("");
                                        let _type = _match["type"].as_str().unwrap();
                                        let mut scope = _match["scope"].as_str().unwrap();
                                        let mut is_negative = false;

                                        // Handle Negative
                                        if &scope[0..1] == "!" {
                                            is_negative = true;
                                            scope = &scope[1..];
                                        }

                                        if _find == "suffix" {
                                            chk = end;
                                        } else {
                                            chk = start - 1;
                                        }

                                        // Beginning
                                        if scope == "punctuation" {
                                            if
                                              ! (
                                              (chk < 0 && (_type == "prefix")) ||
                                              (chk >= len as i32 && (_type == "suffix")) ||
                                               self.is_punctuation(fixed.at(chk as usize))
                                              ) ^ is_negative
                                            {
                                                replace = false;
                                                break;
                                            }
                                        } else if scope == "vowel" {
                                            if
                                              !(
                                               (
                                               (chk >= 0 && (_type == "prefix")) ||
                                               (chk < len as i32 && (_type == "suffix"))
                                               ) &&
                                               self.is_vowel(fixed.at(chk as usize))
                                               ) ^ is_negative
                                            {
                                                replace = false;
                                                break;
                                            }
                                        } else if scope == "consonant" {
                                            if
                                                !(
                                                (
                                                (chk >= 0 && (_type == "prefix")) ||
                                                (chk < len as i32 && (_type == "suffix"))
                                                ) &&
                                                self.is_consonant(fixed.at(chk as usize))
                                                ) ^ is_negative
                                            {
                                                replace = false;
                                                break;
                                            }
                                        } else if scope == "number" {
                                            if
                                              !(
                                               (
                                               (chk >= 0 && (_type == "prefix")) ||
                                               (chk < len as i32 && (_type == "suffix"))
                                               ) &&
                                               self.is_number(fixed.at(chk as usize))
                                               ) ^ is_negative
                                            {
                                                replace = false;
                                                break;
                                            }
                                        } else if scope == "exact" {
                                            let mut s: i32 = 0;
                                            let mut e: i32 = 0;
                                            if _type == "suffix" {
                                                s = end;
                                                e = end + value.len() as i32;
                                            } else {
                                                // Prefix
                                                s = start - value.len() as i32;
                                                e = start;
                                            }
                                            if !self.is_exact(value, &fixed, s, e, is_negative) {
                                                replace = false;
                                                break;
                                            }
                                        }
                                    }

                                    if replace {
                                        let rl = rule["replace"].as_str().unwrap();
                                        output += rl;
                                        cur = (end - 1) as usize;
                                        matched = true;
                                        break;
                                    }
                                }
                            }

                            if matched { break; }

                            // Default
                            let rl = pattern["replace"].as_str().unwrap();
                            output += rl;
                            cur = (end - 1) as usize;
                            matched = true;
                            break;
                        } else if find.len() > chunk.len() ||
                                  (find.len() == chunk.len() && find.cmp(&chunk) == Ordering::Less) {
                            left = mid + 1;
                        } else {
                            right = mid - 1;
                        }
                    }
                    if matched { break; }
                }
            }

            if !matched {
                output += &fixed[cur..cur+1];
            }
        }

        output
    }

    fn fix_string(&self, string: String) -> String {
        string.chars().map(|character| {
            if self.is_case_sensitive(character) {
                character
            } else {
                character.to_lowercase().next().unwrap()
            }
        }).collect()
    }

    fn is_vowel(&self, string: &str) -> bool {
        self.vowel.contains(&string.to_lowercase())
    }

    fn is_consonant(&self, string: &str) -> bool {
        self.consonant.contains(&string.to_lowercase())
    }

    fn is_case_sensitive(&self, character: char) -> bool {
        self.case_sensitive.contains(&character.to_lowercase().to_string())
    }

    fn is_number(&self, character: &str) -> bool {
        self.numbers.contains(character)
    }

    fn is_exact(&self, needle: &str, heystack: &str, start: i32, end: i32, not: bool) -> bool {
        let len = end - start;
        //return ((start >= 0 && end < heystack.length() && (heystack.mid(start, len) == needle)) ^ strnot);
        let b = (start >= 0 && end < heystack.len() as i32 && (heystack.substring(start as usize, len as usize) == needle)) != not;
        println!("needle {} heystack {} start {} end {} not {} exact {}", needle, heystack, start, end, not, b);
        b
    }

    fn is_punctuation(&self, character: &str) -> bool {
        !(self.is_vowel(character) || self.is_consonant(character))
    }
}

trait Substring {
    fn substring(&self, start: usize, length: usize) -> &str;
    fn at(&self, pos: usize) -> &str;
}

impl Substring for std::string::String {
    fn substring(&self, start: usize, length: usize) -> &str {
        &self[start..(start+length)]
    }

    fn at(&self, pos: usize) -> &str {
        &self[pos..pos+1]
    }
}

impl Substring for str {
    fn substring(&self, start: usize, length: usize) -> &str {
        &self[start..(start+length)]
    }

    fn at(&self, pos: usize) -> &str {
        &self[pos..pos+1]
    }
}

#[cfg(test)]
mod tests {
    use std::env;
    use std::fs::File;
    use std::io::Read;
    use json;
    use super::PhoneticParser;

    fn get_rule() -> json::JsonValue {
        // Get the rule file
        let mut p = env::current_dir().unwrap();
        p.push("AvroPhonetic.json");
        let path = p.to_str().unwrap();

        let mut grammer = String::new();

        let _ = File::open(path).unwrap().read_to_string(&mut grammer);

        json::parse(&grammer).unwrap()
    }

    #[test]
    fn test_helpers() {
        let json = get_rule();
        let parser = PhoneticParser::new(&json);

        assert!(parser.is_vowel("A"));
        assert_eq!(parser.is_vowel("b"), false);
        assert!(parser.is_consonant("B"));
        assert_eq!(parser.is_consonant("e"), false);
        assert_eq!(parser.fix_string("ODEr AMAr".to_string()), "ODer amar");
        assert!(parser.is_number("1"));
    }
}
<|endoftext|>


<commit_before>//! # aHash
//!
//! This hashing algorithm is intended to be a high performance, (hardware specific), keyed hash function.
//! This can be seen as a DOS resistant alternative to FxHash, or a fast equivalent to SipHash.
//! It provides a high speed hash algorithm, but where the result is not predictable without knowing a Key.
//! This allows it to be used in a HashMap without allowing for the possibility that an malicious user can
//! induce a collision.
//!
//! # How aHash works
//!
//! aHash uses the hardware AES instruction on x86 processors to provide a keyed hash function.
//! It uses two rounds of AES per hash. So it should not be considered cryptographically secure.
#![cfg_attr(not(test), no_std)]
//#![feature(core_intrinsics)]
extern crate const_random;

#[macro_use]
mod convert;

mod fallback_hash;
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes"))]
mod aes_hash;
#[cfg(test)]
mod hash_quality_test;

use const_random::const_random;
use core::hash::{BuildHasher};

#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes"))]
pub use crate::aes_hash::AHasher;

#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes")))]
pub use crate::fallback_hash::AHasher;

/// A `HashMap` using a `BuildHasherDefault` BuildHasher to hash the items.
//pub type AHashMap<K, V> = HashMap<K, V, ABuildHasher>;

///Const random provides randomized keys with no runtime cost.
const DEFAULT_KEYS: [u64; 2] = [const_random!(u64), const_random!(u64)];

/// Provides a default [Hasher] compile time generated constants for keys.
/// This is typically used in conjunction with [BuildHasherDefault] to create
/// [AHasher]s in order to hash the keys of the map.
///
/// # Example
/// ```
/// use std::hash::BuildHasherDefault;
/// use ahash::{AHasher, ABuildHasher};
/// use std::collections::HashMap;
///
/// let mut map: HashMap<i32, i32, ABuildHasher> = HashMap::default();
/// map.insert(12, 34);
/// ```
///
/// [BuildHasherDefault]: std::hash::BuildHasherDefault
/// [Hasher]: std::hash::Hasher
/// [HashMap]: std::collections::HashMap
impl Default for AHasher {

    /// Constructs a new [AHasher] with compile time generated constants for keys.
    /// This means the keys will be the same from one instance to another,
    /// but different from build to the next. So if it is possible for a potential
    /// attacker to have access to the compiled binary it would be better
    /// to specify keys generated at runtime.
    ///
    /// # Examples
    ///
    /// ```
    /// use ahash::AHasher;
    /// use std::hash::Hasher;
    ///
    /// let mut hasher_1 = AHasher::default();
    /// let mut hasher_2 = AHasher::default();
    ///
    /// hasher_1.write_u32(1234);
    /// hasher_2.write_u32(1234);
    ///
    /// assert_eq!(hasher_1.finish(), hasher_2.finish());
    /// ```
    #[inline]
    fn default() -> AHasher {
        return AHasher::new_with_keys(DEFAULT_KEYS[0], DEFAULT_KEYS[1]);
    }
}

/// Provides a [Hasher] factory. This is typically used (e.g. by [HashMap]) to create
/// [AHasher]s in order to hash the keys of the map. See `build_hasher` below.
///
/// [build_hasher]: ahash::
/// [Hasher]: std::hash::Hasher
/// [BuildHasher]: std::hash::BuildHasher
/// [HashMap]: std::collections::HashMap
#[derive(Clone)]
pub struct ABuildHasher {
    keys: [u64; 2],
}

impl ABuildHasher {
    #[inline]
    pub fn new() -> ABuildHasher {
        let mut result = ABuildHasher {
            keys: [0, 0]
        };
        //Using a self pointer. When running with ASLR this is a random value.
        let k0 = &DEFAULT_KEYS as *const _ as usize as u64;
        let mut k1 = &result.keys as *const _ as usize as u64;
        //Scramble seeds (from xoroshiro128+) //It's important that this is not similar to the hash algorithm
        k1 ^= k0;
        result.keys[0] = k0.rotate_left(24) ^ k1 ^ (k1 << 16);
        result.keys[1] = k1.rotate_left(37);
        result
    }
}

impl Default for ABuildHasher {
    #[inline]
    fn default() -> ABuildHasher {
        ABuildHasher::new()
    }
}

impl BuildHasher for ABuildHasher {
    type Hasher = AHasher;

    /// Constructs a new [AHasher] with keys based on compile time generated constants and the location
    /// of the this object in memory. This means that two different [BuildHasher]s will will generate
    /// [AHasher]s that will return different hashcodes, but [Hasher]s created from the same [BuildHasher]
    /// will generate the same hashes for the same input data.
    ///
    /// # Examples
    ///
    /// ```
    /// use ahash::{AHasher, ABuildHasher};
    /// use std::hash::{Hasher, BuildHasher};
    ///
    /// let build_hasher = ABuildHasher::new();
    /// let mut hasher_1 = build_hasher.build_hasher();
    /// let mut hasher_2 = build_hasher.build_hasher();
    ///
    /// hasher_1.write_u32(1234);
    /// hasher_2.write_u32(1234);
    ///
    /// assert_eq!(hasher_1.finish(), hasher_2.finish());
    ///
    /// let other_build_hasher = ABuildHasher::new();
    /// let mut different_hasher = other_build_hasher.build_hasher();
    /// different_hasher.write_u32(1234);
    /// assert_ne!(different_hasher.finish(), hasher_1.finish());
    /// ```
    /// [Hasher]: std::hash::Hasher
    /// [BuildHasher]: std::hash::BuildHasher
    /// [HashMap]: std::collections::HashMap
    #[inline]
    fn build_hasher(&self) -> AHasher {
        return AHasher::new_with_keys(self.keys[0], self.keys[1]);
    }
}

#[inline(never)]
#[no_mangle]
fn hash_test_final(input: usize) -> u64 {
    use core::hash::{Hasher};
    let builder = ABuildHasher::default();
    let mut hasher = builder.build_hasher();
    hasher.write_usize(input);
    hasher.finish()
}

#[cfg(test)]
mod test {
    use core::hash::BuildHasherDefault;
    use std::collections::HashMap;
    use crate::convert::Convert;
    use crate::*;

    #[test]
    fn test_default_builder() {
        let mut map = HashMap::<u32, u64, BuildHasherDefault<AHasher>>::default();
        map.insert(1, 3);
    }
    #[test]
    fn test_builder() {
        let mut map = HashMap::<u32, u64, ABuildHasher>::default();
        map.insert(1, 3);
    }

    #[test]
    fn test_conversion() {
        let input: &[u8] = "dddddddd".as_bytes();
        let bytes: u64 = as_array!(input, 8).convert();
        assert_eq!(bytes, 0x6464646464646464);
    }
}
<commit_msg>Use static atomic to increment on each hash builder creation to ensure every instance is distinct without doing work at hasher creation time.<commit_after>//! # aHash
//!
//! This hashing algorithm is intended to be a high performance, (hardware specific), keyed hash function.
//! This can be seen as a DOS resistant alternative to FxHash, or a fast equivalent to SipHash.
//! It provides a high speed hash algorithm, but where the result is not predictable without knowing a Key.
//! This allows it to be used in a HashMap without allowing for the possibility that an malicious user can
//! induce a collision.
//!
//! # How aHash works
//!
//! aHash uses the hardware AES instruction on x86 processors to provide a keyed hash function.
//! It uses two rounds of AES per hash. So it should not be considered cryptographically secure.
#![cfg_attr(not(test), no_std)]
//#![feature(core_intrinsics)]
extern crate const_random;

#[macro_use]
mod convert;

mod fallback_hash;
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes"))]
mod aes_hash;
#[cfg(test)]
mod hash_quality_test;

use const_random::const_random;
use core::hash::{BuildHasher};
use core::sync::atomic::AtomicUsize;
use core::sync::atomic::Ordering;

#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes"))]
pub use crate::aes_hash::AHasher;

#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "aes")))]
pub use crate::fallback_hash::AHasher;

/// A `HashMap` using a `BuildHasherDefault` BuildHasher to hash the items.
//pub type AHashMap<K, V> = HashMap<K, V, ABuildHasher>;

///Const random provides randomized starting key with no runtime cost.
static SEED: AtomicUsize = AtomicUsize::new(const_random!(u64));

/// Provides a default [Hasher] compile time generated constants for keys.
/// This is typically used in conjunction with [BuildHasherDefault] to create
/// [AHasher]s in order to hash the keys of the map.
///
/// # Example
/// ```
/// use std::hash::BuildHasherDefault;
/// use ahash::{AHasher, ABuildHasher};
/// use std::collections::HashMap;
///
/// let mut map: HashMap<i32, i32, ABuildHasher> = HashMap::default();
/// map.insert(12, 34);
/// ```
///
/// [BuildHasherDefault]: std::hash::BuildHasherDefault
/// [Hasher]: std::hash::Hasher
/// [HashMap]: std::collections::HashMap
impl Default for AHasher {

    /// Constructs a new [AHasher] with compile time generated constants for keys.
    /// This means the keys will be the same from one instance to another,
    /// but different from build to the next. So if it is possible for a potential
    /// attacker to have access to the compiled binary it would be better
    /// to specify keys generated at runtime.
    ///
    /// # Examples
    ///
    /// ```
    /// use ahash::AHasher;
    /// use std::hash::Hasher;
    ///
    /// let mut hasher_1 = AHasher::default();
    /// let mut hasher_2 = AHasher::default();
    ///
    /// hasher_1.write_u32(1234);
    /// hasher_2.write_u32(1234);
    ///
    /// assert_eq!(hasher_1.finish(), hasher_2.finish());
    /// ```
    #[inline]
    fn default() -> AHasher {
        return AHasher::new_with_keys(const_random!(u64), const_random!(u64));
    }
}

/// Provides a [Hasher] factory. This is typically used (e.g. by [HashMap]) to create
/// [AHasher]s in order to hash the keys of the map. See `build_hasher` below.
///
/// [build_hasher]: ahash::
/// [Hasher]: std::hash::Hasher
/// [BuildHasher]: std::hash::BuildHasher
/// [HashMap]: std::collections::HashMap
#[derive(Clone)]
pub struct ABuildHasher {
    k0: u64,
    k1: u64,
}

impl ABuildHasher {
    #[inline]
    pub fn new() -> ABuildHasher {
        //Using a self pointer. When running with ASLR this is a random value.
        let mut k0 = SEED.load(Ordering::Relaxed) as u64;
        let k1 = (&SEED as *const _ as u64).rotate_left(32);
        let mut k1= &k1 as *const _ as u64 ^ k1;
        //Scramble seeds (based on xoroshiro128+)
        //It's important that this is not similar to the hash algorithm
        k1 ^= k0;
        k0 = k0.rotate_left(24) ^ k1 ^ (k1 << 16);
        SEED.store(k0 as usize, Ordering::Relaxed);
        k1 = k1.rotate_left(37);
        ABuildHasher{ k0:k0, k1:k1 }
    }
}

impl Default for ABuildHasher {
    #[inline]
    fn default() -> ABuildHasher {
        ABuildHasher::new()
    }
}

impl BuildHasher for ABuildHasher {
    type Hasher = AHasher;

    /// Constructs a new [AHasher] with keys based on compile time generated constants and the location
    /// of the this object in memory. This means that two different [BuildHasher]s will will generate
    /// [AHasher]s that will return different hashcodes, but [Hasher]s created from the same [BuildHasher]
    /// will generate the same hashes for the same input data.
    ///
    /// # Examples
    ///
    /// ```
    /// use ahash::{AHasher, ABuildHasher};
    /// use std::hash::{Hasher, BuildHasher};
    ///
    /// let build_hasher = ABuildHasher::new();
    /// let mut hasher_1 = build_hasher.build_hasher();
    /// let mut hasher_2 = build_hasher.build_hasher();
    ///
    /// hasher_1.write_u32(1234);
    /// hasher_2.write_u32(1234);
    ///
    /// assert_eq!(hasher_1.finish(), hasher_2.finish());
    ///
    /// let other_build_hasher = ABuildHasher::new();
    /// let mut different_hasher = other_build_hasher.build_hasher();
    /// different_hasher.write_u32(1234);
    /// assert_ne!(different_hasher.finish(), hasher_1.finish());
    /// ```
    /// [Hasher]: std::hash::Hasher
    /// [BuildHasher]: std::hash::BuildHasher
    /// [HashMap]: std::collections::HashMap
    #[inline]
    fn build_hasher(&self) -> AHasher {
        return AHasher::new_with_keys(self.k0, self.k1);
    }
}

#[inline(never)]
#[no_mangle]
fn hash_test_final(input: usize) -> u64 {
    use core::hash::{Hasher};
    let builder = ABuildHasher::default();
    let mut hasher = builder.build_hasher();
    hasher.write_usize(input);
    hasher.finish()
}

#[cfg(test)]
mod test {
    use core::hash::BuildHasherDefault;
    use std::collections::HashMap;
    use crate::convert::Convert;
    use crate::*;

    #[test]
    fn test_default_builder() {
        let mut map = HashMap::<u32, u64, BuildHasherDefault<AHasher>>::default();
        map.insert(1, 3);
    }
    #[test]
    fn test_builder() {
        let mut map = HashMap::<u32, u64, ABuildHasher>::default();
        map.insert(1, 3);
    }

    #[test]
    fn test_conversion() {
        let input: &[u8] = "dddddddd".as_bytes();
        let bytes: u64 = as_array!(input, 8).convert();
        assert_eq!(bytes, 0x6464646464646464);
    }
}
<|endoftext|>


<commit_before>//! **mprovision** is a tool that helps `iOS` developers to manage mobileprovision
//! files. Main purpose of this crate is to contain functions and types
//! for **mprovision**.

#[cfg(test)]
#[macro_use(expect)]
extern crate expectest;
#[cfg(test)]
extern crate tempdir;
extern crate plist;
extern crate chrono;
extern crate memmem;

extern crate futures;
extern crate futures_cpupool;
extern crate num_cpus;

use futures::stream::Stream;
use futures::Future;
use futures_cpupool::CpuPool;

use chrono::*;

use std::fs::{self, DirEntry, File};
use std::path::{Path, PathBuf};
use std::env;
use std::io::Read;

pub use error::Error;
pub use profile::Profile;
pub use context::Context;

mod error;
mod profile;
mod context;

/// A Result type for this crate.
pub type Result<T> = std::result::Result<T, Error>;

/// Returns an iterator over the `*.mobileprovision` entries within a given
/// directory.
///
/// # Errors
/// This function will return an error in the following cases:
///
/// - the user lacks the requisite permissions
/// - there is no entry in the filesystem at the provided path
/// - the provided path is not a directory
pub fn entries(dir: &Path) -> Result<Box<Iterator<Item = DirEntry>>> {
    let entries = try!(fs::read_dir(dir));
    let filtered = entries.filter_map(|entry| entry.ok())
        .filter_map(|entry| {
            if let Some(ext) = entry.path().extension() {
                if ext == "mobileprovision" {
                    return Some(entry);
                }
            }
            None
        });
    Ok(Box::new(filtered))
}

/// Returns the path to the directory that contains installed mobile
/// provisioning profiles.
///
/// Should return `~/Library/MobileDevice/Provisioning Profiles` directory.
///
/// # Errors
/// This function will return an error if 'HOME' environment variable is not set
/// or equal to the empty string.
pub fn directory() -> Result<PathBuf> {
    env::home_dir()
        .map(|path| path.join("Library/MobileDevice/Provisioning Profiles"))
        .ok_or_else(|| {
            Error::Own("'HOME' environment variable is not set or equal to the empty string."
                .to_owned())
        })
}

pub fn with_dir<F, T>(dir: Option<&Path>, f: F) -> Result<T>
    where F: FnOnce(&Path) -> Result<T>
{
    if let Some(dir) = dir {
        f(dir)
    } else {
        let dir = try!(directory());
        f(&dir)
    }
}

pub struct SearchInfo {
    pub total: usize,
    pub profiles: Vec<Profile>,
}

pub fn search(dir: &Path, s: &str) -> Result<SearchInfo> {
    let entries: Vec<DirEntry> = try!(entries(dir)).collect();
    Ok(SearchInfo {
        total: entries.len(),
        profiles: parallel(entries, |profile| profile.contains(s)),
    })
}

pub fn remove(dir: &Path, uuid: &str) -> Result<()> {
    find_by_uuid(dir, uuid)
        .and_then(|profile| std::fs::remove_file(&profile.path).map_err(|err| err.into()))
}

pub fn xml(dir: &Path, uuid: &str) -> Result<String> {
    match find_by_uuid(dir, uuid) {
        Ok(profile) => {
            let context = Context::default();
            let mut file = try!(File::open(&profile.path));
            let mut buf = Vec::new();
            try!(file.read_to_end(&mut buf));
            let data = try!(context.find_plist(&buf)
                .ok_or(Error::Own("Couldn't parse file.".into())));
            Ok(try!(String::from_utf8(data.to_owned())))
        }
        Err(err) => Err(err),
    }
}

pub fn expired_profiles(dir: &Path, date: DateTime<UTC>) -> Result<SearchInfo> {
    let entries: Vec<DirEntry> = try!(entries(dir)).collect();
    Ok(SearchInfo {
        total: entries.len(),
        profiles: parallel(entries, |profile| profile.expiration_date <= date),
    })
}

fn parallel<F>(entries: Vec<DirEntry>, f: F) -> Vec<Profile>
    where F: Fn(&Profile) -> bool + Sync
{
    let cpu_pool = CpuPool::new(num_cpus::get());

    let stream = futures::stream::iter(entries.into_iter().map(|entry| Ok(entry)))
        .map(|entry| {
            cpu_pool.spawn_fn(move || {
                Profile::from_file(&entry.path())
            })
        })
        .buffered(num_cpus::get() * 2)
        .filter(f)
        .collect();

    stream.wait().unwrap_or(Vec::new())
}

fn find_by_uuid(dir: &Path, uuid: &str) -> Result<Profile> {
    let entries: Vec<DirEntry> = try!(entries(dir)).collect();
    if let Some(profile) = parallel(entries, |profile| profile.uuid == uuid).pop() {
        Ok(profile)
    } else {
        Err(Error::Own(format!("Profile '{}' is not found.", uuid)))
    }
}

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

    #[test]
    fn filter_mobileprovision_files() {
        use tempdir::TempDir;
        use std::fs::File;

        let temp_dir = TempDir::new("test").unwrap();
        let result = entries(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(0));

        File::create(temp_dir.path().join("1.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("2.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("3.txt")).unwrap();
        let result = entries(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(2));
    }
}
<commit_msg>Reformat.<commit_after>//! **mprovision** is a tool that helps `iOS` developers to manage mobileprovision
//! files. Main purpose of this crate is to contain functions and types
//! for **mprovision**.

#[cfg(test)]
#[macro_use(expect)]
extern crate expectest;
#[cfg(test)]
extern crate tempdir;
extern crate plist;
extern crate chrono;
extern crate memmem;

extern crate futures;
extern crate futures_cpupool;
extern crate num_cpus;

use futures::stream::Stream;
use futures::Future;
use futures_cpupool::CpuPool;

use chrono::*;

use std::fs::{self, DirEntry, File};
use std::path::{Path, PathBuf};
use std::env;
use std::io::Read;

pub use error::Error;
pub use profile::Profile;
pub use context::Context;

mod error;
mod profile;
mod context;

/// A Result type for this crate.
pub type Result<T> = std::result::Result<T, Error>;

/// Returns an iterator over the `*.mobileprovision` entries within a given
/// directory.
///
/// # Errors
/// This function will return an error in the following cases:
///
/// - the user lacks the requisite permissions
/// - there is no entry in the filesystem at the provided path
/// - the provided path is not a directory
pub fn entries(dir: &Path) -> Result<Box<Iterator<Item = DirEntry>>> {
    let entries = try!(fs::read_dir(dir));
    let filtered = entries.filter_map(|entry| entry.ok())
        .filter_map(|entry| {
            if let Some(ext) = entry.path().extension() {
                if ext == "mobileprovision" {
                    return Some(entry);
                }
            }
            None
        });
    Ok(Box::new(filtered))
}

/// Returns the path to the directory that contains installed mobile
/// provisioning profiles.
///
/// Should return `~/Library/MobileDevice/Provisioning Profiles` directory.
///
/// # Errors
/// This function will return an error if 'HOME' environment variable is not set
/// or equal to the empty string.
pub fn directory() -> Result<PathBuf> {
    env::home_dir()
        .map(|path| path.join("Library/MobileDevice/Provisioning Profiles"))
        .ok_or_else(|| {
            Error::Own("'HOME' environment variable is not set or equal to the empty string."
                .to_owned())
        })
}

pub fn with_dir<F, T>(dir: Option<&Path>, f: F) -> Result<T>
    where F: FnOnce(&Path) -> Result<T>
{
    if let Some(dir) = dir {
        f(dir)
    } else {
        let dir = try!(directory());
        f(&dir)
    }
}

pub struct SearchInfo {
    pub total: usize,
    pub profiles: Vec<Profile>,
}

pub fn search(dir: &Path, s: &str) -> Result<SearchInfo> {
    let entries: Vec<DirEntry> = try!(entries(dir)).collect();
    Ok(SearchInfo {
        total: entries.len(),
        profiles: parallel(entries, |profile| profile.contains(s)),
    })
}

pub fn remove(dir: &Path, uuid: &str) -> Result<()> {
    find_by_uuid(dir, uuid)
        .and_then(|profile| std::fs::remove_file(&profile.path).map_err(|err| err.into()))
}

pub fn xml(dir: &Path, uuid: &str) -> Result<String> {
    match find_by_uuid(dir, uuid) {
        Ok(profile) => {
            let context = Context::default();
            let mut file = try!(File::open(&profile.path));
            let mut buf = Vec::new();
            try!(file.read_to_end(&mut buf));
            let data = try!(context.find_plist(&buf)
                .ok_or(Error::Own("Couldn't parse file.".into())));
            Ok(try!(String::from_utf8(data.to_owned())))
        }
        Err(err) => Err(err),
    }
}

pub fn expired_profiles(dir: &Path, date: DateTime<UTC>) -> Result<SearchInfo> {
    let entries: Vec<DirEntry> = try!(entries(dir)).collect();
    Ok(SearchInfo {
        total: entries.len(),
        profiles: parallel(entries, |profile| profile.expiration_date <= date),
    })
}

fn parallel<F>(entries: Vec<DirEntry>, f: F) -> Vec<Profile>
    where F: Fn(&Profile) -> bool + Sync
{
    let cpu_pool = CpuPool::new(num_cpus::get());

    let stream = futures::stream::iter(entries.into_iter().map(|entry| Ok(entry)))
        .map(|entry| cpu_pool.spawn_fn(move || Profile::from_file(&entry.path())))
        .buffered(num_cpus::get() * 2)
        .filter(f)
        .collect();

    stream.wait().unwrap_or(Vec::new())
}

fn find_by_uuid(dir: &Path, uuid: &str) -> Result<Profile> {
    let entries: Vec<DirEntry> = try!(entries(dir)).collect();
    if let Some(profile) = parallel(entries, |profile| profile.uuid == uuid).pop() {
        Ok(profile)
    } else {
        Err(Error::Own(format!("Profile '{}' is not found.", uuid)))
    }
}

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

    #[test]
    fn filter_mobileprovision_files() {
        use tempdir::TempDir;
        use std::fs::File;

        let temp_dir = TempDir::new("test").unwrap();
        let result = entries(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(0));

        File::create(temp_dir.path().join("1.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("2.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("3.txt")).unwrap();
        let result = entries(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(2));
    }
}
<|endoftext|>


<commit_before>//! which
//!
//! A Rust equivalent of Unix command `which(1)`.
//! # Example:
//!
//! To find which rustc executable binary is using:
//!
//! ```no_run
//! use which::which;
//! use std::path::PathBuf;
//!
//! let result = which::which("rustc").unwrap();
//! assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
//!
//! ```

#[cfg(windows)]
#[macro_use]
extern crate lazy_static;

mod checker;
mod error;
mod finder;
#[cfg(windows)]
mod helper;

#[cfg(feature = "regex")]
use regex::Regex;
#[cfg(feature = "regex")]
use std::borrow::Borrow;
use std::env;
use std::fmt;
use std::path;

use std::ffi::OsStr;

use crate::checker::{CompositeChecker, ExecutableChecker, ExistedChecker};
pub use crate::error::*;
use crate::finder::Finder;

/// Find a exectable binary's path by name.
///
/// If given an absolute path, returns it if the file exists and is executable.
///
/// If given a relative path, returns an absolute path to the file if
/// it exists and is executable.
///
/// If given a string without path separators, looks for a file named
/// `binary_name` at each directory in `$PATH` and if it finds an executable
/// file there, returns it.
///
/// # Example
///
/// ```no_run
/// use which::which;
/// use std::path::PathBuf;
///
/// let result = which::which("rustc").unwrap();
/// assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
///
/// ```
pub fn which<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> {
    which_all(binary_name).and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
}

/// Find all binaries with `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
pub fn which_all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = path::PathBuf>> {
    let cwd = env::current_dir().map_err(|_| Error::CannotGetCurrentDir)?;

    which_in_all(binary_name, env::var_os("PATH"), cwd)
}

/// Find all binaries matching a regular expression in a the system PATH.
///
/// # Arguments
///
/// * `regex` - A regular expression to match binaries with
///
/// # Examples
///
/// ```no_run
/// use regex::Regex;
/// use which::which;
/// use std::path::PathBuf;
///
/// let re = Regex::new(r"python\d$").unwrap();
/// let binaries: Vec<PathBuf> = which::which_re(re).unwrap().collect();
/// let python_paths = vec![PathBuf::from("/usr/bin/python2"), PathBuf::from("/usr/bin/python3")];
/// assert_eq!(binaries, python_paths);
/// ```
#[cfg(feature = "regex")]
pub fn which_re(regex: impl Borrow<Regex>) -> Result<impl Iterator<Item = path::PathBuf>> {
    which_re_in(regex, env::var_os("PATH"))
}

/// Find `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
pub fn which_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<path::PathBuf>
where
    T: AsRef<OsStr>,
    U: AsRef<OsStr>,
    V: AsRef<path::Path>,
{
    which_in_all(binary_name, paths, cwd)
        .and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
}

/// Find all binaries matching a regular expression in a list of paths.
///
/// # Arguments
///
/// * `regex` - A regular expression to match binaries with
/// * `paths` - A string containing the paths to search
///             (separated in the same way as the PATH environment variable)
///
/// # Examples
///
/// ```no_run
/// use regex::Regex;
/// use which::which;
/// use std::path::PathBuf;
///
/// let re = Regex::new(r"python\d$").unwrap();
/// let paths = Some("/usr/bin:/usr/local/bin");
/// let binaries: Vec<PathBuf> = which::which_re_in(re, paths).unwrap().collect();
/// let python_paths = vec![PathBuf::from("/usr/bin/python2"), PathBuf::from("/usr/bin/python3")];
/// assert_eq!(binaries, python_paths);
/// ```
#[cfg(feature = "regex")]
pub fn which_re_in<T>(regex: impl Borrow<Regex>, paths: Option<T>) -> Result<impl Iterator<Item = path::PathBuf>>
where
    T: AsRef<OsStr>,
{
    let binary_checker = CompositeChecker::new()
        .add_checker(Box::new(ExistedChecker::new()))
        .add_checker(Box::new(ExecutableChecker::new()));

    let finder = Finder::new();

    finder.find_re(regex, paths, binary_checker)
}

/// Find all binaries with `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
pub fn which_in_all<T, U, V>(
    binary_name: T,
    paths: Option<U>,
    cwd: V,
) -> Result<impl Iterator<Item = path::PathBuf>>
where
    T: AsRef<OsStr>,
    U: AsRef<OsStr>,
    V: AsRef<path::Path>,
{
    let binary_checker = CompositeChecker::new()
        .add_checker(Box::new(ExistedChecker::new()))
        .add_checker(Box::new(ExecutableChecker::new()));

    let finder = Finder::new();

    finder.find(binary_name, paths, cwd, binary_checker)
}

/// An owned, immutable wrapper around a `PathBuf` containing the path of an executable.
///
/// The constructed `PathBuf` is the output of `which` or `which_in`, but `which::Path` has the
/// advantage of being a type distinct from `std::path::Path` and `std::path::PathBuf`.
///
/// It can be beneficial to use `which::Path` instead of `std::path::Path` when you want the type
/// system to enforce the need for a path that exists and points to a binary that is executable.
///
/// Since `which::Path` implements `Deref` for `std::path::Path`, all methods on `&std::path::Path`
/// are also available to `&which::Path` values.
#[derive(Clone, PartialEq)]
pub struct Path {
    inner: path::PathBuf,
}

impl Path {
    /// Returns the path of an executable binary by name.
    ///
    /// This calls `which` and maps the result into a `Path`.
    pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<Path> {
        which(binary_name).map(|inner| Path { inner })
    }

    /// Returns the paths of all executable binaries by a name.
    ///
    /// this calls `which_all` and maps the results into `Path`s.
    pub fn all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = Path>> {
        which_all(binary_name).map(|inner| inner.map(|inner| Path { inner }))
    }

    /// Returns the path of an executable binary by name in the path list `paths` and using the
    /// current working directory `cwd` to resolve relative paths.
    ///
    /// This calls `which_in` and maps the result into a `Path`.
    pub fn new_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<Path>
    where
        T: AsRef<OsStr>,
        U: AsRef<OsStr>,
        V: AsRef<path::Path>,
    {
        which_in(binary_name, paths, cwd).map(|inner| Path { inner })
    }

    /// Returns all paths of an executable binary by name in the path list `paths` and using the
    /// current working directory `cwd` to resolve relative paths.
    ///
    /// This calls `which_in_all` and maps the results into a `Path`.
    pub fn all_in<T, U, V>(
        binary_name: T,
        paths: Option<U>,
        cwd: V,
    ) -> Result<impl Iterator<Item = Path>>
    where
        T: AsRef<OsStr>,
        U: AsRef<OsStr>,
        V: AsRef<path::Path>,
    {
        which_in_all(binary_name, paths, cwd).map(|inner| inner.map(|inner| Path { inner }))
    }

    /// Returns a reference to a `std::path::Path`.
    pub fn as_path(&self) -> &path::Path {
        self.inner.as_path()
    }

    /// Consumes the `which::Path`, yielding its underlying `std::path::PathBuf`.
    pub fn into_path_buf(self) -> path::PathBuf {
        self.inner
    }
}

impl fmt::Debug for Path {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.inner, f)
    }
}

impl std::ops::Deref for Path {
    type Target = path::Path;

    fn deref(&self) -> &path::Path {
        self.inner.deref()
    }
}

impl AsRef<path::Path> for Path {
    fn as_ref(&self) -> &path::Path {
        self.as_path()
    }
}

impl AsRef<OsStr> for Path {
    fn as_ref(&self) -> &OsStr {
        self.as_os_str()
    }
}

impl Eq for Path {}

impl PartialEq<path::PathBuf> for Path {
    fn eq(&self, other: &path::PathBuf) -> bool {
        self.inner == *other
    }
}

impl PartialEq<Path> for path::PathBuf {
    fn eq(&self, other: &Path) -> bool {
        *self == other.inner
    }
}

/// An owned, immutable wrapper around a `PathBuf` containing the _canonical_ path of an
/// executable.
///
/// The constructed `PathBuf` is the result of `which` or `which_in` followed by
/// `Path::canonicalize`, but `CanonicalPath` has the advantage of being a type distinct from
/// `std::path::Path` and `std::path::PathBuf`.
///
/// It can be beneficial to use `CanonicalPath` instead of `std::path::Path` when you want the type
/// system to enforce the need for a path that exists, points to a binary that is executable, is
/// absolute, has all components normalized, and has all symbolic links resolved
///
/// Since `CanonicalPath` implements `Deref` for `std::path::Path`, all methods on
/// `&std::path::Path` are also available to `&CanonicalPath` values.
#[derive(Clone, PartialEq)]
pub struct CanonicalPath {
    inner: path::PathBuf,
}

impl CanonicalPath {
    /// Returns the canonical path of an executable binary by name.
    ///
    /// This calls `which` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
    pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<CanonicalPath> {
        which(binary_name)
            .and_then(|p| p.canonicalize().map_err(|_| Error::CannotCanonicalize))
            .map(|inner| CanonicalPath { inner })
    }

    /// Returns the canonical paths of an executable binary by name.
    ///
    /// This calls `which_all` and `Path::canonicalize` and maps the results into `CanonicalPath`s.
    pub fn all<T: AsRef<OsStr>>(
        binary_name: T,
    ) -> Result<impl Iterator<Item = Result<CanonicalPath>>> {
        which_all(binary_name).map(|inner| {
            inner.map(|inner| {
                inner
                    .canonicalize()
                    .map_err(|_| Error::CannotCanonicalize)
                    .map(|inner| CanonicalPath { inner })
            })
        })
    }

    /// Returns the canonical path of an executable binary by name in the path list `paths` and
    /// using the current working directory `cwd` to resolve relative paths.
    ///
    /// This calls `which_in` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
    pub fn new_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<CanonicalPath>
    where
        T: AsRef<OsStr>,
        U: AsRef<OsStr>,
        V: AsRef<path::Path>,
    {
        which_in(binary_name, paths, cwd)
            .and_then(|p| p.canonicalize().map_err(|_| Error::CannotCanonicalize))
            .map(|inner| CanonicalPath { inner })
    }

    /// Returns all of the canonical paths of an executable binary by name in the path list `paths` and
    /// using the current working directory `cwd` to resolve relative paths.
    ///
    /// This calls `which_in_all` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
    pub fn all_in<T, U, V>(
        binary_name: T,
        paths: Option<U>,
        cwd: V,
    ) -> Result<impl Iterator<Item = Result<CanonicalPath>>>
    where
        T: AsRef<OsStr>,
        U: AsRef<OsStr>,
        V: AsRef<path::Path>,
    {
        which_in_all(binary_name, paths, cwd).map(|inner| {
            inner.map(|inner| {
                inner
                    .canonicalize()
                    .map_err(|_| Error::CannotCanonicalize)
                    .map(|inner| CanonicalPath { inner })
            })
        })
    }

    /// Returns a reference to a `std::path::Path`.
    pub fn as_path(&self) -> &path::Path {
        self.inner.as_path()
    }

    /// Consumes the `which::CanonicalPath`, yielding its underlying `std::path::PathBuf`.
    pub fn into_path_buf(self) -> path::PathBuf {
        self.inner
    }
}

impl fmt::Debug for CanonicalPath {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.inner, f)
    }
}

impl std::ops::Deref for CanonicalPath {
    type Target = path::Path;

    fn deref(&self) -> &path::Path {
        self.inner.deref()
    }
}

impl AsRef<path::Path> for CanonicalPath {
    fn as_ref(&self) -> &path::Path {
        self.as_path()
    }
}

impl AsRef<OsStr> for CanonicalPath {
    fn as_ref(&self) -> &OsStr {
        self.as_os_str()
    }
}

impl Eq for CanonicalPath {}

impl PartialEq<path::PathBuf> for CanonicalPath {
    fn eq(&self, other: &path::PathBuf) -> bool {
        self.inner == *other
    }
}

impl PartialEq<CanonicalPath> for path::PathBuf {
    fn eq(&self, other: &CanonicalPath) -> bool {
        *self == other.inner
    }
}
<commit_msg>Add feature requirements and example to which_re documentation<commit_after>//! which
//!
//! A Rust equivalent of Unix command `which(1)`.
//! # Example:
//!
//! To find which rustc executable binary is using:
//!
//! ```no_run
//! use which::which;
//! use std::path::PathBuf;
//!
//! let result = which("rustc").unwrap();
//! assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
//!
//! ```

#[cfg(windows)]
#[macro_use]
extern crate lazy_static;

mod checker;
mod error;
mod finder;
#[cfg(windows)]
mod helper;

#[cfg(feature = "regex")]
use regex::Regex;
#[cfg(feature = "regex")]
use std::borrow::Borrow;
use std::env;
use std::fmt;
use std::path;

use std::ffi::OsStr;

use crate::checker::{CompositeChecker, ExecutableChecker, ExistedChecker};
pub use crate::error::*;
use crate::finder::Finder;

/// Find a exectable binary's path by name.
///
/// If given an absolute path, returns it if the file exists and is executable.
///
/// If given a relative path, returns an absolute path to the file if
/// it exists and is executable.
///
/// If given a string without path separators, looks for a file named
/// `binary_name` at each directory in `$PATH` and if it finds an executable
/// file there, returns it.
///
/// # Example
///
/// ```no_run
/// use which::which;
/// use std::path::PathBuf;
///
/// let result = which::which("rustc").unwrap();
/// assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
///
/// ```
pub fn which<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> {
    which_all(binary_name).and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
}

/// Find all binaries with `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
pub fn which_all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = path::PathBuf>> {
    let cwd = env::current_dir().map_err(|_| Error::CannotGetCurrentDir)?;

    which_in_all(binary_name, env::var_os("PATH"), cwd)
}

/// Find all binaries matching a regular expression in a the system PATH.
///
/// Only available when feature `regex` is enabled.
///
/// # Arguments
///
/// * `regex` - A regular expression to match binaries with
///
/// # Examples
///
/// Find Python executables:
///
/// ```no_run
/// use regex::Regex;
/// use which::which;
/// use std::path::PathBuf;
///
/// let re = Regex::new(r"python\d$").unwrap();
/// let binaries: Vec<PathBuf> = which::which_re(re).unwrap().collect();
/// let python_paths = vec![PathBuf::from("/usr/bin/python2"), PathBuf::from("/usr/bin/python3")];
/// assert_eq!(binaries, python_paths);
/// ```
///
/// Find all cargo subcommand executables on the path:
///
/// ```
/// use which::which_re;
/// use regex::Regex;
///
/// which_re(Regex::new("^cargo-.*").unwrap()).unwrap()
///     .for_each(|pth| println!("{}", pth.to_string_lossy()));
/// ```
#[cfg(feature = "regex")]
pub fn which_re(regex: impl Borrow<Regex>) -> Result<impl Iterator<Item = path::PathBuf>> {
    which_re_in(regex, env::var_os("PATH"))
}

/// Find `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
pub fn which_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<path::PathBuf>
where
    T: AsRef<OsStr>,
    U: AsRef<OsStr>,
    V: AsRef<path::Path>,
{
    which_in_all(binary_name, paths, cwd)
        .and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
}

/// Find all binaries matching a regular expression in a list of paths.
///
/// Only available when feature `regex` is enabled.
///
/// # Arguments
///
/// * `regex` - A regular expression to match binaries with
/// * `paths` - A string containing the paths to search
///             (separated in the same way as the PATH environment variable)
///
/// # Examples
///
/// ```no_run
/// use regex::Regex;
/// use which::which;
/// use std::path::PathBuf;
///
/// let re = Regex::new(r"python\d$").unwrap();
/// let paths = Some("/usr/bin:/usr/local/bin");
/// let binaries: Vec<PathBuf> = which::which_re_in(re, paths).unwrap().collect();
/// let python_paths = vec![PathBuf::from("/usr/bin/python2"), PathBuf::from("/usr/bin/python3")];
/// assert_eq!(binaries, python_paths);
/// ```
#[cfg(feature = "regex")]
pub fn which_re_in<T>(regex: impl Borrow<Regex>, paths: Option<T>) -> Result<impl Iterator<Item = path::PathBuf>>
where
    T: AsRef<OsStr>,
{
    let binary_checker = CompositeChecker::new()
        .add_checker(Box::new(ExistedChecker::new()))
        .add_checker(Box::new(ExecutableChecker::new()));

    let finder = Finder::new();

    finder.find_re(regex, paths, binary_checker)
}

/// Find all binaries with `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
pub fn which_in_all<T, U, V>(
    binary_name: T,
    paths: Option<U>,
    cwd: V,
) -> Result<impl Iterator<Item = path::PathBuf>>
where
    T: AsRef<OsStr>,
    U: AsRef<OsStr>,
    V: AsRef<path::Path>,
{
    let binary_checker = CompositeChecker::new()
        .add_checker(Box::new(ExistedChecker::new()))
        .add_checker(Box::new(ExecutableChecker::new()));

    let finder = Finder::new();

    finder.find(binary_name, paths, cwd, binary_checker)
}

/// An owned, immutable wrapper around a `PathBuf` containing the path of an executable.
///
/// The constructed `PathBuf` is the output of `which` or `which_in`, but `which::Path` has the
/// advantage of being a type distinct from `std::path::Path` and `std::path::PathBuf`.
///
/// It can be beneficial to use `which::Path` instead of `std::path::Path` when you want the type
/// system to enforce the need for a path that exists and points to a binary that is executable.
///
/// Since `which::Path` implements `Deref` for `std::path::Path`, all methods on `&std::path::Path`
/// are also available to `&which::Path` values.
#[derive(Clone, PartialEq)]
pub struct Path {
    inner: path::PathBuf,
}

impl Path {
    /// Returns the path of an executable binary by name.
    ///
    /// This calls `which` and maps the result into a `Path`.
    pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<Path> {
        which(binary_name).map(|inner| Path { inner })
    }

    /// Returns the paths of all executable binaries by a name.
    ///
    /// this calls `which_all` and maps the results into `Path`s.
    pub fn all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = Path>> {
        which_all(binary_name).map(|inner| inner.map(|inner| Path { inner }))
    }

    /// Returns the path of an executable binary by name in the path list `paths` and using the
    /// current working directory `cwd` to resolve relative paths.
    ///
    /// This calls `which_in` and maps the result into a `Path`.
    pub fn new_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<Path>
    where
        T: AsRef<OsStr>,
        U: AsRef<OsStr>,
        V: AsRef<path::Path>,
    {
        which_in(binary_name, paths, cwd).map(|inner| Path { inner })
    }

    /// Returns all paths of an executable binary by name in the path list `paths` and using the
    /// current working directory `cwd` to resolve relative paths.
    ///
    /// This calls `which_in_all` and maps the results into a `Path`.
    pub fn all_in<T, U, V>(
        binary_name: T,
        paths: Option<U>,
        cwd: V,
    ) -> Result<impl Iterator<Item = Path>>
    where
        T: AsRef<OsStr>,
        U: AsRef<OsStr>,
        V: AsRef<path::Path>,
    {
        which_in_all(binary_name, paths, cwd).map(|inner| inner.map(|inner| Path { inner }))
    }

    /// Returns a reference to a `std::path::Path`.
    pub fn as_path(&self) -> &path::Path {
        self.inner.as_path()
    }

    /// Consumes the `which::Path`, yielding its underlying `std::path::PathBuf`.
    pub fn into_path_buf(self) -> path::PathBuf {
        self.inner
    }
}

impl fmt::Debug for Path {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.inner, f)
    }
}

impl std::ops::Deref for Path {
    type Target = path::Path;

    fn deref(&self) -> &path::Path {
        self.inner.deref()
    }
}

impl AsRef<path::Path> for Path {
    fn as_ref(&self) -> &path::Path {
        self.as_path()
    }
}

impl AsRef<OsStr> for Path {
    fn as_ref(&self) -> &OsStr {
        self.as_os_str()
    }
}

impl Eq for Path {}

impl PartialEq<path::PathBuf> for Path {
    fn eq(&self, other: &path::PathBuf) -> bool {
        self.inner == *other
    }
}

impl PartialEq<Path> for path::PathBuf {
    fn eq(&self, other: &Path) -> bool {
        *self == other.inner
    }
}

/// An owned, immutable wrapper around a `PathBuf` containing the _canonical_ path of an
/// executable.
///
/// The constructed `PathBuf` is the result of `which` or `which_in` followed by
/// `Path::canonicalize`, but `CanonicalPath` has the advantage of being a type distinct from
/// `std::path::Path` and `std::path::PathBuf`.
///
/// It can be beneficial to use `CanonicalPath` instead of `std::path::Path` when you want the type
/// system to enforce the need for a path that exists, points to a binary that is executable, is
/// absolute, has all components normalized, and has all symbolic links resolved
///
/// Since `CanonicalPath` implements `Deref` for `std::path::Path`, all methods on
/// `&std::path::Path` are also available to `&CanonicalPath` values.
#[derive(Clone, PartialEq)]
pub struct CanonicalPath {
    inner: path::PathBuf,
}

impl CanonicalPath {
    /// Returns the canonical path of an executable binary by name.
    ///
    /// This calls `which` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
    pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<CanonicalPath> {
        which(binary_name)
            .and_then(|p| p.canonicalize().map_err(|_| Error::CannotCanonicalize))
            .map(|inner| CanonicalPath { inner })
    }

    /// Returns the canonical paths of an executable binary by name.
    ///
    /// This calls `which_all` and `Path::canonicalize` and maps the results into `CanonicalPath`s.
    pub fn all<T: AsRef<OsStr>>(
        binary_name: T,
    ) -> Result<impl Iterator<Item = Result<CanonicalPath>>> {
        which_all(binary_name).map(|inner| {
            inner.map(|inner| {
                inner
                    .canonicalize()
                    .map_err(|_| Error::CannotCanonicalize)
                    .map(|inner| CanonicalPath { inner })
            })
        })
    }

    /// Returns the canonical path of an executable binary by name in the path list `paths` and
    /// using the current working directory `cwd` to resolve relative paths.
    ///
    /// This calls `which_in` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
    pub fn new_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<CanonicalPath>
    where
        T: AsRef<OsStr>,
        U: AsRef<OsStr>,
        V: AsRef<path::Path>,
    {
        which_in(binary_name, paths, cwd)
            .and_then(|p| p.canonicalize().map_err(|_| Error::CannotCanonicalize))
            .map(|inner| CanonicalPath { inner })
    }

    /// Returns all of the canonical paths of an executable binary by name in the path list `paths` and
    /// using the current working directory `cwd` to resolve relative paths.
    ///
    /// This calls `which_in_all` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
    pub fn all_in<T, U, V>(
        binary_name: T,
        paths: Option<U>,
        cwd: V,
    ) -> Result<impl Iterator<Item = Result<CanonicalPath>>>
    where
        T: AsRef<OsStr>,
        U: AsRef<OsStr>,
        V: AsRef<path::Path>,
    {
        which_in_all(binary_name, paths, cwd).map(|inner| {
            inner.map(|inner| {
                inner
                    .canonicalize()
                    .map_err(|_| Error::CannotCanonicalize)
                    .map(|inner| CanonicalPath { inner })
            })
        })
    }

    /// Returns a reference to a `std::path::Path`.
    pub fn as_path(&self) -> &path::Path {
        self.inner.as_path()
    }

    /// Consumes the `which::CanonicalPath`, yielding its underlying `std::path::PathBuf`.
    pub fn into_path_buf(self) -> path::PathBuf {
        self.inner
    }
}

impl fmt::Debug for CanonicalPath {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Debug::fmt(&self.inner, f)
    }
}

impl std::ops::Deref for CanonicalPath {
    type Target = path::Path;

    fn deref(&self) -> &path::Path {
        self.inner.deref()
    }
}

impl AsRef<path::Path> for CanonicalPath {
    fn as_ref(&self) -> &path::Path {
        self.as_path()
    }
}

impl AsRef<OsStr> for CanonicalPath {
    fn as_ref(&self) -> &OsStr {
        self.as_os_str()
    }
}

impl Eq for CanonicalPath {}

impl PartialEq<path::PathBuf> for CanonicalPath {
    fn eq(&self, other: &path::PathBuf) -> bool {
        self.inner == *other
    }
}

impl PartialEq<CanonicalPath> for path::PathBuf {
    fn eq(&self, other: &CanonicalPath) -> bool {
        *self == other.inner
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2014, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

#![feature(globs)]

#![crate_name="capnpc"]
#![crate_type = "lib"]

extern crate capnp;

pub mod schema_capnp;
pub mod codegen;

pub fn compile(prefix : Path, files : &[Path]) {

    let out_dir = ::std::os::getenv("OUT_DIR").unwrap();

    let mut command = ::std::io::Command::new("capnp");
    command
        .arg("compile")
        .arg(format!("-o/bin/cat:{}", out_dir))
        .arg(format!("--src-prefix={}", prefix.display()));

    for file in files.iter() {
        command.arg(format!("{}", file.display()));
    }

    command.stdout(::std::io::process::CreatePipe(false, true));

    match command.spawn() {
        Ok(ref mut p) =>  {
            let mut child_stdout = p.stdout.take().unwrap();
            ::codegen::main(&mut child_stdout).unwrap();
        }
        Err(e) => {
            println!("could not start process: {}", e);
        }
    }

}

<commit_msg>need to change directory<commit_after>/*
 * Copyright (c) 2013-2014, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

#![feature(globs)]

#![crate_name="capnpc"]
#![crate_type = "lib"]

extern crate capnp;

pub mod schema_capnp;
pub mod codegen;

pub fn compile(prefix : Path, files : &[Path]) {
    let out_dir = Path::new(::std::os::getenv("OUT_DIR").unwrap());
    ::std::os::change_dir(&out_dir);

    let mut command = ::std::io::Command::new("capnp");
    command
        .arg("compile")
        .arg("-o/bin/cat")
        .arg(format!("--src-prefix={}", prefix.display()));

    for file in files.iter() {
        command.arg(format!("{}", file.display()));
    }

    command.stdout(::std::io::process::CreatePipe(false, true));

    match command.spawn() {
        Ok(ref mut p) =>  {
            let mut child_stdout = p.stdout.take().unwrap();
            ::codegen::main(&mut child_stdout).unwrap();
            p.wait().unwrap();
        }
        Err(e) => {
            panic!("could not start process: {}", e);
        }
    }

}

<|endoftext|>


<commit_before>use std::collections::BTreeMap;

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Token {
    Output,
    Input,
    Loop,
    EndLoop,
    Move(i32),
    Add(i32, i32),
    Set(i32, i32),
    MulCopy(i32, i32, i32),
    Scan(i32),
    LoadOut(i32, i32),
    LoadOutSet(i32),
    If(i32),
    EndIf,
}

pub fn parse(code: &str) -> Vec<Token> {
    let mut tokens = Vec::new();
    for i in code.chars() {
        match i {
            '+' => tokens.push(Token::Add(0, 1)),
            '-' => tokens.push(Token::Add(0, -1)),
            '>' => tokens.push(Token::Move(1)),
            '<' => tokens.push(Token::Move(-1)),
            '[' => tokens.push(Token::Loop),
            ']' => tokens.push(Token::EndLoop),
            ',' => tokens.push(Token::Input),
            '.' => {
                tokens.push(Token::LoadOut(0, 0));
                tokens.push(Token::Output);
            },
            _ => ()
        };
    }

    tokens
    }

pub fn optimize(tokens: Vec<Token>) -> Vec<Token> {
    let mut newtokens: Vec<Token> = Vec::new();
    let mut shift = 0;
    let mut do_output = false;
    // With HashMap, the order sometimes switches
    // in recursion, and the optimizer never exits.
    let mut adds: BTreeMap<i32, i32> = BTreeMap::new();
    let mut sets: BTreeMap<i32, i32> = BTreeMap::new();
    let mut pre_loop_sets: BTreeMap<i32, i32> = BTreeMap::new();

    for token in tokens.iter() {
        let mut prev_token = match newtokens.last() {
            Some(tok) => Some(*tok),
            None => None
        };
        if *token == Token::EndLoop && prev_token == Some(Token::Loop) && shift == 0 && adds.contains_key(&0) {
            if adds.len() == 1 {
                newtokens.pop(); // Remove Loop
                if !sets.is_empty() {
                    newtokens.push(Token::If(0));
                    for (offset, value) in sets.iter() {
                        newtokens.push(Token::Set(*offset, *value));
                    }
                    sets.clear();
                    newtokens.push(Token::Set(0, 0));
                    newtokens.push(Token::EndIf);
                } else {
                    sets.insert(0, 0);
                }
                pre_loop_sets.clear();
                adds.clear();
                continue
            } else if adds.get(&0) == Some(&-1) {
                newtokens.pop(); // Remove Loop
                if !sets.is_empty() {
                    newtokens.push(Token::If(0));
                    for (offset, value) in sets.iter() {
                        newtokens.push(Token::Set(*offset, *value));
                    }
                }
                for (offset, value) in adds.iter() {
                    if *offset != 0 {
                        let src = 0;
                        let dest = *offset;
                        let mul = *value;
                        if pre_loop_sets.contains_key(&src) {
                            let val = pre_loop_sets.get(&src).unwrap() * mul;
                            newtokens.push(Token::Add(dest, val));
                        } else {
                            newtokens.push(Token::MulCopy(src, dest, mul));
                        }
                    }
                }
                if !sets.is_empty() {
                    newtokens.push(Token::EndIf);
                }
                pre_loop_sets.clear();
                adds.clear();
                sets.clear();
                sets.insert(0, 0);
                continue
            }
        }

        match *token {
            Token::Loop =>
                for (offset, value) in sets.iter() {
                    pre_loop_sets.insert(*offset+shift, *value);
                },
            Token::Set(_, _) | Token::Add(_, _) | Token::Move(_) => {},
            _ => pre_loop_sets.clear()
        }

        match *token {
            Token::Set(_, _) | Token::Add(_, _) | Token::Move(_) | Token::LoadOut(_, _) | Token::LoadOutSet(_) | Token::Output => {},
            _ => {
               if do_output {
                   newtokens.push(Token::Output);
                   do_output = false;
               }

               for (offset, value) in sets.iter() {
                  newtokens.push(Token::Set(*offset, *value));
               }
               for (offset, value) in adds.iter() {
                  newtokens.push(Token::Add(*offset, *value));
               }
               sets.clear();
               adds.clear();
            }
        }

        if shift != 0 {
           match *token {
               Token::Loop | Token::Input | Token::Scan(_) => {
                   newtokens.push(Token::Move(shift));
                   shift = 0;
               },
               _ => {}
           }
        }

        match *token {
            Token::Set(mut offset, val) => {
                offset += shift;
                // Add before Set does nothing; remove it
                if adds.contains_key(&offset) {
                    adds.remove(&offset);
                }
                sets.insert(offset, val);
            },
            Token::Add(mut offset, mut val) => {
                offset += shift;
                if sets.contains_key(&offset) {
                    val = sets.get(&offset).unwrap() + val;
                    sets.insert(offset, val);
                } else {
                    val = adds.get(&offset).unwrap_or(&0) + val;
                    adds.insert(offset, val);
                }
            },
            Token::MulCopy(src, dest, mul) =>
                newtokens.push(Token::MulCopy(src+shift, dest+shift, mul)),
            // XXX Deal with shift in if, if those are ever generated
            Token::If(offset) =>
                newtokens.push(Token::If(offset+shift)),
            Token::Move(offset) =>
                shift += offset,
            Token::Output =>
                do_output = true,
            Token::LoadOut(mut offset, add) => {
                offset += shift;
                if sets.contains_key(&offset) {
                    newtokens.push(Token::LoadOutSet(sets.get(&offset).unwrap() + add));
                } else {
                    newtokens.push(Token::LoadOut(offset, adds.get(&offset).unwrap_or(&0) + add));
                }
            },
            Token::EndLoop => {
                prev_token = match newtokens.last() {
                            Some(tok) => Some(*tok),
                            None => None
                        };
                if prev_token == Some(Token::Loop) && shift != 0 && sets.is_empty() && adds.is_empty() {
                    newtokens.pop(); // Remove StartLoop
                    newtokens.push(Token::Scan(shift));
                } else {
                    if shift != 0 {
                        newtokens.push(Token::Move(shift));
                        shift = 0;
                    }
                    newtokens.push(Token::EndLoop);
                }
            },
            Token::EndIf | Token::LoadOutSet(_) | Token::Loop | Token::Input | Token::Scan(_) =>
                newtokens.push(*token),
        }
    }

    // Any remaining add/set/shift is ignored, as it would have no effect
    if do_output {
        newtokens.push(Token::Output);
    }

    // Optimize recursively
    if newtokens != tokens {
        optimize(newtokens)
    } else {
        newtokens
    }
}
<commit_msg>Fix one bug with the rust port<commit_after>use std::collections::BTreeMap;

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Token {
    Output,
    Input,
    Loop,
    EndLoop,
    Move(i32),
    Add(i32, i32),
    Set(i32, i32),
    MulCopy(i32, i32, i32),
    Scan(i32),
    LoadOut(i32, i32),
    LoadOutSet(i32),
    If(i32),
    EndIf,
}

pub fn parse(code: &str) -> Vec<Token> {
    let mut tokens = Vec::new();
    for i in code.chars() {
        match i {
            '+' => tokens.push(Token::Add(0, 1)),
            '-' => tokens.push(Token::Add(0, -1)),
            '>' => tokens.push(Token::Move(1)),
            '<' => tokens.push(Token::Move(-1)),
            '[' => tokens.push(Token::Loop),
            ']' => tokens.push(Token::EndLoop),
            ',' => tokens.push(Token::Input),
            '.' => {
                tokens.push(Token::LoadOut(0, 0));
                tokens.push(Token::Output);
            },
            _ => ()
        };
    }

    tokens
    }

pub fn optimize(tokens: Vec<Token>) -> Vec<Token> {
    let mut newtokens: Vec<Token> = Vec::new();
    let mut shift = 0;
    let mut do_output = false;
    // With HashMap, the order sometimes switches
    // in recursion, and the optimizer never exits.
    let mut adds: BTreeMap<i32, i32> = BTreeMap::new();
    let mut sets: BTreeMap<i32, i32> = BTreeMap::new();
    let mut pre_loop_sets: BTreeMap<i32, i32> = BTreeMap::new();

    for token in tokens.iter() {
        let mut prev_token = match newtokens.last() {
            Some(tok) => Some(*tok),
            None => None
        };
        if *token == Token::EndLoop && prev_token == Some(Token::Loop) && shift == 0 && adds.contains_key(&0) {
            if adds.len() == 1 {
                newtokens.pop(); // Remove Loop
                if !sets.is_empty() {
                    newtokens.push(Token::If(0));
                    for (offset, value) in sets.iter() {
                        newtokens.push(Token::Set(*offset, *value));
                    }
                    sets.clear();
                    newtokens.push(Token::Set(0, 0));
                    newtokens.push(Token::EndIf);
                } else {
                    sets.insert(0, 0);
                }
                pre_loop_sets.clear();
                adds.clear();
                continue
            } else if adds.get(&0) == Some(&-1) {
                newtokens.pop(); // Remove Loop
                if !sets.is_empty() {
                    newtokens.push(Token::If(0));
                    for (offset, value) in sets.iter() {
                        newtokens.push(Token::Set(*offset, *value));
                    }
                }
                for (offset, value) in adds.iter() {
                    if *offset != 0 {
                        let src = 0;
                        let dest = *offset;
                        let mul = *value;
                        if pre_loop_sets.contains_key(&src) {
                            let val = pre_loop_sets.get(&src).unwrap() * mul;
                            newtokens.push(Token::Add(dest, val));
                        } else {
                            newtokens.push(Token::MulCopy(src, dest, mul));
                        }
                    }
                }
                if !sets.is_empty() {
                    newtokens.push(Token::EndIf);
                }
                pre_loop_sets.clear();
                adds.clear();
                sets.clear();
                sets.insert(0, 0);
                continue
            }
        }

        match *token {
            Token::Loop =>
                for (offset, value) in sets.iter() {
                    pre_loop_sets.insert(*offset+shift, *value);
                },
            Token::Set(_, _) | Token::Add(_, _) | Token::Move(_) => {},
            _ => pre_loop_sets.clear()
        }

        match *token {
            Token::Set(_, _) | Token::Add(_, _) | Token::Move(_) | Token::LoadOut(_, _) | Token::LoadOutSet(_) | Token::Output => {},
            _ => {
               if do_output {
                   newtokens.push(Token::Output);
                   do_output = false;
               }

               for (offset, value) in sets.iter() {
                  newtokens.push(Token::Set(*offset, *value));
               }
               for (offset, value) in adds.iter() {
                  newtokens.push(Token::Add(*offset, *value));
               }
               sets.clear();
               adds.clear();
            }
        }

        if shift != 0 {
           match *token {
               Token::Loop | Token::Input | Token::Scan(_) => {
                   newtokens.push(Token::Move(shift));
                   shift = 0;
               },
               _ => {}
           }
        }

        match *token {
            Token::Set(mut offset, val) => {
                offset += shift;
                // Add before Set does nothing; remove it
                if adds.contains_key(&offset) {
                    adds.remove(&offset);
                }
                sets.insert(offset, val);
            },
            Token::Add(mut offset, mut val) => {
                offset += shift;
                if sets.contains_key(&offset) {
                    val = sets.get(&offset).unwrap() + val;
                    sets.insert(offset, val);
                } else {
                    val = adds.get(&offset).unwrap_or(&0) + val;
                    adds.insert(offset, val);
                }
            },
            Token::MulCopy(src, dest, mul) =>
                newtokens.push(Token::MulCopy(src+shift, dest+shift, mul)),
            // XXX Deal with shift in if, if those are ever generated
            Token::If(offset) =>
                newtokens.push(Token::If(offset+shift)),
            Token::Move(offset) =>
                shift += offset,
            Token::Output =>
                do_output = true,
            Token::LoadOut(mut offset, add) => {
                offset += shift;
                if sets.contains_key(&offset) {
                    newtokens.push(Token::LoadOutSet(sets.get(&offset).unwrap() + add));
                } else {
                    newtokens.push(Token::LoadOut(offset, adds.get(&offset).unwrap_or(&0) + add));
                }
            },
            Token::EndLoop => {
                prev_token = match newtokens.last() {
                            Some(tok) => Some(*tok),
                            None => None
                        };
                if prev_token == Some(Token::Loop) && shift != 0 && sets.is_empty() && adds.is_empty() {
                    newtokens.pop(); // Remove StartLoop
                    newtokens.push(Token::Scan(shift));
                } else {
                    if shift != 0 {
                        newtokens.push(Token::Move(shift));
                    }
                    shift = 0;
                    newtokens.push(Token::EndLoop);
                }
            },
            Token::EndIf | Token::LoadOutSet(_) | Token::Loop | Token::Input | Token::Scan(_) =>
                newtokens.push(*token),
        }
    }

    // Any remaining add/set/shift is ignored, as it would have no effect
    if do_output {
        newtokens.push(Token::Output);
    }

    // Optimize recursively
    if newtokens != tokens {
        optimize(newtokens)
    } else {
        newtokens
    }
}
<|endoftext|>