<commit_before>#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate radeco_lib;
extern crate r2pipe;
extern crate r2api;
extern crate base64;
extern crate rustyline;

use r2pipe::{R2, R2Pipe};
use radeco_lib::analysis::cse::cse::CSE;
use radeco_lib::analysis::interproc::fixcall::CallFixer;
use radeco_lib::analysis::sccp;
use radeco_lib::backend::lang_c::c_cfg::CCFGVerifier;
use radeco_lib::backend::lang_c::c_cfg::ctrl_flow_struct;
use radeco_lib::backend::lang_c::c_cfg_builder;
use radeco_lib::frontend::radeco_containers::*;
use radeco_lib::middle::{dce, dot};
use radeco_lib::middle::ir_writer;
use radeco_lib::middle::regfile::SubRegisterFile;
use radeco_lib::middle::ssa::ssastorage::SSAStorage;
use radeco_lib::middle::ssa::verifier;
use rustyline::Editor;

use rustyline::error::ReadlineError;
use std::cell::RefCell;
use std::collections::HashMap;
use std::env;

use std::fs;
use std::process;
use std::rc::Rc;
use std::str;
use std::sync::Arc;

mod scheme {
    pub const HTTP: &'static str = "http://";
    pub const TCP: &'static str = "tcp://";
}

// On unix platforms you can use ANSI escape sequences
#[cfg(unix)]
const PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";

// Windows consoles typically don't support ANSI escape sequences out
// of the box
#[cfg(windows)]
const PROMPT: &'static str = ">> ";

fn main() {
    let mut rl = Editor::<()>::new();
    let mut proj = env::args().nth(1).and_then(|ref s| {
        if is_http(s) {
            load_proj_http(&s[scheme::HTTP.len()..])
        } else if is_tcp(s) {
            load_proj_tcp(&s[scheme::TCP.len()..])
        } else if is_file(s) {
            Ok(load_proj_by_path(s))
        } else {
            Err("Invalid argument")
        }.ok()
    });
    loop {
        let readline = rl.readline(PROMPT);
        match readline {
            Ok(line) => {
                let mut terms = line.split_whitespace();
                let o1 = terms.next();
                let o2 = terms.next();
                cmd(o1, o2, &mut proj);
            }
            Err(ReadlineError::Interrupted) |
            Err(ReadlineError::Eof) => break,
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

mod command {
    pub const HELP: &'static str = "help";
    pub const LOAD: &'static str = "load";
    pub const CONNECT: &'static str = "connect";
    pub const FNLIST: &'static str = "fn_list";
    pub const ANALYZE: &'static str = "analyze";
    pub const DOT: &'static str = "dot";
    pub const IR: &'static str = "ir";
    pub const DECOMPILE: &'static str = "decompile";
}

fn help() {
    let width = 30;
    println!("{:width$}    Show this help", command::HELP, width = width);
    println!(
        "{:width$}    Load binary",
        format!("{} path", command::LOAD),
        width = width
    );
    println!(
        "{:width$}    Connect to radare2 server",
        format!("{} (http|tcp)://<url>", command::CONNECT),
        width = width
    );
    println!(
        "{:width$}    Show function list",
        command::FNLIST,
        width = width
    );
    println!(
        "{:width$}    Analyze <func>",
        format!("{} <func>", command::ANALYZE),
        width = width
    );
    println!(
        "{:width$}    Emit IR of <func>",
        format!("{} <func>", command::IR),
        width = width
    );
    println!(
        "{:width$}    Emit graph of the IR in Graphviz dot",
        format!("{} <func>", command::DOT),
        width = width
    );
    println!(
        "{:width$}    Decompile <func>",
        format!("{} <func>", command::DECOMPILE),
        width = width
    );
}

fn cmd(op1: Option<&str>, op2: Option<&str>, proj_opt: &mut Option<RadecoProject>) {
    match (op1, op2) {
        (Some(command::HELP), _) => {
            help();
            return;
        }
        (Some(command::LOAD), Some(path)) => {
            if is_file(path) {
                *proj_opt = Some(load_proj_by_path(path));
                return;
            } else {
                println!("{} is not found.", path);
                return;
            }
        }
        (Some(command::CONNECT), Some(url)) => {
            let p_opt = if is_http(&url) {
                load_proj_http(&url[7..])
            } else if is_tcp(&url) {
                load_proj_tcp(&url[6..])
            } else {
                Err("Invalid url")
            };
            match p_opt {
                Ok(proj) => *proj_opt = Some(proj),
                Err(msg) => println!("{}", msg),
            }
            return;
        }
        _ => {}
    };
    let mut proj = match proj_opt {
        Some(proj) => proj,
        None => {
            println!("Load a project first");
            return;
        }
    };
    match (op1, op2) {
        (Some(command::ANALYZE), Some("*")) => {
            let rfns = proj.iter_mut().map(|i| i.module).flat_map(|rmod| {
                rmod.functions.values_mut()
            });
            for rfn in rfns {
                analyze(rfn);
            }
        }
        (Some(command::FNLIST), _) => {
            let funcs = fn_list(&proj);
            println!("{}", funcs.join("\n"));
        }
        // TODO Show list of dependency information of analyses
        // TODO Add command for individual analyses
        (Some(command::ANALYZE), Some(f)) => {
            if let Some(rfn) = get_function_mut(f, &mut proj) {
                analyze(rfn);
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DOT), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_dot(rfn.ssa()));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::IR), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_ir(rfn));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DECOMPILE), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                let rmod = proj.iter().map(|i| i.module).next().unwrap();
                let func_name_map = func_names(&rmod);
                let strings = strings(&rmod);
                println!("{}", decompile(rfn, &func_name_map, &strings));
            } else {
                println!("{} is not found", f);
            }
        }
        _ => {
            println!(
                "Invalid command {} {}",
                op1.unwrap_or(""),
                op2.unwrap_or("")
            );
        }
    }
}

fn load_proj_by_path(path: &str) -> RadecoProject {
    let mut p = ProjectLoader::new().path(path).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

fn load_proj_tcp(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::tcp(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

fn load_proj_http(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::http(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

fn load_project_by_r2pipe(r2p: R2Pipe) -> RadecoProject {
    let r2 = R2::from(r2p);
    let r2w = Rc::new(RefCell::new(r2));
    let mut p = ProjectLoader::new().source(Rc::new(r2w)).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

fn fn_list(proj: &RadecoProject) -> Vec<String> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .map(|s| s.name.to_string())
        .collect()
}

fn get_function<'a>(name: &str, proj: &'a RadecoProject) -> Option<&'a RadecoFunction> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn get_function_mut<'a>(name: &str, proj: &'a mut RadecoProject) -> Option<&'a mut RadecoFunction> {
    proj.iter_mut()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values_mut())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn analyze_mod(regfile: Arc<SubRegisterFile>, rmod: &mut RadecoModule) {
    // Analyze preserved for all functions.
    {
        println!("[*] Fixing Callee Information");
        let bp_name = regfile.get_name_by_alias(&"BP".to_string());
        let bp_name = bp_name.map(|s| s.to_owned());
        let sp_name = regfile.get_name_by_alias(&"SP".to_string());
        let sp_name = sp_name.map(|s| s.to_owned());
        let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
        callfixer.rounded_analysis();
    }

    // Fix call sites
    radeco_lib::analysis::functions::fix_ssa_opcalls::go(rmod);

    // Infer calling conventions
    radeco_lib::analysis::functions::infer_regusage::run(rmod, &*regfile);
}

fn analyze(rfn: &mut RadecoFunction) {
    println!("[+] Analyzing: {} @ {:#x}", rfn.name, rfn.offset);
    {
        println!("  [*] Eliminating Dead Code");
        dce::collect(rfn.ssa_mut());
    }
    let mut ssa = {
        // Constant Propagation (sccp)
        println!("  [*] Propagating Constants");
        let mut analyzer = sccp::Analyzer::new(rfn.ssa_mut());
        analyzer.analyze();
        analyzer.emit_ssa()
    };
    {
        println!("  [*] Eliminating More DeadCode");
        dce::collect(&mut ssa);
    }
    *rfn.ssa_mut() = ssa;
    {
        // Common SubExpression Elimination (cse)
        println!("  [*] Eliminating Common SubExpressions");
        let mut cse = CSE::new(rfn.ssa_mut());
        cse.run();
    }
    {
        // Verify SSA
        println!("  [*] Verifying SSA's Validity");
        match verifier::verify(rfn.ssa()) {
            Err(e) => {
                println!("  [*] Found Error: {}", e);
                process::exit(255);
            }
            Ok(_) => {}
        }
    }
}

fn emit_ir(rfn: &RadecoFunction) -> String {
    println!("  [*] Writing out IR");
    let mut res = String::new();
    ir_writer::emit_il(&mut res, Some(rfn.name.to_string()), rfn.ssa()).unwrap();
    res
}

fn emit_dot(ssa: &SSAStorage) -> String {
    dot::emit_dot(ssa)
}

fn decompile(
    rfn: &RadecoFunction,
    func_name_map: &HashMap<u64, String>,
    strings: &HashMap<u64, String>,
) -> String {
    let c_cfg = c_cfg_builder::recover_c_cfg(rfn, func_name_map, strings);
    if let Err(err) = CCFGVerifier::verify(&c_cfg) {
        println!("CCFG verification failed");
        println!("{}", err);
    }
    ctrl_flow_struct::structure_and_convert(c_cfg)
        .unwrap()
        .print()
}

fn is_file(path: &str) -> bool {
    fs::metadata(path).map(|f| f.is_file()).unwrap_or(false)
}

fn is_http(url: &str) -> bool {
    url.starts_with(scheme::HTTP)
}

fn is_tcp(url: &str) -> bool {
    url.starts_with(scheme::TCP)
}

fn func_names(rmod: &RadecoModule) -> HashMap<u64, String> {
    rmod.functions
        .iter()
        .map(|(&addr, f)| (addr, f.name.to_string()))
        .collect()
}

fn strings(rmod: &RadecoModule) -> HashMap<u64, String> {
    rmod.strings()
        .iter()
        .filter(|ref s| s.paddr.is_some() && s.string.is_some())
        .cloned()
        .map(|s| {
            let (addr, _s) = (s.vaddr.unwrap(), s.string.unwrap());
            let bytes = base64::decode(&_s).unwrap_or(Vec::new());
            let ret_string = match str::from_utf8(bytes.as_slice()) {
                Ok(v) => v.to_string(),
                Err(_e) => _s,
            };
            (addr, ret_string)
        })
        .collect()
}
<commit_msg>Minor fixes<commit_after>#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate radeco_lib;
extern crate r2pipe;
extern crate r2api;
extern crate base64;
extern crate rustyline;

use r2pipe::{R2, R2Pipe};
use radeco_lib::analysis::cse::cse::CSE;
use radeco_lib::analysis::interproc::fixcall::CallFixer;
use radeco_lib::analysis::sccp;
use radeco_lib::backend::lang_c::c_cfg::CCFGVerifier;
use radeco_lib::backend::lang_c::c_cfg::ctrl_flow_struct;
use radeco_lib::backend::lang_c::c_cfg_builder;
use radeco_lib::frontend::radeco_containers::*;
use radeco_lib::middle::{dce, dot};
use radeco_lib::middle::ir_writer;
use radeco_lib::middle::regfile::SubRegisterFile;
use radeco_lib::middle::ssa::ssastorage::SSAStorage;
use radeco_lib::middle::ssa::verifier;
use rustyline::Editor;

use rustyline::error::ReadlineError;
use std::cell::RefCell;
use std::collections::HashMap;
use std::env;

use std::fs;
use std::process;
use std::rc::Rc;
use std::str;
use std::sync::Arc;

mod scheme {
    pub const HTTP: &'static str = "http://";
    pub const TCP: &'static str = "tcp://";
}

// On unix platforms you can use ANSI escape sequences
#[cfg(unix)]
const PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";

// Windows consoles typically don't support ANSI escape sequences out
// of the box
#[cfg(windows)]
const PROMPT: &'static str = ">> ";

fn main() {
    let mut rl = Editor::<()>::new();
    let mut proj = env::args().nth(1).and_then(|ref s| {
        if is_http(s) {
            load_proj_http(&s[scheme::HTTP.len()..])
        } else if is_tcp(s) {
            load_proj_tcp(&s[scheme::TCP.len()..])
        } else if is_file(s) {
            Ok(load_proj_by_path(s))
        } else {
            Err("Invalid argument")
        }.ok()
    });
    loop {
        let readline = rl.readline(PROMPT);
        match readline {
            Ok(line) => {
                let mut terms = line.split_whitespace();
                let o1 = terms.next();
                let o2 = terms.next();
                cmd(o1, o2, &mut proj);
            }
            Err(ReadlineError::Interrupted) |
            Err(ReadlineError::Eof) => break,
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }
        }
    }
}

mod command {
    pub const HELP: &'static str = "help";
    pub const LOAD: &'static str = "load";
    pub const CONNECT: &'static str = "connect";
    pub const FNLIST: &'static str = "fn_list";
    pub const ANALYZE: &'static str = "analyze";
    pub const DOT: &'static str = "dot";
    pub const IR: &'static str = "ir";
    pub const DECOMPILE: &'static str = "decompile";
}

fn help() {
    let width = 30;
    println!("{:width$}    Show this help", command::HELP, width = width);
    println!(
        "{:width$}    Load binary",
        format!("{} path", command::LOAD),
        width = width
    );
    println!(
        "{:width$}    Connect to radare2 server",
        format!("{} (http|tcp)://<url>", command::CONNECT),
        width = width
    );
    println!(
        "{:width$}    Show function list",
        command::FNLIST,
        width = width
    );
    println!(
        "{:width$}    Analyze <func>",
        format!("{} <func>", command::ANALYZE),
        width = width
    );
    println!(
        "{:width$}    Emit IR of <func>",
        format!("{} <func>", command::IR),
        width = width
    );
    println!(
        "{:width$}    Emit graph of the IR in Graphviz dot",
        format!("{} <func>", command::DOT),
        width = width
    );
    println!(
        "{:width$}    Decompile <func>",
        format!("{} <func>", command::DECOMPILE),
        width = width
    );
}

fn cmd(op1: Option<&str>, op2: Option<&str>, proj_opt: &mut Option<RadecoProject>) {
    match (op1, op2) {
        (Some(command::HELP), _) => {
            help();
            return;
        }
        (Some(command::LOAD), Some(path)) => {
            if is_file(path) {
                *proj_opt = Some(load_proj_by_path(path));
                return;
            } else {
                println!("{} is not found.", path);
                return;
            }
        }
        (Some(command::CONNECT), Some(url)) => {
            let p_opt = if is_http(&url) {
                load_proj_http(&url[scheme::HTTP.len()..])
            } else if is_tcp(&url) {
                load_proj_tcp(&url[scheme::TCP.len()..])
            } else {
                Err("Invalid url")
            };
            match p_opt {
                Ok(proj) => *proj_opt = Some(proj),
                Err(msg) => println!("{}", msg),
            }
            return;
        }
        _ => {}
    };
    let mut proj = match proj_opt {
        Some(proj) => proj,
        None => {
            println!("Load a project first");
            return;
        }
    };
    match (op1, op2) {
        (Some(command::ANALYZE), Some("*")) => {
            let rfns = proj.iter_mut().map(|i| i.module).flat_map(|rmod| {
                rmod.functions.values_mut()
            });
            for rfn in rfns {
                analyze(rfn);
            }
        }
        (Some(command::FNLIST), _) => {
            let funcs = fn_list(&proj);
            println!("{}", funcs.join("\n"));
        }
        // TODO Show list of dependency information of analyses
        // TODO Add command for individual analyses
        (Some(command::ANALYZE), Some(f)) => {
            if let Some(rfn) = get_function_mut(f, &mut proj) {
                analyze(rfn);
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DOT), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_dot(rfn.ssa()));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::IR), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                println!("{}", emit_ir(rfn));
            } else {
                println!("{} is not found", f);
            }
        }
        (Some(command::DECOMPILE), Some(f)) => {
            if let Some(rfn) = get_function(f, &proj) {
                let rmod = proj.iter().map(|i| i.module).next().unwrap();
                let func_name_map = func_names(&rmod);
                let strings = strings(&rmod);
                println!("{}", decompile(rfn, &func_name_map, &strings));
            } else {
                println!("{} is not found", f);
            }
        }
        _ => {
            println!(
                "Invalid command {} {}",
                op1.unwrap_or(""),
                op2.unwrap_or("")
            );
        }
    }
}

fn load_proj_by_path(path: &str) -> RadecoProject {
    let mut p = ProjectLoader::new().path(path).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

fn load_proj_tcp(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::tcp(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

fn load_proj_http(url: &str) -> Result<RadecoProject, &'static str> {
    let r2p = R2Pipe::http(url)?;
    Ok(load_project_by_r2pipe(r2p))
}

fn load_project_by_r2pipe(r2p: R2Pipe) -> RadecoProject {
    let r2 = R2::from(r2p);
    let r2w = Rc::new(RefCell::new(r2));
    let mut p = ProjectLoader::new().source(Rc::new(r2w)).load();
    let regfile = p.regfile().clone();
    for mut xy in p.iter_mut() {
        analyze_mod(regfile.clone(), xy.module);
    }
    p
}

fn fn_list(proj: &RadecoProject) -> Vec<String> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .map(|s| s.name.to_string())
        .collect()
}

fn get_function<'a>(name: &str, proj: &'a RadecoProject) -> Option<&'a RadecoFunction> {
    proj.iter()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn get_function_mut<'a>(name: &str, proj: &'a mut RadecoProject) -> Option<&'a mut RadecoFunction> {
    proj.iter_mut()
        .map(|i| i.module)
        .flat_map(|rmod| rmod.functions.values_mut())
        .filter(|rfn| rfn.name == name)
        .next()
}

fn analyze_mod(regfile: Arc<SubRegisterFile>, rmod: &mut RadecoModule) {
    // Analyze preserved for all functions.
    {
        println!("[*] Fixing Callee Information");
        let bp_name = regfile.get_name_by_alias(&"BP".to_string());
        let bp_name = bp_name.map(|s| s.to_owned());
        let sp_name = regfile.get_name_by_alias(&"SP".to_string());
        let sp_name = sp_name.map(|s| s.to_owned());
        let mut callfixer = CallFixer::new(rmod, bp_name, sp_name);
        callfixer.rounded_analysis();
    }

    // Fix call sites
    radeco_lib::analysis::functions::fix_ssa_opcalls::go(rmod);

    // Infer calling conventions
    radeco_lib::analysis::functions::infer_regusage::run(rmod, &*regfile);
}

fn analyze(rfn: &mut RadecoFunction) {
    println!("[+] Analyzing: {} @ {:#x}", rfn.name, rfn.offset);
    {
        println!("  [*] Eliminating Dead Code");
        dce::collect(rfn.ssa_mut());
    }
    let mut ssa = {
        // Constant Propagation (sccp)
        println!("  [*] Propagating Constants");
        let mut analyzer = sccp::Analyzer::new(rfn.ssa_mut());
        analyzer.analyze();
        analyzer.emit_ssa()
    };
    {
        println!("  [*] Eliminating More DeadCode");
        dce::collect(&mut ssa);
    }
    *rfn.ssa_mut() = ssa;
    {
        // Common SubExpression Elimination (cse)
        println!("  [*] Eliminating Common SubExpressions");
        let mut cse = CSE::new(rfn.ssa_mut());
        cse.run();
    }
    {
        // Verify SSA
        println!("  [*] Verifying SSA's Validity");
        match verifier::verify(rfn.ssa()) {
            Err(e) => {
                println!("  [*] Found Error: {}", e);
                process::exit(255);
            }
            Ok(_) => {}
        }
    }
}

fn emit_ir(rfn: &RadecoFunction) -> String {
    println!("  [*] Writing out IR");
    let mut res = String::new();
    ir_writer::emit_il(&mut res, Some(rfn.name.to_string()), rfn.ssa()).unwrap();
    res
}

fn emit_dot(ssa: &SSAStorage) -> String {
    dot::emit_dot(ssa)
}

fn decompile(
    rfn: &RadecoFunction,
    func_name_map: &HashMap<u64, String>,
    strings: &HashMap<u64, String>,
) -> String {
    let c_cfg = c_cfg_builder::recover_c_cfg(rfn, func_name_map, strings);
    if let Err(err) = CCFGVerifier::verify(&c_cfg) {
        println!("CCFG verification failed");
        println!("{}", err);
    }
    ctrl_flow_struct::structure_and_convert(c_cfg)
        .unwrap()
        .print()
}

fn is_file(path: &str) -> bool {
    fs::metadata(path).map(|f| f.is_file()).unwrap_or(false)
}

fn is_http(url: &str) -> bool {
    url.starts_with(scheme::HTTP)
}

fn is_tcp(url: &str) -> bool {
    url.starts_with(scheme::TCP)
}

fn func_names(rmod: &RadecoModule) -> HashMap<u64, String> {
    rmod.functions
        .iter()
        .map(|(&addr, f)| (addr, f.name.to_string()))
        .collect()
}

fn strings(rmod: &RadecoModule) -> HashMap<u64, String> {
    rmod.strings()
        .iter()
        .filter(|ref s| s.paddr.is_some() && s.string.is_some())
        .cloned()
        .map(|s| {
            let (addr, _s) = (s.vaddr.unwrap(), s.string.unwrap());
            let bytes = base64::decode(&_s).unwrap_or(Vec::new());
            let ret_string = match str::from_utf8(bytes.as_slice()) {
                Ok(v) => v.to_string(),
                Err(_e) => _s,
            };
            (addr, ret_string)
        })
        .collect()
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use std::fmt;
use std::hash::{Hash, Hasher};
use std::ptr;
use std::rc::Rc;
use std::str;

use super::pairs::{self, Pairs};
use super::queueable_token::QueueableToken;
use super::tokens::{self, Tokens};
use RuleType;
use span::{self, Span};

/// A matching pair of [`Token`]s and everything between them.
///
/// A matching `Token` pair is formed by a `Token::Start` and a subsequent `Token::End` with the
/// same `Rule`, with the condition that all `Token`s between them can form such pairs as well.
/// This is similar to the [brace matching problem](https://en.wikipedia.org/wiki/Brace_matching) in
/// editors.
///
/// [`Token`]: ../enum.Token.html
#[derive(Clone)]
pub struct Pair<'i, R> {
    /// # Safety
    ///
    /// All `QueueableToken`s' `input_pos` must be valid character boundary indices into `input`.
    queue: Rc<Vec<QueueableToken<R>>>,
    input: &'i str,
    /// Token index into `queue`.
    start: usize
}

// TODO(safety): QueueableTokens must be valid indices into input.
pub fn new<R: RuleType>(queue: Rc<Vec<QueueableToken<R>>>, input: &str, start: usize) -> Pair<R> {
    if cfg!(debug_assertions) {
        for tok in queue.iter() {
            match *tok {
                QueueableToken::Start { input_pos, .. } | QueueableToken::End { input_pos, .. } =>
                    assert!(input.get(input_pos..).is_some(), "💥 UNSAFE `Pair` CREATED 💥")
            }
        }
    }

    Pair {
        queue,
        input,
        start
    }
}

impl<'i, R: RuleType> Pair<'i, R> {
    /// Returns the `Rule` of the `Pair`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = "";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::a ...
    /// #     state.rule(Rule::a, |s| Ok(s))
    /// }).unwrap().next().unwrap();
    ///
    /// assert_eq!(pair.as_rule(), Rule::a);
    /// ```
    #[inline]
    pub fn as_rule(&self) -> R {
        match self.queue[self.pair()] {
            QueueableToken::End { rule, .. } => rule,
            _ => unreachable!()
        }
    }

    /// Captures a slice from the `&str` defined by the token `Pair`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     ab
    /// }
    ///
    /// let input = "ab";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::ab ...
    /// #     state.rule(Rule::ab, |s| s.match_string("ab"))
    /// }).unwrap().next().unwrap();
    ///
    /// assert_eq!(pair.as_str(), "ab");
    /// ```
    #[inline]
    pub fn as_str(&self) -> &'i str {
        let start = self.pos(self.start);
        let end = self.pos(self.pair());

        // Generated positions always come from Positions and are UTF-8 borders.
        &self.input[start..end]
    }

    /// Returns the `Span` defined by the `Pair`, consuming it.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     ab
    /// }
    ///
    /// let input = "ab";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::ab ...
    /// #     state.rule(Rule::ab, |s| s.match_string("ab"))
    /// }).unwrap().next().unwrap();
    ///
    /// assert_eq!(pair.into_span().as_str(), "ab");
    /// ```
    #[inline]
    #[deprecated(since = "2.0.0", note = "Please use `as_span` instead")]
    pub fn into_span(self) -> Span<'i> {
        self.as_span()
    }

    /// Returns the `Span` defined by the `Pair`, **without** consuming it.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     ab
    /// }
    ///
    /// let input = "ab";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::ab ...
    /// #     state.rule(Rule::ab, |s| s.match_string("ab"))
    /// }).unwrap().next().unwrap();
    ///
    /// assert_eq!(pair.as_span().as_str(), "ab");
    /// ```
    #[inline]
    pub fn as_span(&self) -> Span<'i> {
        let start = self.pos(self.start);
        let end = self.pos(self.pair());

        // Generated positions always come from Positions and are UTF-8 borders.
        unsafe { span::Span::new_unchecked(self.input, start, end) }
    }

    /// Returns the inner `Pairs` between the `Pair`, consuming it.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = "";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::a ...
    /// #     state.rule(Rule::a, |s| Ok(s))
    /// }).unwrap().next().unwrap();
    ///
    /// assert!(pair.into_inner().next().is_none());
    /// ```
    #[inline]
    pub fn into_inner(self) -> Pairs<'i, R> {
        let pair = self.pair();

        pairs::new(self.queue, self.input, self.start + 1, pair)
    }

    /// Returns the `Tokens` for the `Pair`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = "";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::a ...
    /// #     state.rule(Rule::a, |s| Ok(s))
    /// }).unwrap().next().unwrap();
    /// let tokens: Vec<_> = pair.tokens().collect();
    ///
    /// assert_eq!(tokens.len(), 2);
    /// ```
    #[inline]
    pub fn tokens(self) -> Tokens<'i, R> {
        let end = self.pair();

        tokens::new(self.queue, self.input, self.start, end + 1)
    }

    fn pair(&self) -> usize {
        match self.queue[self.start] {
            QueueableToken::Start {
                end_token_index, ..
            } => end_token_index,
            _ => unreachable!()
        }
    }

    fn pos(&self, index: usize) -> usize {
        match self.queue[index] {
            QueueableToken::Start { input_pos, .. } | QueueableToken::End { input_pos, .. } => {
                input_pos
            }
        }
    }
}

impl<'i, R: RuleType> fmt::Debug for Pair<'i, R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Pair")
            .field("rule", &self.as_rule())
            .field("span", &self.as_span())
            .field("inner", &self.clone().into_inner().collect::<Vec<_>>())
            .finish()
    }
}

impl<'i, R: RuleType> fmt::Display for Pair<'i, R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let rule = self.as_rule();
        let start = self.pos(self.start);
        let end = self.pos(self.pair());
        let mut pairs = self.clone().into_inner().peekable();

        if pairs.peek().is_none() {
            write!(f, "{:?}({}, {})", rule, start, end)
        } else {
            write!(
                f,
                "{:?}({}, {}, [{}])",
                rule,
                start,
                end,
                pairs
                    .map(|pair| format!("{}", pair))
                    .collect::<Vec<_>>()
                    .join(", ")
            )
        }
    }
}

impl<'i, R: PartialEq> PartialEq for Pair<'i, R> {
    fn eq(&self, other: &Pair<'i, R>) -> bool {
        Rc::ptr_eq(&self.queue, &other.queue) && ptr::eq(self.input, other.input)
            && self.start == other.start
    }
}

impl<'i, R: Eq> Eq for Pair<'i, R> {}

impl<'i, R: Hash> Hash for Pair<'i, R> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        (&*self.queue as *const Vec<QueueableToken<R>>).hash(state);
        (self.input.as_bytes() as *const [u8]).hash(state);
        self.start.hash(state);
    }
}

#[cfg(test)]
mod tests {
    use macros::tests::*;
    use parser::Parser;

    #[test]
    fn pair_into_inner() {
        let pair = AbcParser::parse(Rule::a, "abcde").unwrap().next().unwrap(); // the tokens a(b())

        let pairs = pair.into_inner(); // the tokens b()

        assert_eq!(2, pairs.tokens().count());
    }
}
<commit_msg>Update pest/src/iterators/pair.rs<commit_after>// pest. The Elegant Parser
// Copyright (c) 2018 Dragoș Tiselice
//
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use std::fmt;
use std::hash::{Hash, Hasher};
use std::ptr;
use std::rc::Rc;
use std::str;

use super::pairs::{self, Pairs};
use super::queueable_token::QueueableToken;
use super::tokens::{self, Tokens};
use RuleType;
use span::{self, Span};

/// A matching pair of [`Token`]s and everything between them.
///
/// A matching `Token` pair is formed by a `Token::Start` and a subsequent `Token::End` with the
/// same `Rule`, with the condition that all `Token`s between them can form such pairs as well.
/// This is similar to the [brace matching problem](https://en.wikipedia.org/wiki/Brace_matching) in
/// editors.
///
/// [`Token`]: ../enum.Token.html
#[derive(Clone)]
pub struct Pair<'i, R> {
    /// # Safety
    ///
    /// All `QueueableToken`s' `input_pos` must be valid character boundary indices into `input`.
    queue: Rc<Vec<QueueableToken<R>>>,
    input: &'i str,
    /// Token index into `queue`.
    start: usize
}

// TODO(safety): QueueableTokens must be valid indices into input.
pub fn new<R: RuleType>(queue: Rc<Vec<QueueableToken<R>>>, input: &str, start: usize) -> Pair<R> {
    if cfg!(debug_assertions) {
        for tok in queue.iter() {
            match *tok {
                QueueableToken::Start { input_pos, .. } | QueueableToken::End { input_pos, .. } =>
                    assert!(input.get(input_pos..).is_some(), "💥 UNSAFE `Pair` CREATED 💥")
            }
        }
    }

    Pair {
        queue,
        input,
        start
    }
}

impl<'i, R: RuleType> Pair<'i, R> {
    /// Returns the `Rule` of the `Pair`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = "";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::a ...
    /// #     state.rule(Rule::a, |s| Ok(s))
    /// }).unwrap().next().unwrap();
    ///
    /// assert_eq!(pair.as_rule(), Rule::a);
    /// ```
    #[inline]
    pub fn as_rule(&self) -> R {
        match self.queue[self.pair()] {
            QueueableToken::End { rule, .. } => rule,
            _ => unreachable!()
        }
    }

    /// Captures a slice from the `&str` defined by the token `Pair`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     ab
    /// }
    ///
    /// let input = "ab";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::ab ...
    /// #     state.rule(Rule::ab, |s| s.match_string("ab"))
    /// }).unwrap().next().unwrap();
    ///
    /// assert_eq!(pair.as_str(), "ab");
    /// ```
    #[inline]
    pub fn as_str(&self) -> &'i str {
        let start = self.pos(self.start);
        let end = self.pos(self.pair());

        // Generated positions always come from Positions and are UTF-8 borders.
        &self.input[start..end]
    }

    /// Returns the `Span` defined by the `Pair`, consuming it.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     ab
    /// }
    ///
    /// let input = "ab";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::ab ...
    /// #     state.rule(Rule::ab, |s| s.match_string("ab"))
    /// }).unwrap().next().unwrap();
    ///
    /// assert_eq!(pair.into_span().as_str(), "ab");
    /// ```
    #[inline]
    #[deprecated(since = "2.0.0", note = "Please use `as_span` instead")]
    pub fn into_span(self) -> Span<'i> {
        self.as_span()
    }

    /// Returns the `Span` defined by the `Pair`, **without** consuming it.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     ab
    /// }
    ///
    /// let input = "ab";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::ab ...
    /// #     state.rule(Rule::ab, |s| s.match_string("ab"))
    /// }).unwrap().next().unwrap();
    ///
    /// assert_eq!(pair.as_span().as_str(), "ab");
    /// ```
    #[inline]
    pub fn as_span(&self) -> Span<'i> {
        let start = self.pos(self.start);
        let end = self.pos(self.pair());

        // Generated positions always come from Positions and are UTF-8 borders.
        unsafe { span::Span::new_unchecked(self.input, start, end) }
    }

    /// Returns the inner `Pairs` between the `Pair`, consuming it.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = "";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::a ...
    /// #     state.rule(Rule::a, |s| Ok(s))
    /// }).unwrap().next().unwrap();
    ///
    /// assert!(pair.into_inner().next().is_none());
    /// ```
    #[inline]
    pub fn into_inner(self) -> Pairs<'i, R> {
        let pair = self.pair();

        pairs::new(self.queue, self.input, self.start + 1, pair)
    }

    /// Returns the `Tokens` for the `Pair`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::rc::Rc;
    /// # use pest;
    /// # #[allow(non_camel_case_types)]
    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    /// enum Rule {
    ///     a
    /// }
    ///
    /// let input = "";
    /// let pair = pest::state(input, |state| {
    ///     // generating Token pair with Rule::a ...
    /// #     state.rule(Rule::a, |s| Ok(s))
    /// }).unwrap().next().unwrap();
    /// let tokens: Vec<_> = pair.tokens().collect();
    ///
    /// assert_eq!(tokens.len(), 2);
    /// ```
    #[inline]
    pub fn tokens(self) -> Tokens<'i, R> {
        let end = self.pair();

        tokens::new(self.queue, self.input, self.start, end + 1)
    }

    fn pair(&self) -> usize {
        match self.queue[self.start] {
            QueueableToken::Start {
                end_token_index, ..
            } => end_token_index,
            _ => unreachable!()
        }
    }

    fn pos(&self, index: usize) -> usize {
        match self.queue[index] {
            QueueableToken::Start { input_pos, .. } | QueueableToken::End { input_pos, .. } => {
                input_pos
            }
        }
    }
}

impl<'i, R: RuleType> fmt::Debug for Pair<'i, R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Pair")
            .field("rule", &self.as_rule())
            .field("span", &self.as_span())
            .field("inner", &self.clone().into_inner().collect::<Vec<_>>())
            .finish()
    }
}

impl<'i, R: RuleType> fmt::Display for Pair<'i, R> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let rule = self.as_rule();
        let start = self.pos(self.start);
        let end = self.pos(self.pair());
        let mut pairs = self.clone().into_inner().peekable();

        if pairs.peek().is_none() {
            write!(f, "{:?}({}, {})", rule, start, end)
        } else {
            write!(
                f,
                "{:?}({}, {}, [{}])",
                rule,
                start,
                end,
                pairs
                    .map(|pair| format!("{}", pair))
                    .collect::<Vec<_>>()
                    .join(", ")
            )
        }
    }
}

impl<'i, R: PartialEq> PartialEq for Pair<'i, R> {
    fn eq(&self, other: &Pair<'i, R>) -> bool {
        Rc::ptr_eq(&self.queue, &other.queue) && ptr::eq(self.input, other.input)
            && self.start == other.start
    }
}

impl<'i, R: Eq> Eq for Pair<'i, R> {}

impl<'i, R: Hash> Hash for Pair<'i, R> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        (&*self.queue as *const Vec<QueueableToken<R>>).hash(state);
        (self.input as *const str).hash(state);
        self.start.hash(state);
    }
}

#[cfg(test)]
mod tests {
    use macros::tests::*;
    use parser::Parser;

    #[test]
    fn pair_into_inner() {
        let pair = AbcParser::parse(Rule::a, "abcde").unwrap().next().unwrap(); // the tokens a(b())

        let pairs = pair.into_inner(); // the tokens b()

        assert_eq!(2, pairs.tokens().count());
    }
}
<|endoftext|>


<commit_before>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Library of GPT disk data types.
//!
//! # GPT disk components
//!
//! ```text
//! ┌───┬───────┬─────────────────┬─────────┬───────────────────┬─────────┐
//! │MBR│Primary│Primary partition│Partition│Secondary partition│Secondary│
//! │   │header │entry array      │data     │entry array        │header   │
//! └───┴───────┴─────────────────┴─────────┴───────────────────┴─────────┘
//! ```
//!
//! 1. The first block of the disk contains a protective MBR. See
//! [`MasterBootRecord::protective_mbr`].
//! 2. The second block of the disk contains the primary GPT header. See
//! [`GptHeader`].
//! 3. Additional blocks after the header contain the partition entry
//! array. See [`GptPartitionEntry`] and [`GptPartitionEntryArray`].
//! 4. At the end of the disk is a secondary GPT header and partition
//! entry array.
//!
//! # Endianness
//!
//! The UEFI Specification specifies that data structures are little
//! endian (section 1.8.1 "Data Structure Descriptions"). Unless
//! otherwise noted, all fields in this library are little endian. This
//! is true even when running the code on a big-endian architecture; the
//! [`U16Le`], [`U32Le`], [`U64Le`], and [`LbaLe`] types help enforce
//! this. The little-endian convention is also used for [`Display`]
//! implementations. This means bytes within each field will appear
//! reversed when compared with a flat hex dump of GPT data.
//!
//! One notable exception is the [`Guid`] type, which is partially
//! little endian and partially big endian as described in Appendix A of
//! the UEFI Specification.
//!
//! # Features
//!
//! * `std`: Provides `std::error::Error` implementations for all of the
//!   error types. Off by default.
//!
//! [`Display`]: core::fmt::Display

#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts)]
#![warn(trivial_numeric_casts)]
#![warn(unreachable_pub)]
#![warn(unsafe_code)]
#![warn(unused_crate_dependencies)]
#![warn(clippy::pedantic)]
#![warn(clippy::as_conversions)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::module_name_repetitions)]

mod block;
mod crc32;
mod guid;
mod header;
mod mbr;
mod num;
mod partition_array;
mod partition_entry;
#[cfg(feature = "std")]
mod std_support;

// Re-export dependencies.
pub use bytemuck;
pub use crc;
pub use ucs2;

pub use block::{BlockSize, Lba, LbaLe, LbaRangeInclusive};
pub use crc32::Crc32;
pub use guid::{Guid, GuidFromStrError};
pub use header::{GptHeader, GptHeaderRevision, GptHeaderSignature};
pub use mbr::{Chs, DiskGeometry, MasterBootRecord, MbrPartitionRecord};
pub use num::{U16Le, U32Le, U64Le};
pub use partition_array::{
    GptPartitionEntryArray, GptPartitionEntryArrayError,
    GptPartitionEntryArrayLayout,
};
pub use partition_entry::{
    GptPartitionAttributes, GptPartitionEntry, GptPartitionEntrySize,
    GptPartitionEntrySizeError, GptPartitionName, GptPartitionNameFromStrError,
    GptPartitionNameSetCharError, GptPartitionType,
};
<commit_msg>Add more docstring examples<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Library of GPT disk data types.
//!
//! # GPT disk components
//!
//! ```text
//! ┌───┬───────┬─────────────────┬─────────┬───────────────────┬─────────┐
//! │MBR│Primary│Primary partition│Partition│Secondary partition│Secondary│
//! │   │header │entry array      │data     │entry array        │header   │
//! └───┴───────┴─────────────────┴─────────┴───────────────────┴─────────┘
//! ```
//!
//! 1. The first block of the disk contains a protective MBR. See
//! [`MasterBootRecord::protective_mbr`].
//! 2. The second block of the disk contains the primary GPT header. See
//! [`GptHeader`].
//! 3. Additional blocks after the header contain the partition entry
//! array. See [`GptPartitionEntry`] and [`GptPartitionEntryArray`].
//! 4. At the end of the disk is a secondary GPT header and partition
//! entry array.
//!
//! # Endianness
//!
//! The UEFI Specification specifies that data structures are little
//! endian (section 1.8.1 "Data Structure Descriptions"). Unless
//! otherwise noted, all fields in this library are little endian. This
//! is true even when running the code on a big-endian architecture; the
//! [`U16Le`], [`U32Le`], [`U64Le`], and [`LbaLe`] types help enforce
//! this. The little-endian convention is also used for [`Display`]
//! implementations. This means bytes within each field will appear
//! reversed when compared with a flat hex dump of GPT data.
//!
//! One notable exception is the [`Guid`] type, which is partially
//! little endian and partially big endian as described in Appendix A of
//! the UEFI Specification.
//!
//! [`Display`]: core::fmt::Display
//!
//! # Features
//!
//! * `std`: Provides `std::error::Error` implementations for all of the
//!   error types. Off by default.
//!
//! # Examples
//!
//! Construct a GPT header:
//!
//! ```
//! use gpt_disk_types::{Crc32, GptHeader, LbaLe, U32Le};
//!
//! let header = GptHeader {
//!     header_crc32: Crc32(U32Le::from_u32(0xa4877843)),
//!     my_lba: LbaLe::from_u64(1),
//!     alternate_lba: LbaLe::from_u64(8191),
//!     first_usable_lba: LbaLe::from_u64(34),
//!     last_usable_lba: LbaLe::from_u64(8158),
//!     disk_guid: "57a7feb6-8cd5-4922-b7bd-c78b0914e870".parse().unwrap(),
//!     partition_entry_lba: LbaLe::from_u64(2),
//!     number_of_partition_entries: U32Le::from_u32(128),
//!     partition_entry_array_crc32: Crc32(U32Le::from_u32(0x9206adff)),
//!     ..Default::default()
//! };
//! ```
//!
//! Construct a GPT partition entry:
//!
//! ```
//! use gpt_disk_types::{GptPartitionEntry, LbaLe};
//!
//! let entry = GptPartitionEntry {
//!     partition_type_guid: "ccf0994f-f7e0-4e26-a011-843e38aa2eac"
//!         .parse()
//!         .unwrap(),
//!     unique_partition_guid: "37c75ffd-8932-467a-9c56-8cf1f0456b12"
//!         .parse()
//!         .unwrap(),
//!     starting_lba: LbaLe::from_u64(2048),
//!     ending_lba: LbaLe::from_u64(4096),
//!     attributes: Default::default(),
//!     name: "hello world!".parse().unwrap(),
//! };
//! ```

#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts)]
#![warn(trivial_numeric_casts)]
#![warn(unreachable_pub)]
#![warn(unsafe_code)]
#![warn(unused_crate_dependencies)]
#![warn(clippy::pedantic)]
#![warn(clippy::as_conversions)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::module_name_repetitions)]

mod block;
mod crc32;
mod guid;
mod header;
mod mbr;
mod num;
mod partition_array;
mod partition_entry;
#[cfg(feature = "std")]
mod std_support;

// Re-export dependencies.
pub use bytemuck;
pub use crc;
pub use ucs2;

pub use block::{BlockSize, Lba, LbaLe, LbaRangeInclusive};
pub use crc32::Crc32;
pub use guid::{Guid, GuidFromStrError};
pub use header::{GptHeader, GptHeaderRevision, GptHeaderSignature};
pub use mbr::{Chs, DiskGeometry, MasterBootRecord, MbrPartitionRecord};
pub use num::{U16Le, U32Le, U64Le};
pub use partition_array::{
    GptPartitionEntryArray, GptPartitionEntryArrayError,
    GptPartitionEntryArrayLayout,
};
pub use partition_entry::{
    GptPartitionAttributes, GptPartitionEntry, GptPartitionEntrySize,
    GptPartitionEntrySizeError, GptPartitionName, GptPartitionNameFromStrError,
    GptPartitionNameSetCharError, GptPartitionType,
};
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::mem;
use std::cell::UnsafeCell;

pub struct ReentrantMutex { inner: UnsafeCell<ffi::CRITICAL_SECTION> }

unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}

impl ReentrantMutex {
    pub unsafe fn uninitialized() -> ReentrantMutex {
        mem::uninitialized()
    }

    //pub unsafe fn init(&mut self) -> ReentrantMutex {
    //    ffi::InitializeCriticalSection(self.inner.get());
    //}
    pub unsafe fn init(&mut self) {
        ffi::InitializeCriticalSection(self.inner.get());
    }

    pub unsafe fn lock(&self) {
        ffi::EnterCriticalSection(self.inner.get());
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        ffi::TryEnterCriticalSection(self.inner.get()) != 0
    }

    pub unsafe fn unlock(&self) {
        ffi::LeaveCriticalSection(self.inner.get());
    }

    pub unsafe fn destroy(&self) {
        ffi::DeleteCriticalSection(self.inner.get());
    }
}

mod ffi {
    use libc::{LPVOID, LONG, HANDLE, c_ulong, BOOLEAN};
    pub type ULONG_PTR = c_ulong;

    #[repr(C)]
    pub struct CRITICAL_SECTION {
        CriticalSectionDebug: LPVOID,
        LockCount: LONG,
        RecursionCount: LONG,
        OwningThread: HANDLE,
        LockSemaphore: HANDLE,
        SpinCount: ULONG_PTR
    }

    extern "system" {
        pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN;
        pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
    }
}
<commit_msg>+ allow non-standard type names<commit_after>// Copyright 2015 The Rust Project Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::mem;
use std::cell::UnsafeCell;

pub struct ReentrantMutex { inner: UnsafeCell<ffi::CRITICAL_SECTION> }

unsafe impl Send for ReentrantMutex {}
unsafe impl Sync for ReentrantMutex {}

impl ReentrantMutex {
    pub unsafe fn uninitialized() -> ReentrantMutex {
        mem::uninitialized()
    }

    //pub unsafe fn init(&mut self) -> ReentrantMutex {
    //    ffi::InitializeCriticalSection(self.inner.get());
    //}
    pub unsafe fn init(&mut self) {
        ffi::InitializeCriticalSection(self.inner.get());
    }

    pub unsafe fn lock(&self) {
        ffi::EnterCriticalSection(self.inner.get());
    }

    #[inline]
    pub unsafe fn try_lock(&self) -> bool {
        ffi::TryEnterCriticalSection(self.inner.get()) != 0
    }

    pub unsafe fn unlock(&self) {
        ffi::LeaveCriticalSection(self.inner.get());
    }

    pub unsafe fn destroy(&self) {
        ffi::DeleteCriticalSection(self.inner.get());
    }
}

mod ffi {
    use libc::{LPVOID, LONG, HANDLE, c_ulong, BOOLEAN};
    #[allow(non_camel_case_types)]
    pub type ULONG_PTR = c_ulong;

    #[allow(non_snake_case)]
    #[repr(C)]
    pub struct CRITICAL_SECTION {
        CriticalSectionDebug: LPVOID,
        LockCount: LONG,
        RecursionCount: LONG,
        OwningThread: HANDLE,
        LockSemaphore: HANDLE,
        SpinCount: ULONG_PTR
    }

    extern "system" {
        pub fn InitializeCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn EnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn TryEnterCriticalSection(CriticalSection: *mut CRITICAL_SECTION) -> BOOLEAN;
        pub fn LeaveCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
        pub fn DeleteCriticalSection(CriticalSection: *mut CRITICAL_SECTION);
    }
}
<|endoftext|>


<commit_before><commit_msg>impl skip/limit for count<commit_after><|endoftext|>


<commit_before>use super::super::{Y, X, Coord, Grid};
use super::*;

extern crate rand;

use rand::{Rng, distributions};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Edge {
    Undecided,
    Line,
    Blank,
}

struct AnswerField {
    height: i32,
    width: i32,
    field: Grid<Edge>,
    invalid: bool,
}

impl AnswerField {
    fn new(height: i32, width: i32) -> AnswerField {
        AnswerField {
            height: height,
            width: width,
            field: Grid::new(2 * height - 1, 2 * width - 1, Edge::Undecided),
            invalid: false,
        }
    }
    fn get(&self, cd: Coord) -> Edge {
        if self.field.is_valid_coord(cd) {
            self.field[cd]
        } else {
            Edge::Blank
        }
    }
    /// Counts the number of (Line, Undecided) around `cd`
    fn count_neighbor(&self, cd: Coord) -> (i32, i32) {
        let (Y(y), X(x)) = cd;
        let mut line = 0;
        let mut undecided = 0;
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        for &(dy, dx) in &dirs {
            let e = self.get((Y(y + dy), X(x + dx)));
            if e == Edge::Line {
                line += 1;
            } else if e == Edge::Undecided {
                undecided += 1;
            }
        }
        (line, undecided)
    }
    /// Returns all neighbors whose state is `Undecided` around `cd`
    fn undecided_neighbors(&self, cd: Coord) -> Vec<Coord> {
        let (Y(y), X(x)) = cd;
        let mut ret = vec![];
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        for &(dy, dx) in &dirs {
            let cd2 = (Y(y + dy), X(x + dx));
            let e = self.get(cd2);
            if e == Edge::Undecided {
                ret.push(cd2);
            }
        }
        ret
    }
    fn decide(&mut self, cd: Coord, state: Edge) {
        let current = self.field[cd];
        if current != Edge::Undecided {
            if current != state {
                self.invalid = true;
            }
            return;
        }
        self.field[cd] = state;

        // check incident vertices
        let (Y(y), X(x)) = cd;
        if y % 2 == 1 {
            self.inspect((Y(y - 1), X(x)));
            self.inspect((Y(y + 1), X(x)));
        } else {
            self.inspect((Y(y), X(x - 1)));
            self.inspect((Y(y), X(x + 1)));
        }

        // check for canonization rule
        if state == Edge::Line {
            if y % 2 == 1 {
                let related = [
                    (Y(y), X(x - 2)),
                    (Y(y - 1), X(x - 1)),
                    (Y(y + 1), X(x - 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
                let related = [
                    (Y(y), X(x + 2)),
                    (Y(y - 1), X(x + 1)),
                    (Y(y + 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
            } else {
                let related = [
                    (Y(y - 2), X(x)),
                    (Y(y - 1), X(x - 1)),
                    (Y(y - 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
                let related = [
                    (Y(y + 2), X(x)),
                    (Y(y + 1), X(x - 1)),
                    (Y(y + 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
            }
        }
    }
    /// Inspect vertex (y, x)
    fn inspect(&mut self, (Y(y), X(x)): Coord) {
        let (line, undecided) = self.count_neighbor((Y(y), X(x)));
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        if line == 0 {
            if undecided == 0 {
                self.invalid = true;
                return;
            }
            if undecided == 1 {
                for &(dy, dx) in &dirs {
                    let e = self.get((Y(y + dy), X(x + dx)));
                    if e == Edge::Undecided {
                        self.decide((Y(y + dy), X(x + dx)), Edge::Line);
                    }
                }
            }
        } else if line == 2 {
            for &(dy, dx) in &dirs {
                let e = self.get((Y(y + dy), X(x + dx)));
                if e == Edge::Undecided {
                    self.decide((Y(y + dy), X(x + dx)), Edge::Blank);
                }
            }
        }
    }
}

pub fn generate_placement<R: Rng>(height: i32, width: i32, rng: &mut R) -> Option<Grid<Clue>> {
    let mut field = AnswerField::new(height, width);

    loop {
        let mut cand = vec![];
        for y in 0..height {
            for x in 0..width {
                let y = y * 2;
                let x = x * 2;
                let (line, undecided) = field.count_neighbor((Y(y), X(x)));
                if (line == 0 && undecided == 2) || (line == 1 && undecided > 0) {
                    cand.push((Y(y), X(x)));
                }
            }
        }

        if cand.len() == 0 { break; }

        let idx = rng.gen_range(0, cand.len());
        let cd = cand[idx];

        if field.count_neighbor(cd) == (0, 2) {
            let nbs = field.undecided_neighbors(cd);
            if rng.next_f64() < 0.9f64 {
                // as angle
                field.decide(nbs[0], Edge::Line);
                field.decide(nbs[1], Edge::Line);
            } else {
                // as an endpoint
                let i = rng.gen_range(0, 2);
                field.decide(nbs[i], Edge::Line);
                field.decide(nbs[(1 - i)], Edge::Blank);
            }
        } else {
            let nbs = field.undecided_neighbors(cd);

            if rng.next_f64() < 1.0f64 {
                // extend
                let i = rng.gen_range(0, nbs.len());
                field.decide(nbs[i], Edge::Line);
            } else {
                // terminate
                for nb in nbs {
                    field.decide(nb, Edge::Blank);
                }
            }
        }
    }

    if field.invalid { return None; }

    let mut ids = Grid::new(height, width, -1);
    let mut id = 1;
    for y in 0..height {
        for x in 0..width {
            if ids[(Y(y), X(x))] == -1 {
                fill_line_id((Y(y), X(x)), &field, &mut ids, id);
                id += 1;
            }
        }
    }

    let mut line_len = vec![0; id as usize];
    for y in 0..height {
        for x in 0..width {
            line_len[ids[(Y(y), X(x))] as usize] += 1;
        }
    }
    for i in 1..id {
        if line_len[i as usize] <= 3 { return None; }
    }

    let mut end_count = vec![0; id as usize];
    for y in 0..height {
        for x in 0..width {
            if field.count_neighbor((Y(y * 2), X(x * 2))) == (1, 0) {
                end_count[ids[(Y(y), X(x))] as usize] += 1;
            }
        }
    }
    for i in 1..id {
        if end_count[i as usize] != 2 { return None; }
    }

    for y in 0..(2 * height - 1) {
        for x in 0..(2 * width - 1) {
            if y % 2 == 1 && x % 2 == 0 {
                if (ids[(Y(y / 2), X(x / 2))] == ids[(Y(y / 2 + 1), X(x / 2))]) != (field.get((Y(y), X(x))) == Edge::Line) { return None; }
            } else if y % 2 == 0 && x % 2 == 1 {
                if (ids[(Y(y / 2), X(x / 2))] == ids[(Y(y / 2), X(x / 2 + 1))]) != (field.get((Y(y), X(x))) == Edge::Line) { return None; }
            }
        }
    }

    let mut ret = Grid::new(height, width, NO_CLUE);
    for y in 0..height {
        for x in 0..width {
            if field.count_neighbor((Y(y * 2), X(x * 2))) == (1, 0) {
                ret[(Y(y), X(x))] = Clue(ids[(Y(y), X(x))]);
            }
        }
    }

    Some(ret)
}
fn fill_line_id(cd: Coord, field: &AnswerField, ids: &mut Grid<i32>, id: i32) {
    if ids[cd] != -1 { return; }
    ids[cd] = id;
    let (Y(y), X(x)) = cd;

    let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
    for &(dy, dx) in &dirs {
        if field.get((Y(y * 2 + dy), X(x * 2 + dx))) == Edge::Line {
            fill_line_id((Y(y + dy), X(x + dx)), field, ids, id);
        }
    }
}
<commit_msg>Numberlink: optimize generate_placement O(H^2W^2) -> O(HW)<commit_after>use super::super::{Y, X, Coord, Grid};
use super::*;

extern crate rand;

use rand::{Rng, distributions};

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Edge {
    Undecided,
    Line,
    Blank,
}

struct AnswerField {
    height: i32,
    width: i32,
    field: Grid<Edge>,
    seed_idx: Grid<i32>,
    seeds: Vec<Coord>,
    invalid: bool,
}

impl AnswerField {
    fn new(height: i32, width: i32) -> AnswerField {
        let mut ret = AnswerField {
            height: height,
            width: width,
            field: Grid::new(2 * height - 1, 2 * width - 1, Edge::Undecided),
            seed_idx: Grid::new(2 * height - 1, 2 * width - 1, -1),
            seeds: vec![],
            invalid: false,
        };

        ret.seeds.push((Y(0), X(0)));
        ret.seeds.push((Y(0), X(2 * width - 2)));
        ret.seeds.push((Y(2 * height - 2), X(0)));
        ret.seeds.push((Y(2 * height - 2), X(2 * width - 2)));
        ret.seed_idx[(Y(0), X(0))] = 0;
        ret.seed_idx[(Y(0), X(2 * width - 2))] = 1;
        ret.seed_idx[(Y(2 * height - 2), X(0))] = 2;
        ret.seed_idx[(Y(2 * height - 2), X(2 * width - 2))] = 3;
        ret
    }
    fn get(&self, cd: Coord) -> Edge {
        if self.field.is_valid_coord(cd) {
            self.field[cd]
        } else {
            Edge::Blank
        }
    }
    /// Counts the number of (Line, Undecided) around `cd`
    fn count_neighbor(&self, cd: Coord) -> (i32, i32) {
        let (Y(y), X(x)) = cd;
        let mut line = 0;
        let mut undecided = 0;
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        for &(dy, dx) in &dirs {
            let e = self.get((Y(y + dy), X(x + dx)));
            if e == Edge::Line {
                line += 1;
            } else if e == Edge::Undecided {
                undecided += 1;
            }
        }
        (line, undecided)
    }
    /// Returns all neighbors whose state is `Undecided` around `cd`
    fn undecided_neighbors(&self, cd: Coord) -> Vec<Coord> {
        let (Y(y), X(x)) = cd;
        let mut ret = vec![];
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        for &(dy, dx) in &dirs {
            let cd2 = (Y(y + dy), X(x + dx));
            let e = self.get(cd2);
            if e == Edge::Undecided {
                ret.push(cd2);
            }
        }
        ret
    }
    /// Returns whether vertex `cd` is a seed
    fn is_seed(&self, cd: Coord) -> bool {
        let nb = self.count_neighbor(cd);
        nb == (0, 2) || (nb.0 == 1 && nb.1 > 0)
    }
    fn num_seeds(&self) -> usize { self.seeds.len() }

    /// Returns whether there is at least one seed
    fn has_seed(&self) -> bool {
        self.seeds.len() != 0
    }
    /// Returns a random seed using `rng`
    fn random_seed<R: Rng>(&self, rng: &mut R) -> Coord {
        let idx = rng.gen_range(0, self.seeds.len());
        self.seeds[idx]
    }
    fn decide(&mut self, cd: Coord, state: Edge) {
        let current = self.field[cd];
        if current != Edge::Undecided {
            if current != state {
                self.invalid = true;
            }
            return;
        }
        self.field[cd] = state;

        // check incident vertices
        let (Y(y), X(x)) = cd;
        if y % 2 == 1 {
            self.inspect((Y(y - 1), X(x)));
            self.inspect((Y(y + 1), X(x)));
        } else {
            self.inspect((Y(y), X(x - 1)));
            self.inspect((Y(y), X(x + 1)));
        }

        // check for canonization rule
        if state == Edge::Line {
            if y % 2 == 1 {
                let related = [
                    (Y(y), X(x - 2)),
                    (Y(y - 1), X(x - 1)),
                    (Y(y + 1), X(x - 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
                let related = [
                    (Y(y), X(x + 2)),
                    (Y(y - 1), X(x + 1)),
                    (Y(y + 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
            } else {
                let related = [
                    (Y(y - 2), X(x)),
                    (Y(y - 1), X(x - 1)),
                    (Y(y - 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
                let related = [
                    (Y(y + 2), X(x)),
                    (Y(y + 1), X(x - 1)),
                    (Y(y + 1), X(x + 1)),
                ];
                for i in 0..3 {
                    if self.get(related[i]) == Edge::Line {
                        self.decide(related[(i + 1) % 3], Edge::Blank);
                        self.decide(related[(i + 2) % 3], Edge::Blank);
                    }
                }
            }
        }
    }
    /// Inspect vertex (y, x)
    fn inspect(&mut self, (Y(y), X(x)): Coord) {
        let (line, undecided) = self.count_neighbor((Y(y), X(x)));
        let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
        if line == 0 {
            if undecided == 0 {
                self.invalid = true;
                return;
            }
            if undecided == 1 {
                for &(dy, dx) in &dirs {
                    let e = self.get((Y(y + dy), X(x + dx)));
                    if e == Edge::Undecided {
                        self.decide((Y(y + dy), X(x + dx)), Edge::Line);
                    }
                }
            }
        } else if line == 2 {
            for &(dy, dx) in &dirs {
                let e = self.get((Y(y + dy), X(x + dx)));
                if e == Edge::Undecided {
                    self.decide((Y(y + dy), X(x + dx)), Edge::Blank);
                }
            }
        }

        let is_seed = self.is_seed((Y(y), X(x)));
        let seed_idx = self.seed_idx[(Y(y), X(x))];

        if seed_idx != -1 && !is_seed {
            // (y, x) is no longer a seed
            let moved = self.seeds[self.seeds.len() - 1];
            self.seed_idx[moved] = seed_idx;
            self.seeds.swap_remove(seed_idx as usize);
            self.seed_idx[(Y(y), X(x))] = -1;
        } else if seed_idx == -1 && is_seed {
            // (y, x) is now a seed
            self.seed_idx[(Y(y), X(x))] = self.seeds.len() as i32;
            self.seeds.push((Y(y), X(x)));
        }
    }
}

pub fn generate_placement<R: Rng>(height: i32, width: i32, rng: &mut R) -> Option<Grid<Clue>> {
    let mut field = AnswerField::new(height, width);

    loop {
        if !field.has_seed() { break; }
        let cd = field.random_seed(rng);

        if field.count_neighbor(cd) == (0, 2) {
            let nbs = field.undecided_neighbors(cd);
            if rng.next_f64() < 0.9f64 {
                // as angle
                field.decide(nbs[0], Edge::Line);
                field.decide(nbs[1], Edge::Line);
            } else {
                // as an endpoint
                let i = rng.gen_range(0, 2);
                field.decide(nbs[i], Edge::Line);
                field.decide(nbs[(1 - i)], Edge::Blank);
            }
        } else {
            let nbs = field.undecided_neighbors(cd);

            if rng.next_f64() < 1.0f64 {
                // extend
                let i = rng.gen_range(0, nbs.len());
                field.decide(nbs[i], Edge::Line);
            } else {
                // terminate
                for nb in nbs {
                    field.decide(nb, Edge::Blank);
                }
            }
        }

        if field.invalid { return None; }
    }

    let mut ids = Grid::new(height, width, -1);
    let mut id = 1;
    for y in 0..height {
        for x in 0..width {
            if ids[(Y(y), X(x))] == -1 {
                fill_line_id((Y(y), X(x)), &field, &mut ids, id);
                id += 1;
            }
        }
    }

    let mut line_len = vec![0; id as usize];
    for y in 0..height {
        for x in 0..width {
            line_len[ids[(Y(y), X(x))] as usize] += 1;
        }
    }
    for i in 1..id {
        if line_len[i as usize] <= 3 { return None; }
    }

    let mut end_count = vec![0; id as usize];
    for y in 0..height {
        for x in 0..width {
            if field.count_neighbor((Y(y * 2), X(x * 2))) == (1, 0) {
                end_count[ids[(Y(y), X(x))] as usize] += 1;
            }
        }
    }
    for i in 1..id {
        if end_count[i as usize] != 2 { return None; }
    }

    for y in 0..(2 * height - 1) {
        for x in 0..(2 * width - 1) {
            if y % 2 == 1 && x % 2 == 0 {
                if (ids[(Y(y / 2), X(x / 2))] == ids[(Y(y / 2 + 1), X(x / 2))]) != (field.get((Y(y), X(x))) == Edge::Line) { return None; }
            } else if y % 2 == 0 && x % 2 == 1 {
                if (ids[(Y(y / 2), X(x / 2))] == ids[(Y(y / 2), X(x / 2 + 1))]) != (field.get((Y(y), X(x))) == Edge::Line) { return None; }
            }
        }
    }

    let mut ret = Grid::new(height, width, NO_CLUE);
    for y in 0..height {
        for x in 0..width {
            if field.count_neighbor((Y(y * 2), X(x * 2))) == (1, 0) {
                ret[(Y(y), X(x))] = Clue(ids[(Y(y), X(x))]);
            }
        }
    }

    Some(ret)
}
fn fill_line_id(cd: Coord, field: &AnswerField, ids: &mut Grid<i32>, id: i32) {
    if ids[cd] != -1 { return; }
    ids[cd] = id;
    let (Y(y), X(x)) = cd;

    let dirs = [(1, 0), (0, 1), (-1, 0), (0, -1)];
    for &(dy, dx) in &dirs {
        if field.get((Y(y * 2 + dy), X(x * 2 + dx))) == Edge::Line {
            fill_line_id((Y(y + dy), X(x + dx)), field, ids, id);
        }
    }
}
<|endoftext|>


<commit_before>#![cfg(not(windows))]

extern crate futures;
extern crate log;
extern crate tokio_core;
extern crate trust_dns;
extern crate trust_dns_proto;

mod server_harness;

use std::env;
use std::fs::File;
use std::path::Path;
use std::io::*;
use std::net::*;

use tokio_core::reactor::Core;

use trust_dns::client::*;
use trust_dns::tcp::TcpClientStream;
use trust_dns::rr::dnssec::*;

use server_harness::*;

#[cfg(all(not(feature = "dnssec-ring"), feature = "dnssec-openssl"))]
fn confg_toml() -> &'static str {
    "openssl_dnssec.toml"
}

#[cfg(all(feature = "dnssec-ring", not(feature = "dnssec-openssl")))]
fn confg_toml() -> &'static str {
    "ring_dnssec.toml"
}

#[cfg(all(feature = "dnssec-ring", feature = "dnssec-openssl"))]
fn confg_toml() -> &'static str {
    "all_supported_dnssec.toml"
}

fn trust_anchor(public_key_path: &Path, format: KeyFormat, algorithm: Algorithm) -> TrustAnchor {
    let mut file = File::open(public_key_path).expect("key not found");
    let mut buf = Vec::<u8>::new();

    file.read_to_end(&mut buf).expect("could not read key");
    let key_pair = format.decode_key(&buf, Some("123456"), algorithm).expect(
        "could not decode key",
    );

    let public_key = key_pair.to_public_key().unwrap();
    let mut trust_anchor = TrustAnchor::new();

    trust_anchor.insert_trust_anchor(public_key);
    trust_anchor
}

fn standard_conn(port: u16, io_loop: &Core) -> BasicClientHandle {
    let addr: SocketAddr = ("127.0.0.1", port)
        .to_socket_addrs()
        .unwrap()
        .next()
        .unwrap();
    let (stream, sender) = TcpClientStream::new(addr, &io_loop.handle());
    ClientFuture::new(stream, sender, &io_loop.handle(), None)
}

fn generic_test(config_toml: &str, key_path: &str, key_format: KeyFormat, algorithm: Algorithm) {
    // use trust_dns::logger;
    // use log::LogLevel;
    // logger::TrustDnsLogger::enable_logging(LogLevel::Debug);

    let server_path = env::var("TDNS_SERVER_SRC_ROOT").unwrap_or(".".to_owned());
    let server_path = Path::new(&server_path);

    named_test_harness(config_toml, |port, _| {
        let mut io_loop = Core::new().unwrap();

        // verify all records are present
        let client = standard_conn(port, &io_loop);
        query_all_dnssec_with_rfc6975(&mut io_loop, client, algorithm);
        let client = standard_conn(port, &io_loop);
        query_all_dnssec_wo_rfc6975(&mut io_loop, client, algorithm);

        // test that request with Secure client is successful, i.e. validates chain
        let trust_anchor = trust_anchor(&server_path.join(key_path), key_format, algorithm);
        let client = standard_conn(port, &io_loop);
        let mut client = SecureClientHandle::with_trust_anchor(client, trust_anchor);

        query_a(&mut io_loop, &mut client);
    });
}

#[test]
#[cfg(feature = "dnssec-openssl")]
fn test_rsa_sha256() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/rsa_2048.pem",
        KeyFormat::Pem,
        Algorithm::RSASHA256,
    );
}

#[test]
#[cfg(feature = "dnssec-openssl")]
fn test_rsa_sha512() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/rsa_2048.pem",
        KeyFormat::Pem,
        Algorithm::RSASHA512,
    );
}

#[test]
#[cfg(feature = "dnssec-openssl")]
fn test_ecdsa_p256() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/ecdsa_p256.pem",
        KeyFormat::Pem,
        Algorithm::ECDSAP256SHA256,
    );
}

#[test]
#[cfg(feature = "dnssec-openssl")]
fn test_ecdsa_p384() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/ecdsa_p384.pem",
        KeyFormat::Pem,
        Algorithm::ECDSAP384SHA384,
    );
}

#[test]
#[cfg(feature = "dnssec-ring")]
fn test_ed25519() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/ed25519.pk8",
        KeyFormat::Pkcs8,
        Algorithm::ED25519,
    );
}

#[test]
#[should_panic]
fn test_rsa_sha1_fails() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/rsa_2048.pem",
        KeyFormat::Pem,
        Algorithm::RSASHA1,
    );
}

#[cfg(feature = "dnssec-openssl")]
#[test]
fn test_dnssec_restart_with_update_journal() {
    // TODO: make journal path configurable, it should be in target/tests/...
    let server_path = env::var("TDNS_SERVER_SRC_ROOT").unwrap_or(".".to_owned());
    let server_path = Path::new(&server_path);
    let journal = server_path.join("tests/named_test_configs/example.com.jrnl");
    std::fs::remove_file(&journal).ok();

    generic_test(
        "dnssec_with_update.toml",
        "tests/named_test_configs/dnssec/rsa_2048.pem",
        KeyFormat::Pem,
        Algorithm::RSASHA256,
    );

    // after running the above test, the journal file should exist
    assert!(journal.exists());

    // and all dnssec tests should still pass
    generic_test(
        "dnssec_with_update.toml",
        "tests/named_test_configs/dnssec/rsa_2048.pem",
        KeyFormat::Pem,
        Algorithm::RSASHA256,
    );

    // and journal should still exist
    assert!(journal.exists());

    // cleanup...
    // TODO: fix journal path so that it doesn't leave the dir dirty...
    std::fs::remove_file(&journal).expect("failed to cleanup after test");
}
<commit_msg>Server: Enable tests/z_named_test_rsa_dnssec.rs on Windows.<commit_after>#![cfg(feature = "dnssec")]

extern crate futures;
extern crate log;
extern crate tokio_core;
extern crate trust_dns;
extern crate trust_dns_proto;

mod server_harness;

use std::env;
use std::fs::File;
use std::path::Path;
use std::io::*;
use std::net::*;

use tokio_core::reactor::Core;

use trust_dns::client::*;
use trust_dns::tcp::TcpClientStream;
use trust_dns::rr::dnssec::*;

use server_harness::*;

#[cfg(all(not(feature = "dnssec-ring"), feature = "dnssec-openssl"))]
fn confg_toml() -> &'static str {
    "openssl_dnssec.toml"
}

#[cfg(all(feature = "dnssec-ring", not(feature = "dnssec-openssl")))]
fn confg_toml() -> &'static str {
    "ring_dnssec.toml"
}

#[cfg(all(feature = "dnssec-ring", feature = "dnssec-openssl"))]
fn confg_toml() -> &'static str {
    "all_supported_dnssec.toml"
}

fn trust_anchor(public_key_path: &Path, format: KeyFormat, algorithm: Algorithm) -> TrustAnchor {
    let mut file = File::open(public_key_path).expect("key not found");
    let mut buf = Vec::<u8>::new();

    file.read_to_end(&mut buf).expect("could not read key");
    let key_pair = format.decode_key(&buf, Some("123456"), algorithm).expect(
        "could not decode key",
    );

    let public_key = key_pair.to_public_key().unwrap();
    let mut trust_anchor = TrustAnchor::new();

    trust_anchor.insert_trust_anchor(public_key);
    trust_anchor
}

fn standard_conn(port: u16, io_loop: &Core) -> BasicClientHandle {
    let addr: SocketAddr = ("127.0.0.1", port)
        .to_socket_addrs()
        .unwrap()
        .next()
        .unwrap();
    let (stream, sender) = TcpClientStream::new(addr, &io_loop.handle());
    ClientFuture::new(stream, sender, &io_loop.handle(), None)
}

fn generic_test(config_toml: &str, key_path: &str, key_format: KeyFormat, algorithm: Algorithm) {
    // use trust_dns::logger;
    // use log::LogLevel;
    // logger::TrustDnsLogger::enable_logging(LogLevel::Debug);

    let server_path = env::var("TDNS_SERVER_SRC_ROOT").unwrap_or(".".to_owned());
    let server_path = Path::new(&server_path);

    named_test_harness(config_toml, |port, _| {
        let mut io_loop = Core::new().unwrap();

        // verify all records are present
        let client = standard_conn(port, &io_loop);
        query_all_dnssec_with_rfc6975(&mut io_loop, client, algorithm);
        let client = standard_conn(port, &io_loop);
        query_all_dnssec_wo_rfc6975(&mut io_loop, client, algorithm);

        // test that request with Secure client is successful, i.e. validates chain
        let trust_anchor = trust_anchor(&server_path.join(key_path), key_format, algorithm);
        let client = standard_conn(port, &io_loop);
        let mut client = SecureClientHandle::with_trust_anchor(client, trust_anchor);

        query_a(&mut io_loop, &mut client);
    });
}

#[test]
#[cfg(feature = "dnssec-openssl")]
fn test_rsa_sha256() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/rsa_2048.pem",
        KeyFormat::Pem,
        Algorithm::RSASHA256,
    );
}

#[test]
#[cfg(feature = "dnssec-openssl")]
fn test_rsa_sha512() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/rsa_2048.pem",
        KeyFormat::Pem,
        Algorithm::RSASHA512,
    );
}

#[test]
#[cfg(feature = "dnssec-openssl")]
fn test_ecdsa_p256() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/ecdsa_p256.pem",
        KeyFormat::Pem,
        Algorithm::ECDSAP256SHA256,
    );
}

#[test]
#[cfg(feature = "dnssec-openssl")]
fn test_ecdsa_p384() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/ecdsa_p384.pem",
        KeyFormat::Pem,
        Algorithm::ECDSAP384SHA384,
    );
}

#[test]
#[cfg(feature = "dnssec-ring")]
fn test_ed25519() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/ed25519.pk8",
        KeyFormat::Pkcs8,
        Algorithm::ED25519,
    );
}

#[test]
#[should_panic]
fn test_rsa_sha1_fails() {
    generic_test(
        confg_toml(),
        "tests/named_test_configs/dnssec/rsa_2048.pem",
        KeyFormat::Pem,
        Algorithm::RSASHA1,
    );
}

#[cfg(feature = "dnssec-openssl")]
#[test]
fn test_dnssec_restart_with_update_journal() {
    // TODO: make journal path configurable, it should be in target/tests/...
    let server_path = env::var("TDNS_SERVER_SRC_ROOT").unwrap_or(".".to_owned());
    let server_path = Path::new(&server_path);
    let journal = server_path.join("tests/named_test_configs/example.com.jrnl");
    std::fs::remove_file(&journal).ok();

    generic_test(
        "dnssec_with_update.toml",
        "tests/named_test_configs/dnssec/rsa_2048.pem",
        KeyFormat::Pem,
        Algorithm::RSASHA256,
    );

    // after running the above test, the journal file should exist
    assert!(journal.exists());

    // and all dnssec tests should still pass
    generic_test(
        "dnssec_with_update.toml",
        "tests/named_test_configs/dnssec/rsa_2048.pem",
        KeyFormat::Pem,
        Algorithm::RSASHA256,
    );

    // and journal should still exist
    assert!(journal.exists());

    // cleanup...
    // TODO: fix journal path so that it doesn't leave the dir dirty...
    std::fs::remove_file(&journal).expect("failed to cleanup after test");
}
<|endoftext|>


<commit_before><commit_msg>blank message<commit_after><|endoftext|>


<commit_before><commit_msg>empty message<commit_after><|endoftext|>


<commit_before>//! Defines the `SearchExecutor` trait.

use std::time::Duration;
use std::sync::Arc;
use std::sync::mpsc::TryRecvError;
use uci::SetOption;
use moves::Move;
use value::*;
use depth::*;
use hash_table::*;
use search_node::SearchNode;
use time_manager::RemainingTime;

/// Parameters describing a search.
///
/// **Important note:** `lower_bound` and `upper_bound` fields
/// together give the interval within which an as precise as possible
/// evaluation is required. If during the search is determined that
/// the exact evaluation is outside of this interval, the search may
/// return a value that is closer to the the interval bounds than the
/// exact evaluation, but always staying on the correct side of the
/// interval (i.e. "fail-soft").
#[derive(Clone)]
pub struct SearchParams<T: SearchNode> {
    /// A number identifying the search.
    pub search_id: usize,

    /// The root position for the search.
    pub position: T,

    /// The requested search depth.
    ///
    /// Should be between `0` and `DEPTH_MAX`.
    pub depth: Depth,

    /// The lower bound for the search.
    ///
    /// Should be no lesser than `VALUE_MIN`.
    pub lower_bound: Value,

    /// The upper bound for the search.
    ///
    /// Should be greater than `lower_bound`, but no greater than
    /// `VALUE_MAX`.
    pub upper_bound: Value,

    /// Restricts the analysis to the supplied list of moves only.
    ///
    /// * All moves in the list should be legal.
    ///
    /// * The same move should not occur more than once.
    ///
    /// * If the root position is final, the supplied list of moves
    ///   should be empty.
    ///
    /// The behavior of the search is *undefined* if the root position
    /// is not final, but `searchmoves` is empty.
    pub searchmoves: Vec<Move>,
}

/// A progress report from a search.
#[derive(Clone)]
pub struct SearchReport<T> {
    /// The ID assigned to the search.
    ///
    /// Should be the same for all reports from a given search.
    pub search_id: usize,

    /// The number of positions searched so far.
    ///
    /// Should be no lesser than the value sent in the previous
    /// report.
    pub searched_nodes: u64,

    /// The search depth completed so far.
    ///
    /// Should be no lesser than `0`. Also, no lesser than the value
    /// sent in the previous report, and no greater than the requested
    /// search depth. If the search has not been forcefully stopped,
    /// the last reported `depth` should be the requested search
    /// depth.
    ///
    /// **Note:** Depth-first searches should send `0` in all reports
    /// except the last one.
    pub depth: Depth,

    /// The evaluation of the root position so far, or `VALUE_UNKNOWN`
    /// if not available.
    ///
    /// If the search has not been forcefully stopped, the last report
    /// should contain the calculated final evaluation.
    ///
    /// **Note:** Depth-first searches should send `VALUE_UNKNOWN` in
    /// all reports except the last one.
    pub value: Value,

    /// Whether the search is done.
    ///
    /// Should be `false` for all reports except the last one.
    pub done: bool,

    /// Auxiliary data.
    ///
    /// For example, this may contain calculated principal
    /// variation(s).
    pub data: T,
}

/// A trait for executing consecutive searches in different starting
/// positions.
///
/// Chess programs must rely on some type of search in order to play
/// reasonably. Searching involves looking ahead at different move
/// sequences and evaluating the positions after making the
/// moves. Normally, this is done by traversing and min-maxing a
/// tree-like data-structure by some algorithm. To implement your own
/// search algorithm, you must define a type that implements the
/// `SearchExecutor` trait.
///
/// There are two types of searches that should be distinguished:
///
/// * **Depth-first search.**
///
///   Starts at the root and explores as far as possible along each
///   branch before backtracking.
///
/// * **Iterative deepening search.**
///
///   A depth-first search is executed with a depth of one ply, then
///   the depth is incremented and another search is executed. This
///   process is repeated until the search is terminated or the
///   requested search depth is reached. In case of a terminated
///   search, the engine can always fall back to the move selected in
///   the last iteration of the search.
///
///   You can use `stock::Deepening` to turn a depth-first searcher
///   into a deepening searcher.
///
/// Here is what the engine does on each move:
///
/// 1. Calls `start_search`.
///
/// 2. Continues calling `wait_report` and `try_recv_report`
///    periodically, until the returned report indicates that the
///    search is done.
///
/// 3. Obtains the principal variation(s) from search reports, or
///    directly from the transposition table.
pub trait SearchExecutor: SetOption {
    /// The type of transposition (hash) table that the implementation
    /// works with.
    type HashTable: HashTable;

    /// The type of search node that the implementation works with.
    type SearchNode: SearchNode;

    /// The type of auxiliary data that search progress reports carry.
    type ReportData;

    /// Creates a new instance.
    fn new(tt: Arc<Self::HashTable>) -> Self;

    /// Starts a new search.
    ///
    /// * `params` describes the new search.
    ///
    /// * `time` gives the remaining time on the clocks, which is
    ///   useful when the search executor wants to implement its own
    ///   time management logic. Otherwise, it can be ignored. `None`
    ///   indicates that there are no time restrictions.
    ///
    /// After calling `start_search`, `wait_report` and
    /// `try_recv_report` will be called periodically until the
    /// returned report indicates that the search is done. A new
    /// search will not be started until the previous search is done.
    ///
    /// **Important note:** The executing search must send periodic
    /// reports, informing about its current progress. Also, the
    /// executing search must continuously update the transposition
    /// table so that, at each moment, it contains the results of the
    /// work done so far.
    fn start_search(&mut self,
                    params: SearchParams<Self::SearchNode>,
                    time: Option<&RemainingTime>);

    /// Attempts to return a search progress report without blocking.
    fn try_recv_report(&mut self) -> Result<SearchReport<Self::ReportData>, TryRecvError>;

    /// Waits until a search progress report is available, timing out
    /// after a specified duration or earlier.
    fn wait_report(&self, duration: Duration);

    /// Requests the termination of the current search.
    ///
    /// Can be called more than once for the same search. After
    /// calling `terminate_search`, `wait_report` and
    /// `try_recv_report` will continue to be called periodically
    /// until the returned report indicates that the search is done.
    fn terminate_search(&mut self);
}
<commit_msg>clarify SearchExecutor's docs<commit_after>//! Defines the `SearchExecutor` trait.

use std::time::Duration;
use std::sync::Arc;
use std::sync::mpsc::TryRecvError;
use uci::SetOption;
use moves::Move;
use value::*;
use depth::*;
use hash_table::*;
use search_node::SearchNode;
use time_manager::RemainingTime;

/// Parameters describing a search.
///
/// **Important note:** `lower_bound` and `upper_bound` fields
/// together give the interval within which an as precise as possible
/// evaluation is required. If during the search is determined that
/// the exact evaluation is outside of this interval, the search may
/// return a value that is closer to the the interval bounds than the
/// exact evaluation, but always staying on the correct side of the
/// interval (i.e. "fail-soft").
#[derive(Clone)]
pub struct SearchParams<T: SearchNode> {
    /// A number identifying the search.
    pub search_id: usize,

    /// The root position for the search.
    pub position: T,

    /// The requested search depth.
    ///
    /// Should be between `0` and `DEPTH_MAX`.
    pub depth: Depth,

    /// The lower bound for the search.
    ///
    /// Should be no lesser than `VALUE_MIN`.
    pub lower_bound: Value,

    /// The upper bound for the search.
    ///
    /// Should be greater than `lower_bound`, but no greater than
    /// `VALUE_MAX`.
    pub upper_bound: Value,

    /// Restricts the analysis to the supplied list of moves only.
    ///
    /// * All moves in the list should be legal.
    ///
    /// * The same move should not occur more than once.
    ///
    /// * If the root position is final, the supplied list of moves
    ///   should be empty.
    ///
    /// The behavior of the search is *undefined* if the root position
    /// is not final, but `searchmoves` is empty.
    pub searchmoves: Vec<Move>,
}

/// A progress report from a search.
#[derive(Clone)]
pub struct SearchReport<T> {
    /// The ID assigned to the search.
    ///
    /// Should be the same for all reports from a given search.
    pub search_id: usize,

    /// The number of positions searched so far.
    ///
    /// Should be no lesser than the value sent in the previous
    /// report.
    pub searched_nodes: u64,

    /// The search depth completed so far.
    ///
    /// Should be no lesser than `0`. Also, no lesser than the value
    /// sent in the previous report, and no greater than the requested
    /// search depth. If the search has not been forcefully stopped,
    /// the last reported `depth` should be the requested search
    /// depth.
    ///
    /// **Note:** Depth-first searches should send `0` in all reports
    /// except the last one.
    pub depth: Depth,

    /// The evaluation of the root position so far, or `VALUE_UNKNOWN`
    /// if not available.
    ///
    /// If the search has not been forcefully stopped, the last report
    /// should contain the calculated final evaluation.
    ///
    /// **Note:** Depth-first searches should send `VALUE_UNKNOWN` in
    /// all reports except the last one.
    pub value: Value,

    /// Whether the search is done.
    ///
    /// Should be `false` for all reports except the last one.
    pub done: bool,

    /// Auxiliary data.
    ///
    /// For example, this may contain calculated principal
    /// variation(s).
    pub data: T,
}

/// A trait for executing consecutive searches in different starting
/// positions.
///
/// Chess programs must rely on some type of search in order to play
/// reasonably. Searching involves looking ahead at different move
/// sequences and evaluating the positions after making the
/// moves. Normally, this is done by traversing and min-maxing a
/// tree-like data-structure by some algorithm. To implement your own
/// search algorithm, you must define a type that implements the
/// `SearchExecutor` trait.
///
/// There are two types of searches that should be distinguished:
///
/// * **Depth-first search.**
///
///   Starts at the root and explores as far as possible along each
///   branch before backtracking.
///
/// * **Iterative deepening search.**
///
///   A depth-first search is executed with a depth of one ply, then
///   the depth is incremented and another search is executed. This
///   process is repeated until the search is terminated or the
///   requested search depth is reached. In case of a terminated
///   search, the engine can always fall back to the move selected in
///   the last iteration of the search.
///
///   You can use `stock::Deepening` to turn a depth-first searcher
///   into a deepening searcher.
///
/// Here is what the engine does on each move:
///
/// 1. Calls `start_search`.
///
/// 2. Continues calling `wait_report` and `try_recv_report`
///    periodically, until the returned report indicates that the
///    search is done.
///
/// 3. Obtains the principal variation(s) from search reports, or
///    directly from the transposition table.
pub trait SearchExecutor: SetOption {
    /// The type of transposition (hash) table that the implementation
    /// works with.
    type HashTable: HashTable;

    /// The type of search node that the implementation works with.
    type SearchNode: SearchNode;

    /// The type of auxiliary data that search progress reports carry.
    type ReportData;

    /// Creates a new instance.
    fn new(tt: Arc<Self::HashTable>) -> Self;

    /// Starts a new search.
    ///
    /// * `params` describes the new search.
    ///
    /// * `time` gives the remaining time on the clocks, which is
    ///   useful when the search executor wants to implement its own
    ///   time management logic. Otherwise, it can be ignored. `None`
    ///   indicates that there are no time restrictions.
    ///
    /// This method must not block the current thread. After calling
    /// `start_search`, `wait_report` and `try_recv_report` will be
    /// called periodically until the returned report indicates that
    /// the search is done. A new search will not be started until the
    /// previous search is done.
    ///
    /// **Important note:** The executing search must send periodic
    /// reports, informing about its current progress. Also, the
    /// executing search must continuously update the transposition
    /// table so that, at each moment, it contains the results of the
    /// work done so far.
    fn start_search(&mut self,
                    params: SearchParams<Self::SearchNode>,
                    time: Option<&RemainingTime>);

    /// Attempts to return a search progress report without blocking.
    fn try_recv_report(&mut self) -> Result<SearchReport<Self::ReportData>, TryRecvError>;

    /// Waits until a search progress report is available, timing out
    /// after a specified duration or earlier.
    fn wait_report(&self, duration: Duration);

    /// Requests the termination of the current search.
    ///
    /// Can be called more than once for the same search. After
    /// calling `terminate_search`, `wait_report` and
    /// `try_recv_report` will continue to be called periodically
    /// until the returned report indicates that the search is done.
    fn terminate_search(&mut self);
}
<|endoftext|>