<commit_before>#![crate_name = "sum"]

/*
* This file is part of the uutils coreutils package.
*
* (c) T. Jameson Little <t.jameson.little@gmail.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

extern crate getopts;
extern crate libc;

#[macro_use]
extern crate uucore;

use std::fs::File;
use std::io::{Read, Result, stdin, Write};
use std::path::Path;

static NAME: &'static str = "sum";
static VERSION: &'static str = "1.0.0";

fn bsd_sum(mut reader: Box<Read>) -> (usize, u16) {
    let mut buf = [0; 1024];
    let mut blocks_read = 0;
    let mut checksum: u16 = 0;
    loop {
        match reader.read(&mut buf) {
            Ok(n) if n != 0 => {
                blocks_read += 1;
                for &byte in buf[..n].iter() {
                    checksum = (checksum >> 1) + ((checksum & 1) << 15);
                    checksum += byte as u16;
                }
            },
            _ => break,
        }
    }

    (blocks_read, checksum)
}

fn sysv_sum(mut reader: Box<Read>) -> (usize, u16) {
    let mut buf = [0; 512];
    let mut blocks_read = 0;
    let mut ret = 0;

    loop {
        match reader.read(&mut buf) {
            Ok(n) if n != 0 => {
                blocks_read += 1;
                for &byte in buf[..n].iter() {
                    ret += byte as u32;
                }
            },
            _ => break,
        }
    }

    ret = (ret & 0xffff) + (ret >> 16);
    ret = (ret & 0xffff) + (ret >> 16);

    (blocks_read, ret as u16)
}

fn open(name: &str) -> Result<Box<Read>> {
    match name {
        "-" => Ok(Box::new(stdin()) as Box<Read>),
        _ => {
            let f = try!(File::open(&Path::new(name)));
            Ok(Box::new(f) as Box<Read>)
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();

    opts.optflag("r", "", "use the BSD compatible algorithm (default)");
    opts.optflag("s", "sysv", "use System V compatible algorithm");
    opts.optflag("h", "help", "show this help message");
    opts.optflag("v", "version", "print the version and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };

    if matches.opt_present("help") {
        let msg = format!("{0} {1}

Usage:
  {0} [OPTION]... [FILE]...

Checksum and count the blocks in a file.", NAME, VERSION);
        println!("{}\nWith no FILE, or when  FILE is -, read standard input.", opts.usage(&msg));
        return 0;
    }
    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    let sysv = matches.opt_present("sysv");

    let files = if matches.free.is_empty() {
        vec!["-".to_string()]
    } else {
        matches.free
    };

    let print_names = if sysv {
        files.len() > 1 || files[0] != "-"
    } else {
        files.len() > 1
    };

    for file in files.iter() {
        let reader = match open(file) {
            Ok(f) => f,
            _ => crash!(1, "unable to open file")
        };
        let (blocks, sum) = if sysv {
            sysv_sum(reader)
        } else {
            bsd_sum(reader)
        };

        if print_names {
            println!("{} {} {}", sum, blocks, file);
        } else {
            println!("{} {}", sum, blocks);
        }
    }

    0
}

#[allow(dead_code)]
fn main() {
    std::process::exit(uumain(std::env::args().collect()));
}
<commit_msg>sum: use wrapping_add to avoid overflow panics<commit_after>#![crate_name = "sum"]

/*
* This file is part of the uutils coreutils package.
*
* (c) T. Jameson Little <t.jameson.little@gmail.com>
*
* For the full copyright and license information, please view the LICENSE file
* that was distributed with this source code.
*/

extern crate getopts;
extern crate libc;

#[macro_use]
extern crate uucore;

use std::fs::File;
use std::io::{Read, Result, stdin, Write};
use std::path::Path;

static NAME: &'static str = "sum";
static VERSION: &'static str = "1.0.0";

fn bsd_sum(mut reader: Box<Read>) -> (usize, u16) {
    let mut buf = [0; 1024];
    let mut blocks_read = 0;
    let mut checksum: u16 = 0;
    loop {
        match reader.read(&mut buf) {
            Ok(n) if n != 0 => {
                blocks_read += 1;
                for &byte in buf[..n].iter() {
                    checksum = (checksum >> 1) + ((checksum & 1) << 15);
                    checksum = checksum.wrapping_add(byte as u16);
                }
            },
            _ => break,
        }
    }

    (blocks_read, checksum)
}

fn sysv_sum(mut reader: Box<Read>) -> (usize, u16) {
    let mut buf = [0; 512];
    let mut blocks_read = 0;
    let mut ret = 0u32;

    loop {
        match reader.read(&mut buf) {
            Ok(n) if n != 0 => {
                blocks_read += 1;
                for &byte in buf[..n].iter() {
                    ret = ret.wrapping_add(byte as u32);
                }
            },
            _ => break,
        }
    }

    ret = (ret & 0xffff) + (ret >> 16);
    ret = (ret & 0xffff) + (ret >> 16);

    (blocks_read, ret as u16)
}

fn open(name: &str) -> Result<Box<Read>> {
    match name {
        "-" => Ok(Box::new(stdin()) as Box<Read>),
        _ => {
            let f = try!(File::open(&Path::new(name)));
            Ok(Box::new(f) as Box<Read>)
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();

    opts.optflag("r", "", "use the BSD compatible algorithm (default)");
    opts.optflag("s", "sysv", "use System V compatible algorithm");
    opts.optflag("h", "help", "show this help message");
    opts.optflag("v", "version", "print the version and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };

    if matches.opt_present("help") {
        let msg = format!("{0} {1}

Usage:
  {0} [OPTION]... [FILE]...

Checksum and count the blocks in a file.", NAME, VERSION);
        println!("{}\nWith no FILE, or when  FILE is -, read standard input.", opts.usage(&msg));
        return 0;
    }
    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    let sysv = matches.opt_present("sysv");

    let files = if matches.free.is_empty() {
        vec!["-".to_string()]
    } else {
        matches.free
    };

    let print_names = if sysv {
        files.len() > 1 || files[0] != "-"
    } else {
        files.len() > 1
    };

    for file in files.iter() {
        let reader = match open(file) {
            Ok(f) => f,
            _ => crash!(1, "unable to open file")
        };
        let (blocks, sum) = if sysv {
            sysv_sum(reader)
        } else {
            bsd_sum(reader)
        };

        if print_names {
            println!("{} {} {}", sum, blocks, file);
        } else {
            println!("{} {}", sum, blocks);
        }
    }

    0
}

#[allow(dead_code)]
fn main() {
    std::process::exit(uumain(std::env::args().collect()));
}
<|endoftext|>


<commit_before>use phf;
use std::ascii::AsciiExt;
use super::namepart::NamePart;

static VOWELLESS_SURNAMES: [&'static str; 4] = ["Ng", "Lv", "Mtz", "Hdz"];

static SINGLE_LETTER_CONJUNCTIONS: [&'static str; 4] = ["e", "y", "E", "Y"];

static SURNAME_PREFIXES: phf::Set<&'static str> = phf_set! {
    "af",
    "av",
    "da",
    "das",
    "dal",
    "de",
    "del",
    "dela",
    "dei",
    "der",
    "di",
    "dí",
    "do",
    "dos",
    "du",
    "la",
    "le",
    "na",
    "ter",
    "van",
    "vel",
    "von",
    "zu",
    "Abu",
    "Abd",
    "Af",
    "Al",
    "Ap",
    "Av",
    "Aw",
    "Bar",
    "Ben",
    "Bon",
    "Bin",
    "Da",
    "Das",
    "Dal",
    "De",
    "Dei",
    "Del",
    "Dela",
    "Den",
    "Der",
    "Di",
    "Dí",
    "Do",
    "Dos",
    "Du",
    "Ibn",
    "La",
    "Le",
    "Lo",
    "Na",
    "Nic",
    "San",
    "Santa",
    "St",
    "Ste",
    "Ter",
    "Van",
    "Vel",
    "Von",
    "Zu",
    "Zum",
};

pub fn is_vowelless_surname(word: &str, use_capitalization: bool) -> bool {
    if use_capitalization {
        VOWELLESS_SURNAMES.contains(&word)
    } else {
        VOWELLESS_SURNAMES.iter().any(|surname| surname.eq_ignore_ascii_case(word))
    }
}

pub fn find_surname_index(words: &[NamePart]) -> usize {
    if words.len() < 2 {
        return 0;
    }

    for (i, word) in words[0..words.len() - 1].iter().enumerate() {
        if SURNAME_PREFIXES.contains(&*word.namecased) {
            return i;
        }
    }

    for (i, word) in words[1..words.len() - 1].iter().enumerate() {
        if SINGLE_LETTER_CONJUNCTIONS.contains(&word.word) {
            // We found what looks like a conjunction in a Spanish or Portuguese
            // style surname (e.g. "Romero y Galdámez" or "Dato e Iradier"), so
            // the previous word was the start of the surname
            let orig_i = i + 1; // Index into original array
            if !words[orig_i - 1].is_initials() && !words[orig_i + 1].is_initials() {
                return orig_i - 1;
            }
        }
    }

    // Default case: just assume the last word is the surname
    words.len() - 1
}
<commit_msg>Add El as last-name particle<commit_after>use phf;
use std::ascii::AsciiExt;
use super::namepart::NamePart;

static VOWELLESS_SURNAMES: [&'static str; 4] = ["Ng", "Lv", "Mtz", "Hdz"];

static SINGLE_LETTER_CONJUNCTIONS: [&'static str; 4] = ["e", "y", "E", "Y"];

static SURNAME_PREFIXES: phf::Set<&'static str> = phf_set! {
    "af",
    "av",
    "da",
    "das",
    "dal",
    "de",
    "del",
    "dela",
    "dei",
    "der",
    "di",
    "dí",
    "do",
    "dos",
    "du",
    "la",
    "le",
    "na",
    "ter",
    "van",
    "vel",
    "von",
    "zu",
    "Abu",
    "Abd",
    "Af",
    "Al",
    "Ap",
    "Av",
    "Aw",
    "Bar",
    "Ben",
    "Bon",
    "Bin",
    "Da",
    "Das",
    "Dal",
    "De",
    "Dei",
    "Del",
    "Dela",
    "Den",
    "Der",
    "Di",
    "Dí",
    "Do",
    "Dos",
    "Du",
    "El",
    "Ibn",
    "La",
    "Le",
    "Lo",
    "Na",
    "Nic",
    "San",
    "Santa",
    "St",
    "Ste",
    "Ter",
    "Van",
    "Vel",
    "Von",
    "Zu",
    "Zum",
};

pub fn is_vowelless_surname(word: &str, use_capitalization: bool) -> bool {
    if use_capitalization {
        VOWELLESS_SURNAMES.contains(&word)
    } else {
        VOWELLESS_SURNAMES.iter().any(|surname| surname.eq_ignore_ascii_case(word))
    }
}

pub fn find_surname_index(words: &[NamePart]) -> usize {
    if words.len() < 2 {
        return 0;
    }

    for (i, word) in words[0..words.len() - 1].iter().enumerate() {
        if SURNAME_PREFIXES.contains(&*word.namecased) {
            return i;
        }
    }

    for (i, word) in words[1..words.len() - 1].iter().enumerate() {
        if SINGLE_LETTER_CONJUNCTIONS.contains(&word.word) {
            // We found what looks like a conjunction in a Spanish or Portuguese
            // style surname (e.g. "Romero y Galdámez" or "Dato e Iradier"), so
            // the previous word was the start of the surname
            let orig_i = i + 1; // Index into original array
            if !words[orig_i - 1].is_initials() && !words[orig_i + 1].is_initials() {
                return orig_i - 1;
            }
        }
    }

    // Default case: just assume the last word is the surname
    words.len() - 1
}
<|endoftext|>


<commit_before>use crate::config::Color;
use crate::generate_tracemap;
use crate::statemachine::{create_state_machine, TestState};
use crate::traces::*;
use crate::{Config, EventLog, LineAnalysis, RunError, TestBinary, TraceEngine};
use std::collections::HashMap;
use std::env;
use std::ffi::OsStr;
use std::fmt;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Child, Command};
use tracing::{error, info, trace_span};

/// Handle to a test currently either PID or a `std::process::Child`
pub enum TestHandle {
    Id(ProcessHandle),
    Process(RunningProcessHandle),
}

pub struct RunningProcessHandle {
    /// Used to map coverage counters to line numbers
    pub(crate) path: PathBuf,
    /// Get the exit status to work out if tests have passed
    pub(crate) child: Child,
    /// maintain a list of existing profraws in the project root to avoid picking up old results
    pub(crate) existing_profraws: Vec<PathBuf>,
}

impl RunningProcessHandle {
    pub fn new(path: PathBuf, cmd: &mut Command, config: &Config) -> Result<Self, RunError> {
        let child = cmd.spawn()?;
        let existing_profraws = fs::read_dir(config.root())?
            .into_iter()
            .filter_map(|x| x.ok())
            .filter(|x| x.path().is_file() && x.path().extension() == Some(OsStr::new("profraw")))
            .map(|x| x.path())
            .collect();

        Ok(Self {
            path,
            child,
            existing_profraws,
        })
    }
}

impl fmt::Display for TestHandle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TestHandle::Id(id) => write!(f, "{}", id),
            TestHandle::Process(c) => write!(f, "{}", c.child.id()),
        }
    }
}

impl From<ProcessHandle> for TestHandle {
    fn from(handle: ProcessHandle) -> Self {
        Self::Id(handle)
    }
}

impl From<RunningProcessHandle> for TestHandle {
    fn from(handle: RunningProcessHandle) -> Self {
        Self::Process(handle)
    }
}

pub fn get_test_coverage(
    test: &TestBinary,
    analysis: &HashMap<PathBuf, LineAnalysis>,
    config: &Config,
    ignored: bool,
    logger: &Option<EventLog>,
) -> Result<Option<(TraceMap, i32)>, RunError> {
    let handle = launch_test(test, config, ignored, logger)?;
    if let Some(handle) = handle {
        match collect_coverage(test.path(), handle, analysis, config, logger) {
            Ok(t) => Ok(Some(t)),
            Err(e) => Err(RunError::TestCoverage(e.to_string())),
        }
    } else {
        Ok(None)
    }
}

fn launch_test(
    test: &TestBinary,
    config: &Config,
    ignored: bool,
    logger: &Option<EventLog>,
) -> Result<Option<TestHandle>, RunError> {
    if let Some(log) = logger.as_ref() {
        log.push_binary(test.clone());
    }
    match config.engine() {
        TraceEngine::Ptrace => {
            cfg_if::cfg_if! {
                if #[cfg(target_os="linux")] {
                    linux::get_test_coverage(test, config, ignored)
                } else {
                    error!("Ptrace is not supported on this platform");
                    Err(RunError::TestCoverage("Unsupported OS".to_string()))
                }
            }
        }
        TraceEngine::Llvm => {
            let res = execute_test(test, ignored, config, None)?;
            Ok(Some(res))
        }
        e => {
            error!(
                "Tarpaulin cannot execute tests with {:?} on this platform",
                e
            );
            Err(RunError::TestCoverage("Unsupported OS".to_string()))
        }
    }
}

cfg_if::cfg_if! {
    if #[cfg(target_os= "linux")] {
        pub mod linux;
        pub use linux::*;

        pub mod breakpoint;
        pub mod ptrace_control;

        pub type ProcessHandle = nix::unistd::Pid;
    } else {
        pub type ProcessHandle = u64;
    }
}

/// Collects the coverage data from the launched test
pub(crate) fn collect_coverage(
    test_path: &Path,
    test: TestHandle,
    analysis: &HashMap<PathBuf, LineAnalysis>,
    config: &Config,
    logger: &Option<EventLog>,
) -> Result<(TraceMap, i32), RunError> {
    let mut ret_code = 0;
    let mut traces = generate_tracemap(test_path, analysis, config)?;
    {
        let span = trace_span!("Collect coverage", pid=%test);
        let _enter = span.enter();
        let (mut state, mut data) =
            create_state_machine(test, &mut traces, analysis, config, logger);
        loop {
            state = state.step(&mut data, config)?;
            if state.is_finished() {
                if let TestState::End(i) = state {
                    ret_code = i;
                }
                break;
            }
        }
    }
    Ok((traces, ret_code))
}

/// Launches the test executable
fn execute_test(
    test: &TestBinary,
    ignored: bool,
    config: &Config,
    num_threads: Option<usize>,
) -> Result<TestHandle, RunError> {
    info!("running {}", test.path().display());
    let _ = match test.manifest_dir() {
        Some(md) => env::set_current_dir(&md),
        None => env::set_current_dir(&config.root()),
    };

    let mut envars: Vec<(String, String)> = Vec::new();

    for (key, value) in env::vars() {
        envars.push((key.to_string(), value.to_string()));
    }
    let mut argv = vec![];
    if ignored {
        argv.push("--ignored".to_string());
    }
    if config.verbose {
        envars.push(("RUST_BACKTRACE".to_string(), "1".to_string()));
    }
    argv.extend_from_slice(&config.varargs);
    if config.color != Color::Auto {
        argv.push("--color".to_string());
        argv.push(config.color.to_string().to_ascii_lowercase());
    }
    if !config.varargs.iter().any(|x| x.contains("--test-threads")) {
        if let Some(threads) = num_threads {
            argv.push("--test-threads".to_string());
            argv.push(threads.to_string());
        }
    }

    if let Some(s) = test.pkg_name() {
        envars.push(("CARGO_PKG_NAME".to_string(), s.to_string()));
    }
    if let Some(s) = test.pkg_version() {
        envars.push(("CARGO_PKG_VERSION".to_string(), s.to_string()));
    }
    if let Some(s) = test.pkg_authors() {
        envars.push(("CARGO_PKG_AUTHORS".to_string(), s.join(":")));
    }
    if let Some(s) = test.manifest_dir() {
        envars.push(("CARGO_MANIFEST_DIR".to_string(), s.display().to_string()));
    }
    match config.engine() {
        TraceEngine::Llvm => {
            // Used for llvm coverage to avoid report naming clashes TODO could have clashes
            // between runs
            envars.push((
                "LLVM_PROFILE_FILE".to_string(),
                format!("{}_%p.profraw", test.file_name()),
            ));
            let mut child = Command::new(test.path());
            child.envs(envars).args(&argv);

            let hnd = RunningProcessHandle::new(test.path().to_path_buf(), &mut child, config)?;
            Ok(hnd.into())
        }
        #[cfg(target_os = "linux")]
        TraceEngine::Ptrace => {
            argv.insert(0, test.path().display().to_string());
            execute(test.path(), &argv, envars.as_slice())
        }
        e => Err(RunError::Engine(format!(
            "invalid execution engine {:?}",
            e
        ))),
    }
}
<commit_msg>Add some more debug logging`<commit_after>use crate::config::Color;
use crate::generate_tracemap;
use crate::statemachine::{create_state_machine, TestState};
use crate::traces::*;
use crate::{Config, EventLog, LineAnalysis, RunError, TestBinary, TraceEngine};
use std::collections::HashMap;
use std::env;
use std::ffi::OsStr;
use std::fmt;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::{Child, Command};
use tracing::{debug, error, info, trace_span};

/// Handle to a test currently either PID or a `std::process::Child`
pub enum TestHandle {
    Id(ProcessHandle),
    Process(RunningProcessHandle),
}

pub struct RunningProcessHandle {
    /// Used to map coverage counters to line numbers
    pub(crate) path: PathBuf,
    /// Get the exit status to work out if tests have passed
    pub(crate) child: Child,
    /// maintain a list of existing profraws in the project root to avoid picking up old results
    pub(crate) existing_profraws: Vec<PathBuf>,
}

impl RunningProcessHandle {
    pub fn new(path: PathBuf, cmd: &mut Command, config: &Config) -> Result<Self, RunError> {
        let child = cmd.spawn()?;
        let existing_profraws = fs::read_dir(config.root())?
            .into_iter()
            .filter_map(|x| x.ok())
            .filter(|x| x.path().is_file() && x.path().extension() == Some(OsStr::new("profraw")))
            .map(|x| x.path())
            .collect();

        Ok(Self {
            path,
            child,
            existing_profraws,
        })
    }
}

impl fmt::Display for TestHandle {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TestHandle::Id(id) => write!(f, "{}", id),
            TestHandle::Process(c) => write!(f, "{}", c.child.id()),
        }
    }
}

impl From<ProcessHandle> for TestHandle {
    fn from(handle: ProcessHandle) -> Self {
        Self::Id(handle)
    }
}

impl From<RunningProcessHandle> for TestHandle {
    fn from(handle: RunningProcessHandle) -> Self {
        Self::Process(handle)
    }
}

pub fn get_test_coverage(
    test: &TestBinary,
    analysis: &HashMap<PathBuf, LineAnalysis>,
    config: &Config,
    ignored: bool,
    logger: &Option<EventLog>,
) -> Result<Option<(TraceMap, i32)>, RunError> {
    let handle = launch_test(test, config, ignored, logger)?;
    if let Some(handle) = handle {
        match collect_coverage(test.path(), handle, analysis, config, logger) {
            Ok(t) => Ok(Some(t)),
            Err(e) => Err(RunError::TestCoverage(e.to_string())),
        }
    } else {
        Ok(None)
    }
}

fn launch_test(
    test: &TestBinary,
    config: &Config,
    ignored: bool,
    logger: &Option<EventLog>,
) -> Result<Option<TestHandle>, RunError> {
    if let Some(log) = logger.as_ref() {
        log.push_binary(test.clone());
    }
    match config.engine() {
        TraceEngine::Ptrace => {
            cfg_if::cfg_if! {
                if #[cfg(target_os="linux")] {
                    linux::get_test_coverage(test, config, ignored)
                } else {
                    error!("Ptrace is not supported on this platform");
                    Err(RunError::TestCoverage("Unsupported OS".to_string()))
                }
            }
        }
        TraceEngine::Llvm => {
            let res = execute_test(test, ignored, config, None)?;
            Ok(Some(res))
        }
        e => {
            error!(
                "Tarpaulin cannot execute tests with {:?} on this platform",
                e
            );
            Err(RunError::TestCoverage("Unsupported OS".to_string()))
        }
    }
}

cfg_if::cfg_if! {
    if #[cfg(target_os= "linux")] {
        pub mod linux;
        pub use linux::*;

        pub mod breakpoint;
        pub mod ptrace_control;

        pub type ProcessHandle = nix::unistd::Pid;
    } else {
        pub type ProcessHandle = u64;
    }
}

/// Collects the coverage data from the launched test
pub(crate) fn collect_coverage(
    test_path: &Path,
    test: TestHandle,
    analysis: &HashMap<PathBuf, LineAnalysis>,
    config: &Config,
    logger: &Option<EventLog>,
) -> Result<(TraceMap, i32), RunError> {
    let mut ret_code = 0;
    let mut traces = generate_tracemap(test_path, analysis, config)?;
    {
        let span = trace_span!("Collect coverage", pid=%test);
        let _enter = span.enter();
        let (mut state, mut data) =
            create_state_machine(test, &mut traces, analysis, config, logger);
        loop {
            state = state.step(&mut data, config)?;
            if state.is_finished() {
                if let TestState::End(i) = state {
                    ret_code = i;
                }
                break;
            }
        }
    }
    Ok((traces, ret_code))
}

/// Launches the test executable
fn execute_test(
    test: &TestBinary,
    ignored: bool,
    config: &Config,
    num_threads: Option<usize>,
) -> Result<TestHandle, RunError> {
    info!("running {}", test.path().display());
    let _ = match test.manifest_dir() {
        Some(md) => env::set_current_dir(&md),
        None => env::set_current_dir(&config.root()),
    };

    let mut envars: Vec<(String, String)> = Vec::new();

    for (key, value) in env::vars() {
        envars.push((key.to_string(), value.to_string()));
    }
    let mut argv = vec![];
    if ignored {
        argv.push("--ignored".to_string());
    }
    if config.verbose {
        envars.push(("RUST_BACKTRACE".to_string(), "1".to_string()));
    }
    argv.extend_from_slice(&config.varargs);
    if config.color != Color::Auto {
        argv.push("--color".to_string());
        argv.push(config.color.to_string().to_ascii_lowercase());
    }
    if !config.varargs.iter().any(|x| x.contains("--test-threads")) {
        if let Some(threads) = num_threads {
            argv.push("--test-threads".to_string());
            argv.push(threads.to_string());
        }
    }

    if let Some(s) = test.pkg_name() {
        envars.push(("CARGO_PKG_NAME".to_string(), s.to_string()));
    }
    if let Some(s) = test.pkg_version() {
        envars.push(("CARGO_PKG_VERSION".to_string(), s.to_string()));
    }
    if let Some(s) = test.pkg_authors() {
        envars.push(("CARGO_PKG_AUTHORS".to_string(), s.join(":")));
    }
    if let Some(s) = test.manifest_dir() {
        envars.push(("CARGO_MANIFEST_DIR".to_string(), s.display().to_string()));
    }
    debug!("Env vars: {:?}", envars);
    debug!("Args: {:?}", argv);
    match config.engine() {
        TraceEngine::Llvm => {
            // Used for llvm coverage to avoid report naming clashes TODO could have clashes
            // between runs
            envars.push((
                "LLVM_PROFILE_FILE".to_string(),
                format!("{}_%p.profraw", test.file_name()),
            ));
            let mut child = Command::new(test.path());
            child.envs(envars).args(&argv);

            let hnd = RunningProcessHandle::new(test.path().to_path_buf(), &mut child, config)?;
            Ok(hnd.into())
        }
        #[cfg(target_os = "linux")]
        TraceEngine::Ptrace => {
            argv.insert(0, test.path().display().to_string());
            execute(test.path(), &argv, envars.as_slice())
        }
        e => Err(RunError::Engine(format!(
            "invalid execution engine {:?}",
            e
        ))),
    }
}
<|endoftext|>


<commit_before>//! Implements a large hash-table that stores results of previously
//! performed searches ("transposition table").
//!
//! 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 basetypes::Value;
use chess_move::MoveDigest;

/// `BOUND_EXACT`, `BOUND_LOWER`, `BOUND_UPPER`, or `BOUND_NONE`.
///
/// For the majority of chess positions our evaluations will be more
/// or less inaccurate, and there is nothing we can do about it. But
/// sometimes we know that a given evaluation is probably inaccurate,
/// and we know the sign of the error. `BoundType` defines the
/// direction of such **known inaccuracies**.
///
/// * `BOUND_EXACT` means that the evaluation is exact (as far as we know).
///
/// * `BOUND_LOWER` means that the real value is greater or equal to
///    the evaluation (as far as we know).
///
/// * `BOUND_UPPER` means that the real value is lesser or equal to
///   the evaluation (as far as we know).
///
/// * `BOUND_NONE` means that the real value might be lesser, equal,
///   or grater than the evaluation.
pub type BoundType = u8;

pub const BOUND_NONE: BoundType = 0;
pub const BOUND_UPPER: BoundType = 0b10;
pub const BOUND_LOWER: BoundType = 0b01;
pub const BOUND_EXACT: BoundType = BOUND_UPPER | BOUND_LOWER;

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

    // The transposition table maintains a generation number for each
    // entry, which is used to implement an efficient replacement
    // strategy. This field stores the entry's generation (the highest
    // 6 bits) and the bound type (the lowest 2 bits).
    gen_bound: u8,
}

impl TtEntry {
    /// Creates a new instance.
    ///
    /// * `value` -- the value assigned to the position;
    ///
    /// * `bound` -- the accuracy 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)
               -> TtEntry {
        assert!(bound <= 0b11);
        assert!(depth < 127);
        TtEntry {
            move16: move16,
            value: value,
            eval_value: eval_value,
            depth: depth,
            gen_bound: bound,
        }
    }

    #[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
    }

    /// Returns the contained data as one `u64` value.
    #[inline(always)]
    fn as_u64(&self) -> u64 {
        unsafe { transmute(*self) }
    }
}

/// A transposition table.
///
/// `Tt` methods (except `resize`) do not require a mutable reference
/// to do their work. This allows one transposition table instance to
/// be shared safely between many threads.
pub struct Tt {
    /// The current generation number. The lowest 2 bits will always
    /// be zeros.
    generation: Cell<u8>,

    /// The number of clusters in the table.
    cluster_count: usize,

    /// The table consists of a vector of clusters. Each cluster
    /// stores 4 records.
    table: UnsafeCell<Vec<[Record; 4]>>,
}

impl Tt {
    /// 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() -> Tt {
        Tt {
            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) {
        let requested_cluster_count = (size_mb * 1024 * 1024) / std::mem::size_of::<[Record; 4]>();

        // Calculate the new cluster count. (To do this, first we make
        // sure that `requested_cluster_count` is exceeded. Then we
        // make one step back.)
        let mut new_cluster_count = 1;
        while new_cluster_count <= requested_cluster_count && new_cluster_count != 0 {
            new_cluster_count <<= 1;
        }
        if new_cluster_count > 1 {
            new_cluster_count >>= 1;
        } else {
            new_cluster_count = 1;
        }
        assert!(new_cluster_count > 0);

        // Allocate the new 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::<[Record; 4]>() / 1024 / 1024
    }

    /// Signals that a new search is about to begin.
    pub fn new_search(&self) {
        self.generation.set(self.generation.get().wrapping_add(0b100));
        assert_eq!(self.generation.get() & 0b11, 0);
    }

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

        // Set the entry's generation.
        data.gen_bound |= self.generation.get();

        // Choose a slot to which to write the data. (Each cluster has
        // 4 slots.)
        let mut cluster = unsafe { self.cluster_mut(key) };
        let mut replace_index = 0;
        let mut replace_score = 0xff;
        for (i, record) in cluster.iter_mut().enumerate() {
            // Check if this is an empty slot, or an old record for
            // the same key. If this this is the case we will use this
            // slot for the new record.
            if record.key == 0 || record.key ^ record.data.as_u64() == key {
                if data.move16 == 0 {
                    data.move16 = record.data.move16; // Preserve any existing move.
                }
                replace_index = i;
                break;
            }

            // Calculate the score for this record. If we can not find
            // an empty slot or an old record, the replaced record
            // will be the record with the lowest score.
            let record_score = self.calc_score(record);
            if record_score < replace_score {
                replace_index = i;
                replace_score = record_score;
            }
        }

        // Write the data to the chosen slot.
        unsafe {
            cluster.get_unchecked_mut(replace_index).key = key ^ data.as_u64();
            cluster.get_unchecked_mut(replace_index).data = data;
        }
    }

    /// Probes for data by a specific key.
    ///
    /// **Note:** This method may write to the transposition table
    /// (for example, to update the generation of the entry).
    #[inline]
    pub fn probe(&self, key: u64) -> Option<TtEntry> {
        let cluster = unsafe { self.cluster_mut(key) };
        for record in cluster.iter_mut() {
            if record.key ^ record.data.as_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).
                record.set_generation(self.generation.get());
                return Some(record.data);
            }
        }
        None
    }

    /// Peeks for data by a specific key.
    ///
    /// This method does the same as `probe`, except it will never
    /// write to the transposition table. This is useful, for example,
    /// when we want to extract the primary variation from the the
    /// transposition table, without affecting any entries in the
    /// table.
    #[inline]
    pub fn peek(&self, key: u64) -> Option<TtEntry> {
        let cluster = unsafe { self.cluster_mut(key) };
        for record in cluster.iter_mut() {
            if record.key ^ record.data.as_u64() == key {
                return Some(record.data);
            }
        }
        None
    }

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

    /// A helper method for `store`. It implements the record
    /// replacement strategy.
    #[inline(always)]
    fn calc_score(&self, record: &Record) -> u8 {
        // Here we try to return higher values for the records that
        // are move likely to save CPU work in the future:

        // Positions from the current generation are always scored
        // higher than positions from older generations.
        (if record.generation() == self.generation.get() {
            128
        } else {
            0
        })

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

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

    /// A helper method for `probe` and `store`. It returns the
    /// cluster for a given key.
    #[inline]
    unsafe fn cluster_mut(&self, key: u64) -> &mut [Record; 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 Tt {}

/// Represents a record in the transposition table.
///
/// It consists of 16 bytes, and is laid out the following way:
///
/// * key        64 bit
/// * move16     16 bit
/// * value      16 bit
/// * eval value 16 bit
/// * depth       8 bit
/// * generation  6 bit
/// * bound type  2 bit
#[derive(Copy, Clone)]
struct Record {
    key: u64,
    data: TtEntry,
}

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

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

    /// Sets record's generation.
    #[inline(always)]
    fn set_generation(&mut self, generation: u8) {
        assert_eq!(generation & 0b11, 0);

        // Since the `key` is saved XOR-ed with the data, when we
        // change the data, we have to change the stored `key` as
        // well.
        let old_data_u64 = self.data.as_u64();
        self.data.gen_bound = generation | self.data.bound();
        self.key ^= old_data_u64 ^ self.data.as_u64();
    }
}

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

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

    #[test]
    fn test_tt_resize() {
        let mut tt = Tt::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 = Tt::new();
        assert!(tt.probe(1).is_none());
        let data = TtEntry::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, TtEntry::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, TtEntry::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 = Tt::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>readability improvements<commit_after>//! Implements a large hash-table that stores results of previously
//! performed searches ("transposition table").
//!
//! 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 basetypes::Value;
use chess_move::MoveDigest;

/// `BOUND_EXACT`, `BOUND_LOWER`, `BOUND_UPPER`, or `BOUND_NONE`.
///
/// For the majority of chess positions our evaluations will be more
/// or less inaccurate, and there is nothing we can do about it. But
/// sometimes we know that a given evaluation is probably inaccurate,
/// and we know the sign of the error. `BoundType` defines the
/// direction of such **known inaccuracies**.
///
/// * `BOUND_EXACT` means that the evaluation is exact (as far as we know).
///
/// * `BOUND_LOWER` means that the real value is greater or equal to
///    the evaluation (as far as we know).
///
/// * `BOUND_UPPER` means that the real value is lesser or equal to
///   the evaluation (as far as we know).
///
/// * `BOUND_NONE` means that the real value might be lesser, equal,
///   or grater than the evaluation.
pub type BoundType = u8;

pub const BOUND_NONE: BoundType = 0;
pub const BOUND_UPPER: BoundType = 0b10;
pub const BOUND_LOWER: BoundType = 0b01;
pub const BOUND_EXACT: BoundType = BOUND_UPPER | BOUND_LOWER;

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

    // The transposition table maintains a generation number for each
    // entry, which is used to implement an efficient replacement
    // strategy. This field stores the entry's generation (the highest
    // 6 bits) and the bound type (the lowest 2 bits).
    gen_bound: u8,
}

impl TtEntry {
    /// Creates a new instance.
    ///
    /// * `value` -- the value assigned to the position;
    ///
    /// * `bound` -- the accuracy 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)
               -> TtEntry {
        assert!(bound <= 0b11);
        assert!(depth < 127);
        TtEntry {
            move16: move16,
            value: value,
            eval_value: eval_value,
            depth: depth,
            gen_bound: bound,
        }
    }

    #[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
    }

    /// Returns the contained data as one `u64` value.
    #[inline(always)]
    fn as_u64(&self) -> u64 {
        unsafe { transmute(*self) }
    }
}

/// A transposition table.
///
/// `Tt` methods (except `resize`) do not require a mutable reference
/// to do their work. This allows one transposition table instance to
/// be shared safely between many threads.
pub struct Tt {
    /// The current generation number. The lowest 2 bits will always
    /// be zeros.
    generation: Cell<u8>,

    /// The number of clusters in the table.
    cluster_count: usize,

    /// The transposition table consists of a vector of clusters. Each
    /// cluster stores 4 records.
    table: UnsafeCell<Vec<[Record; 4]>>,
}

impl Tt {
    /// 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() -> Tt {
        Tt {
            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) {
        let requested_cluster_count = (size_mb * 1024 * 1024) / std::mem::size_of::<[Record; 4]>();

        // Calculate the new cluster count. (To do this, first we make
        // sure that `requested_cluster_count` is exceeded. Then we
        // make one step back.)
        let mut new_cluster_count = 1;
        while new_cluster_count <= requested_cluster_count && new_cluster_count != 0 {
            new_cluster_count <<= 1;
        }
        if new_cluster_count > 1 {
            new_cluster_count >>= 1;
        } else {
            new_cluster_count = 1;
        }
        assert!(new_cluster_count > 0);

        // Allocate the new vector of clusters.
        self.generation.set(0);
        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::<[Record; 4]>() / 1024 / 1024
    }

    /// Signals that a new search is about to begin.
    pub fn new_search(&self) {
        self.generation.set(self.generation.get().wrapping_add(0b100));
        assert_eq!(self.generation.get() & 0b11, 0);
    }

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

        // Set the entry's generation.
        data.gen_bound |= self.generation.get();

        // Choose a slot to which to write the data. (Each cluster has
        // 4 slots.)
        let mut cluster = unsafe { self.cluster_mut(key) };
        let mut replace_index = 0;
        let mut replace_score = 0xff;
        for (i, record) in cluster.iter_mut().enumerate() {
            // Check if this is an empty slot, or an old record for
            // the same key. If this this is the case we will use this
            // slot for the new record.
            if record.key == 0 || record.key ^ record.data.as_u64() == key {
                if data.move16 == 0 {
                    data.move16 = record.data.move16; // Preserve any existing move.
                }
                replace_index = i;
                break;
            }

            // Calculate the score for this record. If we can not find
            // an empty slot or an old record, the replaced record
            // will be the record with the lowest score.
            let record_score = self.calc_score(record);
            if record_score < replace_score {
                replace_index = i;
                replace_score = record_score;
            }
        }

        // Write the data to the chosen slot.
        unsafe {
            cluster.get_unchecked_mut(replace_index).key = key ^ data.as_u64();
            cluster.get_unchecked_mut(replace_index).data = data;
        }
    }

    /// Probes for data by a specific key.
    ///
    /// **Note:** This method may write to the transposition table
    /// (for example, to update the generation of the entry).
    #[inline]
    pub fn probe(&self, key: u64) -> Option<TtEntry> {
        let cluster = unsafe { self.cluster_mut(key) };
        for record in cluster.iter_mut() {
            if record.key ^ record.data.as_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).
                record.set_generation(self.generation.get());
                return Some(record.data);
            }
        }
        None
    }

    /// Peeks for data by a specific key.
    ///
    /// This method does the same as `probe`, except it will never
    /// write to the transposition table. This is useful, for example,
    /// when we want to extract the primary variation from the the
    /// transposition table, without affecting any entries in the
    /// table.
    #[inline]
    pub fn peek(&self, key: u64) -> Option<TtEntry> {
        let cluster = unsafe { self.cluster_mut(key) };
        for record in cluster.iter_mut() {
            if record.key ^ record.data.as_u64() == key {
                return Some(record.data);
            }
        }
        None
    }

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

    /// A helper method for `store`. It implements the record
    /// replacement strategy.
    #[inline(always)]
    fn calc_score(&self, record: &Record) -> u8 {
        // Here we try to return higher values for the records that
        // are move likely to save CPU work in the future:

        // Positions from the current generation are always scored
        // higher than positions from older generations.
        (if record.generation() == self.generation.get() {
            128
        } else {
            0
        })

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

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

    /// A helper method for `probe` and `store`. It returns the
    /// cluster for a given key.
    #[inline]
    unsafe fn cluster_mut(&self, key: u64) -> &mut [Record; 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 Tt {}

/// Represents a record in the transposition table.
///
/// It consists of 16 bytes, and is laid out the following way:
///
/// * key        64 bit
/// * move16     16 bit
/// * value      16 bit
/// * eval value 16 bit
/// * depth       8 bit
/// * generation  6 bit
/// * bound type  2 bit
#[derive(Copy, Clone)]
struct Record {
    key: u64,
    data: TtEntry,
}

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

impl Record {
    #[inline(always)]
    fn generation(&self) -> u8 {
        self.data.gen_bound & 0b11111100
    }

    #[inline(always)]
    fn set_generation(&mut self, generation: u8) {
        assert_eq!(generation & 0b11, 0);

        // Since the `key` is saved XOR-ed with the data, when we
        // change the data, we have to change the stored `key` as
        // well.
        let old_data_as_u64 = self.data.as_u64();
        self.data.gen_bound = generation | self.data.bound();
        self.key ^= old_data_as_u64 ^ self.data.as_u64();
    }
}

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

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

    #[test]
    fn test_tt_resize() {
        let mut tt = Tt::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 = Tt::new();
        assert!(tt.probe(1).is_none());
        let data = TtEntry::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, TtEntry::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, TtEntry::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 = Tt::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>//! Utilities for the test suite.

use std::sync::Arc;
use std::fmt::Debug;
use rand::random;

use lift::lift0;
use signal::Signal;
use stream::Stream;

/// The identity function.
pub fn id<T>(t: T) -> T { t }

/// Trace equality of two signals.
pub fn signal_eq<T>(a: &Signal<T>, b: &Signal<T>) -> Signal<bool>
    where T: PartialEq + Clone + Send + Sync + 'static
{
    lift!(|a, b| a == b, a, b)
}

/// Trace equality of two streams.
pub fn stream_eq<T>(a: &Stream<T>, b: &Stream<T>) -> Signal<bool>
    where T: PartialEq + Clone + Send + Sync + 'static,
{
    fn tag<T>(t: u64, a: T) -> Option<(u64, T)> {
        Some((t, a))
    }

    let tagger = lift0(random::<u64>);
    tagger.snapshot(a, tag)
        .merge(&tagger.snapshot(b, tag))
        .coalesce(|a, b| match (a, b) {
            (Some(a), Some(b)) => {
                if &a == &b { Some(a) } else { None }
            },
            _ => None,
        })
        .scan(true, |state, a| state && a.is_some())
}

/// A boxed function.
pub type ArcFn<A, B> = Arc<Box<Fn(A) -> B + Send + Sync + 'static>>;

/// Wrap a function into a constant signal of that function.
pub fn pure_fn<A, B, F: Fn(A) -> B + Send + Sync + 'static>(f: F) -> Signal<ArcFn<A, B>> {
    Signal::new(make_fn(f))
}

/// Box a function to erase its type.
pub fn make_fn<A, B, F: Fn(A) -> B + Send + Sync + 'static>(f: F) -> ArcFn<A, B> {
    Arc::new(Box::new(f))
}

/// Function composition on boxed functions.
pub fn comp<A, B, C>(f: ArcFn<B, C>, g: ArcFn<A, B>) -> ArcFn<A, C> {
    make_fn(move |a| f(g(a)))
}

/// Partially applied function composition.
pub fn partial_comp<A, B, C>(f: ArcFn<B, C>) -> ArcFn<ArcFn<A, B>, ArcFn<A, C>> {
    make_fn(move |g| comp(f.clone(), g))
}

/// Self-tests.
mod test {
    use signal::Signal;
    use stream::{ Stream, Sink };
    use super::{ stream_eq, signal_eq };

    #[test]
    fn signal_eq_const() {
        let a = Signal::new(3);
        let eq = signal_eq(&a, &a);
        assert!(eq.sample());
    }

    #[test]
    fn signal_eq_piecewise_const() {
        let sink = Sink::new();
        let sig = sink.stream().hold(4);
        let eq = signal_eq(&sig, &sig);
        for &a in &[1, 2, 3] {
            sink.send(a);
            assert!(eq.sample());
        }
    }

    #[test]
    fn signal_eq_algebraic() {
        let eq = signal_eq(&Stream::never().hold(3), &Signal::new(3));
        assert!(eq.sample());
    }

    #[test]
    fn stream_eq_never() {
        let eq = stream_eq(&Stream::<()>::never(), &Stream::never());
        assert!(eq.sample());
    }

    #[test]
    fn stream_eq_same_sink() {
        let sink = Sink::new();
        let eq = stream_eq(&sink.stream(), &sink.stream());
        for n in 0..20 { sink.send(n); }
        assert!(eq.sample());
    }

    #[test]
    fn stream_eq_algebraic() {
        fn f(n: i32) -> i32 { n + 3 }
        fn g(n: i32) -> i32 { 2 * n }
        fn h(n: i32) -> i32 { f(g(n)) }

        let sink = Sink::new();
        let eq = stream_eq(&sink.stream().map(g).map(f), &sink.stream().map(h));
        for n in 0..20 { sink.send(n); }
        assert!(eq.sample());
    }
}
<commit_msg>Improve stream equality testing<commit_after>//! Utilities for the test suite.

use std::sync::Arc;
use std::fmt::Debug;
use rand::random;

use lift::{ lift0, lift1 };
use signal::Signal;
use stream::Stream;

/// The identity function.
pub fn id<T>(t: T) -> T { t }

/// Trace equality of two signals.
pub fn signal_eq<T>(a: &Signal<T>, b: &Signal<T>) -> Signal<bool>
    where T: PartialEq + Clone + Send + Sync + 'static
{
    lift!(|a, b| a == b, a, b)
}

/// Error of stream equivalence.
#[derive(Copy, Clone, PartialEq, Debug)]
enum EquivError<T> {
    Mismatch(T, T),
    OnlyOne(T),
    Other,
}

/// Simple pairing function.
fn pair<T, U>(t: T, u: U) -> (T, U) {
    (t, u)
}

/// Trace equality of two streams.
pub fn stream_eq<T>(a: &Stream<T>, b: &Stream<T>) -> Signal<Result<bool, String>>
    where T: PartialEq + Clone + Send + Sync + 'static + Debug,
{
    use self::EquivError::*;
    let tagger = lift0(random::<u64>);
    let result = tagger.snapshot(a, pair)
        .merge(&tagger.snapshot(b, pair))
        .map(|x| Err(OnlyOne(x)))
        .coalesce(|a, b| match (a, b) {
            (Err(OnlyOne(a)), Err(OnlyOne(b))) =>
                if &a == &b { Ok(true) }
                else { Err(Mismatch(a, b)) },
            (Err(Mismatch(a, b)), Err(OnlyOne(_))) => Err(Mismatch(a, b)),
            (Ok(true), Err(OnlyOne(a))) => Err(OnlyOne(a)),
            (Err(Other), Err(OnlyOne(_))) => Err(Other),
            _ => unreachable!(),
        })
        .scan(Ok(true), |state, a| state.and(a));
    lift1(|r| r.map_err(|e| format!("{:?}", e)), &result)
}

/// A boxed function.
pub type ArcFn<A, B> = Arc<Box<Fn(A) -> B + Send + Sync + 'static>>;

/// Wrap a function into a constant signal of that function.
pub fn pure_fn<A, B, F: Fn(A) -> B + Send + Sync + 'static>(f: F) -> Signal<ArcFn<A, B>> {
    Signal::new(make_fn(f))
}

/// Box a function to erase its type.
pub fn make_fn<A, B, F: Fn(A) -> B + Send + Sync + 'static>(f: F) -> ArcFn<A, B> {
    Arc::new(Box::new(f))
}

/// Function composition on boxed functions.
pub fn comp<A, B, C>(f: ArcFn<B, C>, g: ArcFn<A, B>) -> ArcFn<A, C> {
    make_fn(move |a| f(g(a)))
}

/// Partially applied function composition.
pub fn partial_comp<A, B, C>(f: ArcFn<B, C>) -> ArcFn<ArcFn<A, B>, ArcFn<A, C>> {
    make_fn(move |g| comp(f.clone(), g))
}

/// Self-tests.
mod test {
    use signal::Signal;
    use stream::{ Stream, Sink };
    use super::{ stream_eq, signal_eq };

    #[test]
    fn signal_eq_const() {
        let a = Signal::new(3);
        let eq = signal_eq(&a, &a);
        assert!(eq.sample());
    }

    #[test]
    fn signal_eq_piecewise_const() {
        let sink = Sink::new();
        let sig = sink.stream().hold(4);
        let eq = signal_eq(&sig, &sig);
        for &a in &[1, 2, 3] {
            sink.send(a);
            assert!(eq.sample());
        }
    }

    #[test]
    fn signal_eq_algebraic() {
        let eq = signal_eq(&Stream::never().hold(3), &Signal::new(3));
        assert!(eq.sample());
    }

    #[test]
    fn stream_eq_never() {
        let eq = stream_eq(&Stream::<()>::never(), &Stream::never());
        assert_eq!(eq.sample(), Ok(true));
    }

    #[test]
    fn stream_eq_same_sink() {
        let sink = Sink::new();
        let eq = stream_eq(&sink.stream(), &sink.stream());
        for n in 0..20 { sink.send(n); }
        assert_eq!(eq.sample(), Ok(true));
    }

    #[test]
    fn stream_eq_algebraic() {
        fn f(n: i32) -> i32 { n + 3 }
        fn g(n: i32) -> i32 { 2 * n }
        fn h(n: i32) -> i32 { f(g(n)) }

        let sink = Sink::new();
        let eq = stream_eq(&sink.stream().map(g).map(f), &sink.stream().map(h));
        for n in 0..20 { sink.send(n); }
        assert_eq!(eq.sample(), Ok(true));
    }
}
<|endoftext|>


<commit_before>use gl;
use gl::types::GLuint;
use graphics::{
    Image,
};

/// Wraps OpenGL texture data.
/// The texture gets deleted when running out of scope.
pub struct Texture {
    id: GLuint,
    width: u32,
    height: u32,
}

impl Texture {
    /// Creates a new texture.
    #[inline(always)]
    pub fn new(id: GLuint, width: u32, height: u32) -> Texture {
        Texture {
            id: id,
            width: width,
            height: height,
        }
    }

    /// Gets the OpenGL id of the texture.
    #[inline(always)]
    pub fn get_id(&self) -> GLuint {
        self.id
    }
}

impl Drop for Texture {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, [self.id].as_ptr());
        }
    }
}

impl Image for Texture {
    fn get_size(&self) -> (u32, u32) {
        (self.width, self.height)
    }
}
<commit_msg>Added `from_path` constructor to `Texture`<commit_after>use png;
use gl;
use gl::types::GLuint;
use libc::c_void;
use graphics::{
    Image,
};

/// Wraps OpenGL texture data.
/// The texture gets deleted when running out of scope.
pub struct Texture {
    id: GLuint,
    width: u32,
    height: u32,
}

impl Texture {
    /// Creates a new texture.
    #[inline(always)]
    pub fn new(id: GLuint, width: u32, height: u32) -> Texture {
        Texture {
            id: id,
            width: width,
            height: height,
        }
    }

    /// Gets the OpenGL id of the texture.
    #[inline(always)]
    pub fn get_id(&self) -> GLuint {
        self.id
    }

    /// Loads image by relative file name to the asset root.
    pub fn from_path(&mut self, path: &Path) -> Result<Texture, String> {
        let img = match png::load_png(path) {
            Ok(img) => img,
            Err(msg) => return Err(format!("Could not load '{}': {}", path.filename_str().unwrap(), msg)),
        };

        match img.color_type {
            png::RGBA8 => {},
            t => fail!("Unsupported color type {:?} in png", t),
        };

        let mut id: GLuint = 0;
        unsafe {
            gl::GenTextures(1, &mut id);
            gl::BindTexture(gl::TEXTURE_2D, id);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
            gl::TexImage2D(
                gl::TEXTURE_2D,
                0,
                gl::RGBA as i32,
                img.width as i32,
                img.height as i32,
                0,
                gl::RGBA,
                gl::UNSIGNED_BYTE,
                img.pixels.as_ptr() as *c_void
            );
        }

        Ok(Texture::new(id, img.width, img.height))
    }
}

impl Drop for Texture {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, [self.id].as_ptr());
        }
    }
}

impl Image for Texture {
    fn get_size(&self) -> (u32, u32) {
        (self.width, self.height)
    }
}
<|endoftext|>


<commit_before>extern crate find_folder;

use std::path::PathBuf;

pub struct Manager {
    assets_path: PathBuf,
}

impl Manager {
    pub fn new() -> Self {
        Manager { assets_path: find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap() }
    }

    pub fn get_asset(&self, name: &str) -> PathBuf {
        self.assets_path.join(name)
    }

    pub fn get_font(&self) -> PathBuf {
        self.get_asset("NotoSans-Regular.ttf")
    }
}

#[test]
fn new() {
    let manager = Manager::new();
    assert!(!manager.assets_path.to_string_lossy().is_empty());
    assert!(manager.assets_path.is_absolute());
    assert!(manager.assets_path.exists());
    assert!(manager.assets_path.is_dir());
}

#[test]
fn get_font() {
    let manager = Manager::new();
    let font_path = manager.get_font();
    assert!(font_path.to_string_lossy().len() > manager.assets_path.to_string_lossy().len());
    assert!(font_path.is_absolute());
    assert!(font_path.is_file());
    assert_eq!("ttf", font_path.extension().unwrap());
}
<commit_msg>get_asset test<commit_after>extern crate find_folder;

use std::path::PathBuf;

pub struct Manager {
    assets_path: PathBuf,
}

impl Manager {
    pub fn new() -> Self {
        Manager { assets_path: find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap() }
    }

    pub fn get_asset(&self, name: &str) -> PathBuf {
        self.assets_path.join(name)
    }

    pub fn get_font(&self) -> PathBuf {
        self.get_asset("NotoSans-Regular.ttf")
    }
}

#[test]
fn new() {
    let manager = Manager::new();
    assert!(!manager.assets_path.to_string_lossy().is_empty());
    assert!(manager.assets_path.is_absolute());
    assert!(manager.assets_path.exists());
    assert!(manager.assets_path.is_dir());
}

#[test]
fn get_asset() {
    let manager = Manager::new();
    let asset_path = manager.get_asset("test");
    assert!(asset_path.to_string_lossy().len() > manager.assets_path.to_string_lossy().len());
}

#[test]
fn get_font() {
    let manager = Manager::new();
    let font_path = manager.get_font();
    assert!(font_path.is_absolute());
    assert!(font_path.is_file());
    assert_eq!("ttf", font_path.extension().unwrap());
}
<|endoftext|>


<commit_before>//! Interleaving and deinterleaving for data packet payloads.

use std;

use bits;
use consts;

trait Redirector {
    /// Redirector the given index to another within the buffer.
    fn redirect(idx: usize) -> usize;
}

/// Redirects for interleaving.
struct InterleaveRedirector;

impl Redirector for InterleaveRedirector {
    fn redirect(idx: usize) -> usize {
        const REDIRECTS: [usize; consts::CODING_DIBITS] = [
            0,
            1,
            8,
            9,
            16,
            17,
            24,
            25,
            32,
            33,
            40,
            41,
            48,
            49,
            56,
            57,
            64,
            65,
            72,
            73,
            80,
            81,
            88,
            89,
            96,
            97,
            2,
            3,
            10,
            11,
            18,
            19,
            26,
            27,
            34,
            35,
            42,
            43,
            50,
            51,
            58,
            59,
            66,
            67,
            74,
            75,
            82,
            83,
            90,
            91,
            4,
            5,
            12,
            13,
            20,
            21,
            28,
            29,
            36,
            37,
            44,
            45,
            52,
            53,
            60,
            61,
            68,
            69,
            76,
            77,
            84,
            85,
            92,
            93,
            6,
            7,
            14,
            15,
            22,
            23,
            30,
            31,
            38,
            39,
            46,
            47,
            54,
            55,
            62,
            63,
            70,
            71,
            78,
            79,
            86,
            87,
            94,
            95,
        ];

        REDIRECTS[idx]
    }
}

/// Redirects to undo interleaving.
struct DeinterleaveRedirector;

impl Redirector for DeinterleaveRedirector {
    fn redirect(idx: usize) -> usize {
        const REDIRECTS: [usize; consts::CODING_DIBITS] = [
            0,
            1,
            26,
            27,
            50,
            51,
            74,
            75,
            2,
            3,
            28,
            29,
            52,
            53,
            76,
            77,
            4,
            5,
            30,
            31,
            54,
            55,
            78,
            79,
            6,
            7,
            32,
            33,
            56,
            57,
            80,
            81,
            8,
            9,
            34,
            35,
            58,
            59,
            82,
            83,
            10,
            11,
            36,
            37,
            60,
            61,
            84,
            85,
            12,
            13,
            38,
            39,
            62,
            63,
            86,
            87,
            14,
            15,
            40,
            41,
            64,
            65,
            88,
            89,
            16,
            17,
            42,
            43,
            66,
            67,
            90,
            91,
            18,
            19,
            44,
            45,
            68,
            69,
            92,
            93,
            20,
            21,
            46,
            47,
            70,
            71,
            94,
            95,
            22,
            23,
            48,
            49,
            72,
            73,
            96,
            97,
            24,
            25,
        ];

        REDIRECTS[idx]
    }
}

struct Indexes<R: Redirector>(std::ops::Range<usize>, std::marker::PhantomData<R>);

impl<R: Redirector> Indexes<R> {
    pub fn new() -> Indexes<R> {
        Indexes(0..consts::CODING_DIBITS, std::marker::PhantomData)
    }
}

impl<R: Redirector> Iterator for Indexes<R> {
    type Item = usize;

    fn next(&mut self) -> Option<Self::Item> {
        match self.0.next() {
            Some(idx) => Some(R::redirect(idx)),
            None => None,
        }
    }
}

/// Takes ownership of a dibit buffer, yielding deinterleaved dibits.
pub struct Interleaver {
    /// Wrapped buffer.
    dibits: [bits::Dibit; consts::CODING_DIBITS],
    /// Redirected indexes.
    idx: Indexes<InterleaveRedirector>,
}

impl Interleaver {
    /// Construct a new `Interleaver` over the given buffer.
    pub fn new(dibits: [bits::Dibit; consts::CODING_DIBITS]) -> Interleaver {
        Interleaver {
            dibits: dibits,
            idx: Indexes::new(),
        }
    }
}

impl Iterator for Interleaver {
    type Item = bits::Dibit;

    fn next(&mut self) -> Option<Self::Item> {
        match self.idx.next() {
            Some(idx) => Some(self.dibits[idx]),
            None => None
        }
    }
}

pub struct Deinterleaver<'a> {
    dibits: &'a [bits::Dibit; consts::CODING_DIBITS],
    idx: Indexes<DeinterleaveRedirector>,
}

impl<'a> Deinterleaver<'a> {
    pub fn new(dibits: &'a [bits::Dibit; consts::CODING_DIBITS]) -> Deinterleaver<'a> {
        Deinterleaver {
            dibits: dibits,
            idx: Indexes::new(),
        }
    }
}

impl<'a> Iterator for Deinterleaver<'a> {
    type Item = bits::Dibit;

    fn next(&mut self) -> Option<Self::Item> {
        match self.idx.next() {
            Some(idx) => Some(self.dibits[idx]),
            None => None,
        }
    }
}

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

    #[test]
    fn test_interleave() {
        let mut buf = [Dibit::default(); 98];

        for i in 0..98 {
            buf[i] = Dibit::new(i as u8 % 4);
        }

        let mut int = Interleaver::new(buf);

        for _ in 0..13 {
            assert_eq!(int.next().unwrap().bits(), 0b00);
            assert_eq!(int.next().unwrap().bits(), 0b01);
        }

        for _ in 0..12 {
            assert_eq!(int.next().unwrap().bits(), 0b10);
            assert_eq!(int.next().unwrap().bits(), 0b11);
        }

        for _ in 0..12 {
            assert_eq!(int.next().unwrap().bits(), 0b00);
            assert_eq!(int.next().unwrap().bits(), 0b01);
        }

        for _ in 0..12 {
            assert_eq!(int.next().unwrap().bits(), 0b10);
            assert_eq!(int.next().unwrap().bits(), 0b11);
        }

        assert!(int.next().is_none());
    }

    #[test]
    fn test_deinterleave() {
        let mut buf = [Dibit::default(); 98];

        for i in 0..98 {
            buf[i] = Dibit::new(i as u8 % 4);
        }

        let mut out = [Dibit::default(); 98];

        for (i, dibit) in Interleaver::new(buf).enumerate() {
            out[i] = dibit;
        }

        let mut deint = Deinterleaver::new(&out);

        for _ in 0..24 {
            assert_eq!(deint.next().unwrap().bits(), 0b00);
            assert_eq!(deint.next().unwrap().bits(), 0b01);
            assert_eq!(deint.next().unwrap().bits(), 0b10);
            assert_eq!(deint.next().unwrap().bits(), 0b11);
        }

        assert_eq!(deint.next().unwrap().bits(), 0b00);
        assert_eq!(deint.next().unwrap().bits(), 0b01);

        assert!(deint.next().is_none());
    }
}
<commit_msg>Use Option::map in data module<commit_after>//! Interleaving and deinterleaving for data packet payloads.

use std;

use bits;
use consts;

trait Redirector {
    /// Redirector the given index to another within the buffer.
    fn redirect(idx: usize) -> usize;
}

/// Redirects for interleaving.
struct InterleaveRedirector;

impl Redirector for InterleaveRedirector {
    fn redirect(idx: usize) -> usize {
        const REDIRECTS: [usize; consts::CODING_DIBITS] = [
            0,
            1,
            8,
            9,
            16,
            17,
            24,
            25,
            32,
            33,
            40,
            41,
            48,
            49,
            56,
            57,
            64,
            65,
            72,
            73,
            80,
            81,
            88,
            89,
            96,
            97,
            2,
            3,
            10,
            11,
            18,
            19,
            26,
            27,
            34,
            35,
            42,
            43,
            50,
            51,
            58,
            59,
            66,
            67,
            74,
            75,
            82,
            83,
            90,
            91,
            4,
            5,
            12,
            13,
            20,
            21,
            28,
            29,
            36,
            37,
            44,
            45,
            52,
            53,
            60,
            61,
            68,
            69,
            76,
            77,
            84,
            85,
            92,
            93,
            6,
            7,
            14,
            15,
            22,
            23,
            30,
            31,
            38,
            39,
            46,
            47,
            54,
            55,
            62,
            63,
            70,
            71,
            78,
            79,
            86,
            87,
            94,
            95,
        ];

        REDIRECTS[idx]
    }
}

/// Redirects to undo interleaving.
struct DeinterleaveRedirector;

impl Redirector for DeinterleaveRedirector {
    fn redirect(idx: usize) -> usize {
        const REDIRECTS: [usize; consts::CODING_DIBITS] = [
            0,
            1,
            26,
            27,
            50,
            51,
            74,
            75,
            2,
            3,
            28,
            29,
            52,
            53,
            76,
            77,
            4,
            5,
            30,
            31,
            54,
            55,
            78,
            79,
            6,
            7,
            32,
            33,
            56,
            57,
            80,
            81,
            8,
            9,
            34,
            35,
            58,
            59,
            82,
            83,
            10,
            11,
            36,
            37,
            60,
            61,
            84,
            85,
            12,
            13,
            38,
            39,
            62,
            63,
            86,
            87,
            14,
            15,
            40,
            41,
            64,
            65,
            88,
            89,
            16,
            17,
            42,
            43,
            66,
            67,
            90,
            91,
            18,
            19,
            44,
            45,
            68,
            69,
            92,
            93,
            20,
            21,
            46,
            47,
            70,
            71,
            94,
            95,
            22,
            23,
            48,
            49,
            72,
            73,
            96,
            97,
            24,
            25,
        ];

        REDIRECTS[idx]
    }
}

struct Indexes<R: Redirector>(std::ops::Range<usize>, std::marker::PhantomData<R>);

impl<R: Redirector> Indexes<R> {
    pub fn new() -> Indexes<R> {
        Indexes(0..consts::CODING_DIBITS, std::marker::PhantomData)
    }
}

impl<R: Redirector> Iterator for Indexes<R> {
    type Item = usize;

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(R::redirect)
    }
}

/// Takes ownership of a dibit buffer, yielding deinterleaved dibits.
pub struct Interleaver {
    /// Wrapped buffer.
    dibits: [bits::Dibit; consts::CODING_DIBITS],
    /// Redirected indexes.
    idx: Indexes<InterleaveRedirector>,
}

impl Interleaver {
    /// Construct a new `Interleaver` over the given buffer.
    pub fn new(dibits: [bits::Dibit; consts::CODING_DIBITS]) -> Interleaver {
        Interleaver {
            dibits: dibits,
            idx: Indexes::new(),
        }
    }
}

impl Iterator for Interleaver {
    type Item = bits::Dibit;

    fn next(&mut self) -> Option<Self::Item> {
        self.idx.next().map(|idx| self.dibits[idx])
    }
}

pub struct Deinterleaver<'a> {
    dibits: &'a [bits::Dibit; consts::CODING_DIBITS],
    idx: Indexes<DeinterleaveRedirector>,
}

impl<'a> Deinterleaver<'a> {
    pub fn new(dibits: &'a [bits::Dibit; consts::CODING_DIBITS]) -> Deinterleaver<'a> {
        Deinterleaver {
            dibits: dibits,
            idx: Indexes::new(),
        }
    }
}

impl<'a> Iterator for Deinterleaver<'a> {
    type Item = bits::Dibit;

    fn next(&mut self) -> Option<Self::Item> {
        self.idx.next().map(|idx| self.dibits[idx])
    }
}

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

    #[test]
    fn test_interleave() {
        let mut buf = [Dibit::default(); 98];

        for i in 0..98 {
            buf[i] = Dibit::new(i as u8 % 4);
        }

        let mut int = Interleaver::new(buf);

        for _ in 0..13 {
            assert_eq!(int.next().unwrap().bits(), 0b00);
            assert_eq!(int.next().unwrap().bits(), 0b01);
        }

        for _ in 0..12 {
            assert_eq!(int.next().unwrap().bits(), 0b10);
            assert_eq!(int.next().unwrap().bits(), 0b11);
        }

        for _ in 0..12 {
            assert_eq!(int.next().unwrap().bits(), 0b00);
            assert_eq!(int.next().unwrap().bits(), 0b01);
        }

        for _ in 0..12 {
            assert_eq!(int.next().unwrap().bits(), 0b10);
            assert_eq!(int.next().unwrap().bits(), 0b11);
        }

        assert!(int.next().is_none());
    }

    #[test]
    fn test_deinterleave() {
        let mut buf = [Dibit::default(); 98];

        for i in 0..98 {
            buf[i] = Dibit::new(i as u8 % 4);
        }

        let mut out = [Dibit::default(); 98];

        for (i, dibit) in Interleaver::new(buf).enumerate() {
            out[i] = dibit;
        }

        let mut deint = Deinterleaver::new(&out);

        for _ in 0..24 {
            assert_eq!(deint.next().unwrap().bits(), 0b00);
            assert_eq!(deint.next().unwrap().bits(), 0b01);
            assert_eq!(deint.next().unwrap().bits(), 0b10);
            assert_eq!(deint.next().unwrap().bits(), 0b11);
        }

        assert_eq!(deint.next().unwrap().bits(), 0b00);
        assert_eq!(deint.next().unwrap().bits(), 0b01);

        assert!(deint.next().is_none());
    }
}
<|endoftext|>