<commit_before>#[macro_use]
extern crate clap;
extern crate rls_analysis;
extern crate rls_data as data;
extern crate serde;
#[macro_use]
extern crate serde_json;

use data::GlobalCrateId;
use rls_analysis::{AnalysisHost, AnalysisLoader};
use std::collections::HashMap;
use std::fs::{self, File};
use std::path::{Path, PathBuf};

/// A global definition id in a crate.
///
/// FIXME(emilio): This key is kind of slow, because GlobalCrateId contains a
/// String. There's a "disambiguator" field which may be more than enough for
/// our purposes.
#[derive(Clone, Hash, Debug, Eq, PartialEq)]
pub struct DefId(GlobalCrateId, u32);

/// A map from global definition ids to the actual definition.
pub struct Defs {
    map: HashMap<DefId, data::Def>,
}

impl Defs {
    fn new() -> Self {
        Self { map: HashMap::new() }
    }

    fn insert(&mut self, analysis: &data::Analysis, def: &data::Def) {
        let crate_id = analysis.prelude.as_ref().unwrap().crate_id.clone();
        let mut definition = def.clone();
        let crate_independent_qualname =
            format!("{}{}", crate_id.name, def.qualname);
        definition.qualname = crate_independent_qualname;

        let index = definition.id.index;
        let previous = self.map.insert(DefId(crate_id, index), definition);
        if previous.is_none() {
            // This shouldn't happen, but as of right now it can happen with
            // some builtin definitions when highly generic types are involved.
            // This is probably a rust bug, just ignore it for now.
            eprintln!(
                "Found a definition with the same ID twice? {:?}, {:?}",
                previous,
                def,
            );
        }
    }

    /// Getter for a given local id, which takes care of converting to a global
    /// ID and returning the definition if present.
    fn get(&self, analysis: &data::Analysis, id: data::Id) -> Option<data::Def> {
        let prelude = analysis.prelude.as_ref().unwrap();
        let krate_id = if id.krate == 0 {
            prelude.crate_id.clone()
        } else {
            // TODO(emilio): This escales with the number of crates in this
            // particular crate, but it's probably not too bad, since it should
            // be a pretty fast linear search.
            let krate = prelude.external_crates.iter().find(|krate| {
                krate.num == id.krate
            });

            let krate = match krate {
                Some(k) => k,
                None => {
                    eprintln!("Crate not found: {:?}", id);
                    return None;
                }
            };

            krate.id.clone()
        };

        let id = DefId(krate_id, id.index);
        self.map.get(&id).cloned()
    }
}

#[derive(Clone)]
pub struct Loader {
    deps_dir: PathBuf,
}

impl Loader {
    pub fn new(deps_dir: PathBuf) -> Self {
        Self { deps_dir }
    }
}

impl AnalysisLoader for Loader {
    fn needs_hard_reload(&self, _: &Path) -> bool {
        true
    }

    fn fresh_host(&self) -> AnalysisHost<Self> {
        AnalysisHost::new_with_loader(self.clone())
    }

    fn set_path_prefix(&mut self, _: &Path) {}

    fn abs_path_prefix(&self) -> Option<PathBuf> {
        None
    }
    fn search_directories(&self) -> Vec<PathBuf> {
        vec![self.deps_dir.clone()]
    }
}

// Searchfox uses 1-indexed lines, 0-indexed columns.
fn span_to_string(span: &data::SpanData) -> String {
    // Rust spans are multi-line... So we just set the start column if it spans
    // multiple rows, searchfox has fallback code to handle this.
    if span.line_start != span.line_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    if span.column_start == span.column_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    format!("{}:{}-{}", span.line_start.0, span.column_start.zero_indexed().0, span.column_end.zero_indexed().0)
}

fn visit(
    file: &mut File,
    kind: &'static str,
    location: &data::SpanData,
    qualname: &str,
    context: Option<&str>,
) {
    use serde_json::map::Map;
    use serde_json::value::Value;
    use std::io::Write;

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("target".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    out.insert("pretty".into(), Value::String(qualname.into()));
    out.insert("sym".into(), Value::String(qualname.into()));
    if let Some(context) = context {
        out.insert("context".into(), Value::String(context.into()));
        out.insert("contextsym".into(), Value::String(context.into()));
    }

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    file.write_all(object.as_bytes()).unwrap();
    write!(file, "\n").unwrap();

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("source".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    out.insert("pretty".into(), Value::String(qualname.into()));
    out.insert("sym".into(), Value::String(qualname.into()));

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    file.write_all(object.as_bytes()).unwrap();
    write!(file, "\n").unwrap();
}

fn analyze_file(
    file_name: &PathBuf,
    defs: &Defs,
    file_analysis: &data::Analysis,
    src_dir: &Path,
    output_dir: &Path,
) {
    let file = match file_name.strip_prefix(src_dir) {
        Ok(f) => f,
        Err(..) => {
            eprintln!("File not in the source directory: {}", file_name.display());
            return;
        }
    };

    let output_file = output_dir.join(file);
    let mut output_dir = output_file.clone();
    output_dir.pop();
    if let Err(err) = fs::create_dir_all(output_dir) {
        eprintln!(
            "Couldn't create dir for: {}, {:?}",
            output_file.display(),
            err
        );
        return;
    }
    let mut file = match File::create(&output_file) {
        Ok(f) => f,
        Err(err) => {
            eprintln!(
                "Couldn't open output file: {}, {:?}",
                output_file.display(),
                err
            );
            return;
        }
    };

    for import in &file_analysis.imports {
        let id = match import.ref_id {
            Some(id) => id,
            None => {
                eprintln!("Dropping import {}: {}, no ref", import.name, import.value);
                continue;
            }
        };

        let def = match defs.get(file_analysis, id) {
            Some(def) => def,
            None => {
                eprintln!("Dropping import {}: {}, no def for ref {:?}", import.name, import.value, id);
                continue;
            }
        };

        visit(&mut file, "use", &import.span, &def.qualname, None)
    }

    for def in &file_analysis.defs {
        let parent = def.parent
            .and_then(|parent_id| defs.get(file_analysis, parent_id).map(|d| d.qualname));

        let crate_name = &file_analysis.prelude.as_ref().unwrap().crate_id.name;
        let qualname = format!("{}{}", crate_name, def.qualname);
        visit(&mut file, "def", &def.span, &qualname, parent.as_ref().map(|p| &**p))
    }

    for ref_ in &file_analysis.refs {
        let def = match defs.get(file_analysis, ref_.ref_id) {
            Some(d) => d,
            None => {
                eprintln!("Dropping ref {:?}, kind {:?}, no def", ref_.ref_id, ref_.kind);
                continue;
            }
        };
        visit(
            &mut file,
            "use",
            &ref_.span,
            &def.qualname,
            /* context = */ None, // TODO
        )
    }
}

fn analyze_crate(
    analysis: &data::Analysis,
    defs: &Defs,
    src_dir: &Path,
    output_dir: &Path,
) {
    let mut per_file = HashMap::new();

    println!("Analyzing crate: {:?}", analysis.prelude);

    macro_rules! flat_map_per_file {
        ($field:ident) => {
            for item in &analysis.$field {
                let mut file_analysis =
                    per_file.entry(item.span.file_name.clone())
                        .or_insert_with(|| {
                            let prelude = analysis.prelude.clone();
                            let mut analysis = data::Analysis::new(analysis.config.clone());
                            analysis.prelude = prelude;
                            analysis
                        });
                file_analysis.$field.push(item.clone());
            }
        }
    }

    flat_map_per_file!(imports);
    flat_map_per_file!(defs);
    flat_map_per_file!(impls);
    flat_map_per_file!(refs);
    flat_map_per_file!(macro_refs);
    flat_map_per_file!(relations);

    for (mut name, analysis) in per_file.drain() {
        if name.is_relative() {
            name = src_dir.join(name);
        }
        analyze_file(&name, defs, &analysis, src_dir, output_dir);
    }
}

fn main() {
    let matches = app_from_crate!()
        .args_from_usage(
            "<src>    'Points to the source root'
             <input>  'Points to the deps/save-analysis directory'
             <output> 'Points to the directory where searchfox metadata should go'",
        )
        .get_matches();

    let src_dir = Path::new(matches.value_of("src").unwrap());
    let input_dir = Path::new(matches.value_of("input").unwrap());
    let output_dir = Path::new(matches.value_of("output").unwrap());

    let loader = Loader::new(PathBuf::from(input_dir));

    let crates = rls_analysis::read_analysis_from_files(&loader, Default::default(), &[]);

    let mut defs = Defs::new();
    for krate in &crates {
        for def in &krate.analysis.defs {
            println!("Indexing def: {:?}", def);
            defs.insert(&krate.analysis, def);
        }
    }

    for krate in crates {
        analyze_crate(&krate.analysis, &defs, &src_dir, &output_dir);
    }
}
<commit_msg>rust-indexer: Fix error handling with duplicate definitions.<commit_after>#[macro_use]
extern crate clap;
extern crate rls_analysis;
extern crate rls_data as data;
extern crate serde;
#[macro_use]
extern crate serde_json;

use data::GlobalCrateId;
use rls_analysis::{AnalysisHost, AnalysisLoader};
use std::collections::HashMap;
use std::fs::{self, File};
use std::path::{Path, PathBuf};

/// A global definition id in a crate.
///
/// FIXME(emilio): This key is kind of slow, because GlobalCrateId contains a
/// String. There's a "disambiguator" field which may be more than enough for
/// our purposes.
#[derive(Clone, Hash, Debug, Eq, PartialEq)]
pub struct DefId(GlobalCrateId, u32);

/// A map from global definition ids to the actual definition.
pub struct Defs {
    map: HashMap<DefId, data::Def>,
}

impl Defs {
    fn new() -> Self {
        Self { map: HashMap::new() }
    }

    fn insert(&mut self, analysis: &data::Analysis, def: &data::Def) {
        let crate_id = analysis.prelude.as_ref().unwrap().crate_id.clone();
        let mut definition = def.clone();
        let crate_independent_qualname =
            format!("{}{}", crate_id.name, def.qualname);
        definition.qualname = crate_independent_qualname;

        let index = definition.id.index;
        let previous = self.map.insert(DefId(crate_id, index), definition);
        if let Some(previous) = previous {
            // This shouldn't happen, but as of right now it can happen with
            // some builtin definitions when highly generic types are involved.
            // This is probably a rust bug, just ignore it for now.
            eprintln!(
                "Found a definition with the same ID twice? {:?}, {:?}",
                previous,
                def,
            );
        }
    }

    /// Getter for a given local id, which takes care of converting to a global
    /// ID and returning the definition if present.
    fn get(&self, analysis: &data::Analysis, id: data::Id) -> Option<data::Def> {
        let prelude = analysis.prelude.as_ref().unwrap();
        let krate_id = if id.krate == 0 {
            prelude.crate_id.clone()
        } else {
            // TODO(emilio): This escales with the number of crates in this
            // particular crate, but it's probably not too bad, since it should
            // be a pretty fast linear search.
            let krate = prelude.external_crates.iter().find(|krate| {
                krate.num == id.krate
            });

            let krate = match krate {
                Some(k) => k,
                None => {
                    eprintln!("Crate not found: {:?}", id);
                    return None;
                }
            };

            krate.id.clone()
        };

        let id = DefId(krate_id, id.index);
        self.map.get(&id).cloned()
    }
}

#[derive(Clone)]
pub struct Loader {
    deps_dir: PathBuf,
}

impl Loader {
    pub fn new(deps_dir: PathBuf) -> Self {
        Self { deps_dir }
    }
}

impl AnalysisLoader for Loader {
    fn needs_hard_reload(&self, _: &Path) -> bool {
        true
    }

    fn fresh_host(&self) -> AnalysisHost<Self> {
        AnalysisHost::new_with_loader(self.clone())
    }

    fn set_path_prefix(&mut self, _: &Path) {}

    fn abs_path_prefix(&self) -> Option<PathBuf> {
        None
    }
    fn search_directories(&self) -> Vec<PathBuf> {
        vec![self.deps_dir.clone()]
    }
}

// Searchfox uses 1-indexed lines, 0-indexed columns.
fn span_to_string(span: &data::SpanData) -> String {
    // Rust spans are multi-line... So we just set the start column if it spans
    // multiple rows, searchfox has fallback code to handle this.
    if span.line_start != span.line_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    if span.column_start == span.column_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    format!("{}:{}-{}", span.line_start.0, span.column_start.zero_indexed().0, span.column_end.zero_indexed().0)
}

fn visit(
    file: &mut File,
    kind: &'static str,
    location: &data::SpanData,
    qualname: &str,
    context: Option<&str>,
) {
    use serde_json::map::Map;
    use serde_json::value::Value;
    use std::io::Write;

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("target".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    out.insert("pretty".into(), Value::String(qualname.into()));
    out.insert("sym".into(), Value::String(qualname.into()));
    if let Some(context) = context {
        out.insert("context".into(), Value::String(context.into()));
        out.insert("contextsym".into(), Value::String(context.into()));
    }

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    file.write_all(object.as_bytes()).unwrap();
    write!(file, "\n").unwrap();

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("source".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    out.insert("pretty".into(), Value::String(qualname.into()));
    out.insert("sym".into(), Value::String(qualname.into()));

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    file.write_all(object.as_bytes()).unwrap();
    write!(file, "\n").unwrap();
}

fn analyze_file(
    file_name: &PathBuf,
    defs: &Defs,
    file_analysis: &data::Analysis,
    src_dir: &Path,
    output_dir: &Path,
) {
    let file = match file_name.strip_prefix(src_dir) {
        Ok(f) => f,
        Err(..) => {
            eprintln!("File not in the source directory: {}", file_name.display());
            return;
        }
    };

    let output_file = output_dir.join(file);
    let mut output_dir = output_file.clone();
    output_dir.pop();
    if let Err(err) = fs::create_dir_all(output_dir) {
        eprintln!(
            "Couldn't create dir for: {}, {:?}",
            output_file.display(),
            err
        );
        return;
    }
    let mut file = match File::create(&output_file) {
        Ok(f) => f,
        Err(err) => {
            eprintln!(
                "Couldn't open output file: {}, {:?}",
                output_file.display(),
                err
            );
            return;
        }
    };

    for import in &file_analysis.imports {
        let id = match import.ref_id {
            Some(id) => id,
            None => {
                eprintln!("Dropping import {}: {}, no ref", import.name, import.value);
                continue;
            }
        };

        let def = match defs.get(file_analysis, id) {
            Some(def) => def,
            None => {
                eprintln!("Dropping import {}: {}, no def for ref {:?}", import.name, import.value, id);
                continue;
            }
        };

        visit(&mut file, "use", &import.span, &def.qualname, None)
    }

    for def in &file_analysis.defs {
        let parent = def.parent
            .and_then(|parent_id| defs.get(file_analysis, parent_id).map(|d| d.qualname));

        let crate_name = &file_analysis.prelude.as_ref().unwrap().crate_id.name;
        let qualname = format!("{}{}", crate_name, def.qualname);
        visit(&mut file, "def", &def.span, &qualname, parent.as_ref().map(|p| &**p))
    }

    for ref_ in &file_analysis.refs {
        let def = match defs.get(file_analysis, ref_.ref_id) {
            Some(d) => d,
            None => {
                eprintln!("Dropping ref {:?}, kind {:?}, no def", ref_.ref_id, ref_.kind);
                continue;
            }
        };
        visit(
            &mut file,
            "use",
            &ref_.span,
            &def.qualname,
            /* context = */ None, // TODO
        )
    }
}

fn analyze_crate(
    analysis: &data::Analysis,
    defs: &Defs,
    src_dir: &Path,
    output_dir: &Path,
) {
    let mut per_file = HashMap::new();

    println!("Analyzing crate: {:?}", analysis.prelude);

    macro_rules! flat_map_per_file {
        ($field:ident) => {
            for item in &analysis.$field {
                let mut file_analysis =
                    per_file.entry(item.span.file_name.clone())
                        .or_insert_with(|| {
                            let prelude = analysis.prelude.clone();
                            let mut analysis = data::Analysis::new(analysis.config.clone());
                            analysis.prelude = prelude;
                            analysis
                        });
                file_analysis.$field.push(item.clone());
            }
        }
    }

    flat_map_per_file!(imports);
    flat_map_per_file!(defs);
    flat_map_per_file!(impls);
    flat_map_per_file!(refs);
    flat_map_per_file!(macro_refs);
    flat_map_per_file!(relations);

    for (mut name, analysis) in per_file.drain() {
        if name.is_relative() {
            name = src_dir.join(name);
        }
        analyze_file(&name, defs, &analysis, src_dir, output_dir);
    }
}

fn main() {
    let matches = app_from_crate!()
        .args_from_usage(
            "<src>    'Points to the source root'
             <input>  'Points to the deps/save-analysis directory'
             <output> 'Points to the directory where searchfox metadata should go'",
        )
        .get_matches();

    let src_dir = Path::new(matches.value_of("src").unwrap());
    let input_dir = Path::new(matches.value_of("input").unwrap());
    let output_dir = Path::new(matches.value_of("output").unwrap());

    let loader = Loader::new(PathBuf::from(input_dir));

    let crates = rls_analysis::read_analysis_from_files(&loader, Default::default(), &[]);

    let mut defs = Defs::new();
    for krate in &crates {
        for def in &krate.analysis.defs {
            println!("Indexing def: {:?}", def);
            defs.insert(&krate.analysis, def);
        }
    }

    for krate in crates {
        analyze_crate(&krate.analysis, &defs, &src_dir, &output_dir);
    }
}
<|endoftext|>


<commit_before>pub struct LineIterator<'a> {
    data: &'a str,
    start: usize,
    end: usize,
    line_number: usize
}

impl<'a> LineIterator<'a> {
    pub fn new<'b>(data: &'b str) -> LineIterator<'b> {
        LineIterator{
            data: data,
            start: 0,
            end: 0,
            line_number: 0
        }
    }

    pub fn done(&self) -> bool {
        self.end == self.data.len()
    }
}

impl<'a> Iterator for LineIterator<'a> {
    type Item = (usize, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        if self.done() {
            return None
        }

        // Move the range beyond its previous position.
        self.start = self.end;

        // Find the next line range.
        for (offset, c) in self.data[self.start..].char_indices() {
            // Extend the current line range to include this char.
            self.end += c.len_utf8();

            if c == '\n' {
                break;
            }
        }
        self.line_number += 1;

        Some((self.line_number-1, &self.data[self.start..self.end]))
    }
}

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

    #[test]
    fn next_includes_trailing_newlines() {
        let mut lines = LineIterator::new("line\nanother line\n");
        assert_eq!(Some((0, "line\n")), lines.next());
        assert_eq!(Some((1, "another line\n")), lines.next());
    }

    #[test]
    fn next_stops_at_end_of_data() {
        let mut lines = LineIterator::new("line\nanother line\n");
        lines.next();
        lines.next();
        assert_eq!(None, lines.next());
    }
}
<commit_msg>Rename line iterator range fields.<commit_after>pub struct LineIterator<'a> {
    data: &'a str,
    line_number: usize,
    line_start: usize,
    line_end: usize
}

impl<'a> LineIterator<'a> {
    pub fn new<'b>(data: &'b str) -> LineIterator<'b> {
        LineIterator{
            data: data,
            line_number: 0,
            line_start: 0,
            line_end: 0,
        }
    }

    pub fn done(&self) -> bool {
        self.line_end == self.data.len()
    }
}

impl<'a> Iterator for LineIterator<'a> {
    type Item = (usize, &'a str);

    fn next(&mut self) -> Option<Self::Item> {
        if self.done() {
            return None
        }

        // Move the range beyond its previous position.
        self.line_start = self.line_end;

        // Find the next line range.
        for (offset, c) in self.data[self.line_start..].char_indices() {
            // Extend the current line range to include this char.
            self.line_end += c.len_utf8();

            if c == '\n' {
                break;
            }
        }
        self.line_number += 1;

        Some((
            self.line_number-1,
            &self.data[
                self.line_start..self.line_end
            ]
        ))
    }
}

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

    #[test]
    fn next_includes_trailing_newlines() {
        let mut lines = LineIterator::new("line\nanother line\n");
        assert_eq!(Some((0, "line\n")), lines.next());
        assert_eq!(Some((1, "another line\n")), lines.next());
    }

    #[test]
    fn next_stops_at_end_of_data() {
        let mut lines = LineIterator::new("line\nanother line\n");
        lines.next();
        lines.next();
        assert_eq!(None, lines.next());
    }
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod lucetc_tests {
    use failure::Error;
    use lucet_module::bindings::Bindings;
    use lucet_validate::Validator;
    use lucet_wasi_sdk::*;
    use lucetc::{Compiler, HeapSettings, OptLevel};
    use std::collections::HashMap;
    use std::fs::File;
    use std::io::Read;
    use std::path::PathBuf;

    /// Compile C -> WebAssembly using wasi-sdk's clang. Does not use the wasi-sdk
    /// libc, and does not produce a wasi executable, just a wasm module with the given set of
    /// export functions.
    fn module_from_c(cfiles: &[&str], exports: &[&str]) -> Result<Vec<u8>, Error> {
        let cfiles: Vec<PathBuf> = cfiles
            .iter()
            .map(|ref name| PathBuf::from(format!("tests/{}.c", name)))
            .collect();
        let tempdir = tempfile::Builder::new().prefix("wasi-sdk-test").tempdir()?;

        let mut wasm = PathBuf::from(tempdir.path());
        wasm.push("out.wasm");

        let mut linker = Link::new(&cfiles)
            .with_cflag("-nostartfiles")
            .with_link_opt(LinkOpt::NoDefaultEntryPoint)
            .with_link_opt(LinkOpt::AllowUndefinedAll);
        for export in exports {
            linker.export(export);
        }
        linker.link(wasm.clone())?;

        let mut wasm_file = File::open(wasm)?;
        let mut wasm_contents = Vec::new();
        wasm_file.read_to_end(&mut wasm_contents)?;
        Ok(wasm_contents)
    }

    #[test]
    fn empty() {
        let m = module_from_c(&["empty"], &[]).expect("build module for empty");
        let b = Bindings::empty();
        let h = HeapSettings::default();
        let v = Validator::parse("").expect("empty validation environment");
        let c =
            Compiler::new(&m, OptLevel::default(), &b, h, false, &Some(v)).expect("compile empty");
        let mdata = c.module_data().unwrap();
        assert!(mdata.heap_spec().is_some());
        // clang creates 3 globals, which are not exported.:
        assert_eq!(mdata.globals_spec().len(), 3);
        assert!(mdata.globals_spec()[0].is_internal());

        assert_eq!(mdata.import_functions().len(), 0, "import functions");
        assert_eq!(mdata.export_functions().len(), 0, "export functions");

        /* FIXME: module data doesn't contain the information to check these properties:
        assert_eq!(num_import_globals(&p), 0, "import globals");
        */

        let _obj = c.object_file().expect("generate code from empty");
    }

    fn d_only_test_bindings() -> Bindings {
        let imports: HashMap<String, String> = [
            ("c".into(), "c".into()), // d_only
        ]
        .iter()
        .cloned()
        .collect();

        Bindings::env(imports)
    }

    #[test]
    fn just_c() {
        let m = module_from_c(&["c"], &["c"]).expect("build module for c");
        let b = Bindings::empty();
        let h = HeapSettings::default();
        let v = Validator::parse("").expect("empty validation environment");

        let c = Compiler::new(&m, OptLevel::default(), &b, h, false, &Some(v)).expect("compile c");
        let mdata = c.module_data().unwrap();
        assert_eq!(mdata.import_functions().len(), 0, "import functions");
        assert_eq!(mdata.export_functions().len(), 1, "export functions");

        /* FIXME: module data doesn't contain the information to check these properties:
        assert_eq!(num_import_globals(&p), 0, "import globals");
        */

        let _obj = c.object_file().expect("generate code from c");
    }

    #[test]
    fn just_d() {
        let m = module_from_c(&["d"], &["d"]).expect("build module for d");
        let b = d_only_test_bindings();
        let h = HeapSettings::default();
        let v = Validator::parse(
            "(module $env (@interface func (export \"c\") (param $a1 s32) (result $r1 s32)))",
        )
        .expect("empty validation environment");
        let c = Compiler::new(&m, OptLevel::default(), &b, h, false, &Some(v)).expect("compile d");
        let mdata = c.module_data().unwrap();
        assert_eq!(mdata.import_functions().len(), 1, "import functions");
        assert_eq!(mdata.export_functions().len(), 1, "export functions");

        /* FIXME: module data doesn't contain the information to check these properties:
        assert_eq!(num_import_globals(&p), 0, "import globals");
        */
        let _obj = c.object_file().expect("generate code from d");
    }

    #[test]
    fn c_and_d() {
        let m = module_from_c(&["c", "d"], &["c", "d"]).expect("build module for c & d");
        let b = Bindings::empty();
        let h = HeapSettings::default();
        let v = Validator::parse("").expect("empty validation environment");
        let c =
            Compiler::new(&m, OptLevel::default(), &b, h, false, &Some(v)).expect("compile c & d");
        let mdata = c.module_data().unwrap();
        assert_eq!(mdata.import_functions().len(), 0, "import functions");
        assert_eq!(mdata.export_functions().len(), 2, "export functions");

        /* FIXME: module data doesn't contain the information to check these properties:
        assert_eq!(num_import_globals(&p), 0, "import globals");
        */
        let _obj = c.object_file().expect("generate code from c & d");
    }

    #[test]
    fn hello() {
        let m = {
            // Unlike in module_from_c, use wasi-sdk to compile a C file to a wasi executable,
            // linking in wasi-libc and exposing the wasi _start entry point only:
            let tempdir = tempfile::Builder::new()
                .prefix("wasi-sdk-test")
                .tempdir()
                .expect("create tempdir");
            let mut wasm = PathBuf::from(tempdir.path());
            wasm.push("out.wasm");

            let linker = Link::new(&[PathBuf::from("tests/hello.c")]);
            linker.link(wasm.clone()).expect("link");

            let mut wasm_file = File::open(wasm).expect("open wasm");
            let mut wasm_contents = Vec::new();
            wasm_file
                .read_to_end(&mut wasm_contents)
                .expect("read wasm");
            wasm_contents
        };

        let b =
            Bindings::from_file("../lucet-wasi/bindings.json").expect("load lucet-wasi bindings");
        let h = HeapSettings::default();
        let v = Validator::load("../wasi/phases/unstable/witx/wasi_unstable_preview0.witx")
            .expect("wasi spec validation")
            .with_wasi_exe(true);
        // Compiler will only unwrap if the Validator defined above accepts the module
        let c =
            Compiler::new(&m, OptLevel::default(), &b, h, false, &Some(v)).expect("compile empty");
        let mdata = c.module_data().unwrap();
        assert!(mdata.heap_spec().is_some());
    }
}
<commit_msg>fix lucet-wasi-sdk test for wask-sdk-7<commit_after>#[cfg(test)]
mod lucetc_tests {
    use failure::Error;
    use lucet_module::bindings::Bindings;
    use lucet_validate::Validator;
    use lucet_wasi_sdk::*;
    use lucetc::{Compiler, HeapSettings, OptLevel};
    use std::collections::HashMap;
    use std::fs::File;
    use std::io::Read;
    use std::path::PathBuf;

    /// Compile C -> WebAssembly using wasi-sdk's clang. Does not use the wasi-sdk
    /// libc, and does not produce a wasi executable, just a wasm module with the given set of
    /// export functions.
    fn module_from_c(cfiles: &[&str], exports: &[&str]) -> Result<Vec<u8>, Error> {
        let cfiles: Vec<PathBuf> = cfiles
            .iter()
            .map(|ref name| PathBuf::from(format!("tests/{}.c", name)))
            .collect();
        let tempdir = tempfile::Builder::new().prefix("wasi-sdk-test").tempdir()?;

        let mut wasm = PathBuf::from(tempdir.path());
        wasm.push("out.wasm");

        let mut linker = Link::new(&cfiles)
            .with_cflag("-nostartfiles")
            .with_link_opt(LinkOpt::NoDefaultEntryPoint)
            .with_link_opt(LinkOpt::AllowUndefinedAll);
        for export in exports {
            linker.export(export);
        }
        linker.link(wasm.clone())?;

        let mut wasm_file = File::open(wasm)?;
        let mut wasm_contents = Vec::new();
        wasm_file.read_to_end(&mut wasm_contents)?;
        Ok(wasm_contents)
    }

    #[test]
    fn empty() {
        let m = module_from_c(&["empty"], &[]).expect("build module for empty");
        let b = Bindings::empty();
        let h = HeapSettings::default();
        let v = Validator::parse("").expect("empty validation environment");
        let c =
            Compiler::new(&m, OptLevel::default(), &b, h, false, &Some(v)).expect("compile empty");
        let mdata = c.module_data().unwrap();
        assert!(mdata.heap_spec().is_some());
        // clang creates just 1 global:
        assert_eq!(mdata.globals_spec().len(), 1);
        assert!(mdata.globals_spec()[0].is_internal());

        assert_eq!(mdata.import_functions().len(), 0, "import functions");
        assert_eq!(mdata.export_functions().len(), 0, "export functions");

        /* FIXME: module data doesn't contain the information to check these properties:
        assert_eq!(num_import_globals(&p), 0, "import globals");
        */

        let _obj = c.object_file().expect("generate code from empty");
    }

    fn d_only_test_bindings() -> Bindings {
        let imports: HashMap<String, String> = [
            ("c".into(), "c".into()), // d_only
        ]
        .iter()
        .cloned()
        .collect();

        Bindings::env(imports)
    }

    #[test]
    fn just_c() {
        let m = module_from_c(&["c"], &["c"]).expect("build module for c");
        let b = Bindings::empty();
        let h = HeapSettings::default();
        let v = Validator::parse("").expect("empty validation environment");

        let c = Compiler::new(&m, OptLevel::default(), &b, h, false, &Some(v)).expect("compile c");
        let mdata = c.module_data().unwrap();
        assert_eq!(mdata.import_functions().len(), 0, "import functions");
        assert_eq!(mdata.export_functions().len(), 1, "export functions");

        /* FIXME: module data doesn't contain the information to check these properties:
        assert_eq!(num_import_globals(&p), 0, "import globals");
        */

        let _obj = c.object_file().expect("generate code from c");
    }

    #[test]
    fn just_d() {
        let m = module_from_c(&["d"], &["d"]).expect("build module for d");
        let b = d_only_test_bindings();
        let h = HeapSettings::default();
        let v = Validator::parse(
            "(module $env (@interface func (export \"c\") (param $a1 s32) (result $r1 s32)))",
        )
        .expect("empty validation environment");
        let c = Compiler::new(&m, OptLevel::default(), &b, h, false, &Some(v)).expect("compile d");
        let mdata = c.module_data().unwrap();
        assert_eq!(mdata.import_functions().len(), 1, "import functions");
        assert_eq!(mdata.export_functions().len(), 1, "export functions");

        /* FIXME: module data doesn't contain the information to check these properties:
        assert_eq!(num_import_globals(&p), 0, "import globals");
        */
        let _obj = c.object_file().expect("generate code from d");
    }

    #[test]
    fn c_and_d() {
        let m = module_from_c(&["c", "d"], &["c", "d"]).expect("build module for c & d");
        let b = Bindings::empty();
        let h = HeapSettings::default();
        let v = Validator::parse("").expect("empty validation environment");
        let c =
            Compiler::new(&m, OptLevel::default(), &b, h, false, &Some(v)).expect("compile c & d");
        let mdata = c.module_data().unwrap();
        assert_eq!(mdata.import_functions().len(), 0, "import functions");
        assert_eq!(mdata.export_functions().len(), 2, "export functions");

        /* FIXME: module data doesn't contain the information to check these properties:
        assert_eq!(num_import_globals(&p), 0, "import globals");
        */
        let _obj = c.object_file().expect("generate code from c & d");
    }

    #[test]
    fn hello() {
        let m = {
            // Unlike in module_from_c, use wasi-sdk to compile a C file to a wasi executable,
            // linking in wasi-libc and exposing the wasi _start entry point only:
            let tempdir = tempfile::Builder::new()
                .prefix("wasi-sdk-test")
                .tempdir()
                .expect("create tempdir");
            let mut wasm = PathBuf::from(tempdir.path());
            wasm.push("out.wasm");

            let linker = Link::new(&[PathBuf::from("tests/hello.c")]);
            linker.link(wasm.clone()).expect("link");

            let mut wasm_file = File::open(wasm).expect("open wasm");
            let mut wasm_contents = Vec::new();
            wasm_file
                .read_to_end(&mut wasm_contents)
                .expect("read wasm");
            wasm_contents
        };

        let b =
            Bindings::from_file("../lucet-wasi/bindings.json").expect("load lucet-wasi bindings");
        let h = HeapSettings::default();
        let v = Validator::load("../wasi/phases/unstable/witx/wasi_unstable_preview0.witx")
            .expect("wasi spec validation")
            .with_wasi_exe(true);
        // Compiler will only unwrap if the Validator defined above accepts the module
        let c =
            Compiler::new(&m, OptLevel::default(), &b, h, false, &Some(v)).expect("compile empty");
        let mdata = c.module_data().unwrap();
        assert!(mdata.heap_spec().is_some());
    }
}
<|endoftext|>


<commit_before>//! Implements `StdTimeManager`.

use std::sync::RwLock;
use std::time::SystemTime;
use board::*;
use depth::*;
use search_executor::*;
use hash_table::Variation;
use search_node::SearchNode;
use time_manager::{TimeManager, RemainingTime};
use uci::{SetOption, OptionDescription};

/// Implements the `TimeManager` trait.
pub struct StdTimeManager {
    started_at: SystemTime,
    depth: Depth,
    extrapolation_points: Vec<(f64, f64)>,
    hard_limit: f64,
    allotted_time: f64,
    must_play: bool,
}

impl<S> TimeManager<S> for StdTimeManager
    where S: SearchExecutor<ReportData = Vec<Variation>>
{
    fn new(position: &S::SearchNode, time: RemainingTime) -> StdTimeManager {
        // Get our remaining time and increment (in milliseconds).
        let (t, inc) = if position.board().to_move == WHITE {
            (time.white_millis as f64, time.winc_millis as f64)
        } else {
            (time.black_millis as f64, time.binc_millis as f64)
        };

        // Get the number of moves till the next time control, or
        // guess the number of moves till the end of the game.
        let n = match time.movestogo.unwrap_or(0) {
            0 => 40,
            n => n,
        } as f64;

        // Calculate an approximation for the total time we have till
        // the next time control or the end of the game.
        let time_heap = t + inc * (n - 1.0);

        // Set a hard limit for the time we will spend on this
        // move. Thinking longer that that would be reckless.
        let hard_limit = (t / n.sqrt() + inc).min(t - 1000.0);

        StdTimeManager {
            started_at: SystemTime::now(),
            depth: 0,
            extrapolation_points: Vec::with_capacity(32),
            hard_limit: if position.legal_moves().len() > 1 {
                hard_limit
            } else {
                // When there is only one legal move, the engine is
                // allowed to think a fraction of a second in order to
                // find a good ponder move.
                hard_limit.min(500.0)
            },
            allotted_time: if *PONDERING_IS_ALLOWED.read().unwrap() {
                // Statistically, the move we ponder will be played in
                // 50% of the cases. Therefore, in principal we should
                // add half of opponent's thinking time to our time
                // heap. In reality we do not know how opponent's time
                // will be spend, so we speculatively increase our
                // time heap by 50%.
                1.5 * time_heap / n
            } else {
                time_heap / n
            },
            must_play: false,
        }
    }

    fn update(&mut self, report: &SearchReport<Vec<Variation>>) {
        let &SearchReport { ref depth, ref searched_nodes, .. } = report;
        if *depth > self.depth {
            self.depth = *depth;
            if *searched_nodes < 100 {
                // We ignore the first few completed depths, because
                // they are calculated almost instantaneously.
                return;
            }
            let t = elapsed_millis(&self.started_at);
            let depth = *depth as f64;
            let searched_nodes = *searched_nodes as f64;

            // We maintain a list of data points so as to be able to
            // intelligently guess how much time it will take for the
            // next search depth to complete. (We apply an exponential
            // regression over the last `M` points in the list.)
            const M: usize = 5;
            self.extrapolation_points.push((depth, searched_nodes.ln()));
            let expected_duration = match self.extrapolation_points.len() {
                n if n >= M => {
                    let last_m = &self.extrapolation_points[n - M..];
                    let factor = extrapolate(last_m, depth + 1.0).exp() / searched_nodes;

                    // Update `BRANCHING_FACTOR`. This is an average
                    // branching factor calculated over the last few
                    // moves.
                    if n == M {
                        let mut bf = BRANCHING_FACTOR.write().unwrap();
                        *bf = (*bf * 2.0 + factor) / 3.0;
                    }

                    factor * t
                }
                _ => *BRANCHING_FACTOR.read().unwrap() * t,
            };

            // We try to stay as close as possible to the allotted
            // time, without crossing the hard limit.
            self.must_play = expected_duration > self.hard_limit ||
                             expected_duration > self.allotted_time &&
                             expected_duration - self.allotted_time > self.allotted_time - t;
        }
    }

    #[allow(unused_variables)]
    fn must_play(&self, search: &S) -> bool {
        self.must_play || elapsed_millis(&self.started_at) > self.hard_limit
    }
}

impl SetOption for StdTimeManager {
    fn options() -> Vec<(String, OptionDescription)> {
        vec![("Ponder".to_string(), OptionDescription::Check { default: false })]
    }

    fn set_option(name: &str, value: &str) {
        if name == "Ponder" {
            match value {
                "true" => *PONDERING_IS_ALLOWED.write().unwrap() = true,
                "false" => *PONDERING_IS_ALLOWED.write().unwrap() = false,
                _ => (),
            }
        }
    }
}

lazy_static! {
    static ref PONDERING_IS_ALLOWED: RwLock<bool> = RwLock::new(false);
    static ref BRANCHING_FACTOR: RwLock<f64> = RwLock::new(2.0);
}

/// A helper function. It calculates the elapsed milliseconds since a
/// given time.
fn elapsed_millis(since: &SystemTime) -> f64 {
    let d = since.elapsed().unwrap();
    (1000 * d.as_secs()) as f64 + (d.subsec_nanos() / 1_000_000) as f64
}

/// A helper function. It linearly extrapolates the value y(x) using
/// the (x, y) values in `points` as a reference.
fn extrapolate(points: &[(f64, f64)], x: f64) -> f64 {
    debug_assert!(points.len() > 1);
    let sum_x = points.iter().fold(0.0, |acc, &p| acc + p.0);
    let sum_y = points.iter().fold(0.0, |acc, &p| acc + p.1);
    let sum_xx = points.iter().fold(0.0, |acc, &p| acc + p.0 * p.0);
    let sum_xy = points.iter().fold(0.0, |acc, &p| acc + p.0 * p.1);
    let n = points.len() as f64;
    let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x);
    let intercept = (sum_y - slope * sum_x) / n;
    slope * x + intercept
}

#[cfg(test)]
mod tests {
    #[test]
    fn linear_regression() {
        use super::extrapolate;
        let points = vec![(21.0, 1.0), (22.0, 2.0), (23.0, 3.0), (24.0, 4.0)];
        let x = 25.0;
        let y = extrapolate(&points, x);
        assert!(4.99 < y && y < 5.01);
    }
}
<commit_msg>revise the alloted time if the value changed too much<commit_after>//! Implements `StdTimeManager`.

use std::sync::RwLock;
use std::time::SystemTime;
use board::*;
use depth::*;
use value::*;
use search_executor::*;
use hash_table::Variation;
use search_node::SearchNode;
use time_manager::{TimeManager, RemainingTime};
use uci::{SetOption, OptionDescription};

/// Implements the `TimeManager` trait.
pub struct StdTimeManager {
    started_at: SystemTime,
    depth: Depth,
    value: Value,
    extrapolation_points: Vec<(f64, f64)>,
    hard_limit: f64,
    allotted_time: f64,
    must_play: bool,
}

impl<S> TimeManager<S> for StdTimeManager
    where S: SearchExecutor<ReportData = Vec<Variation>>
{
    fn new(position: &S::SearchNode, time: RemainingTime) -> StdTimeManager {
        // Get our remaining time and increment (in milliseconds).
        let (t, inc) = if position.board().to_move == WHITE {
            (time.white_millis as f64, time.winc_millis as f64)
        } else {
            (time.black_millis as f64, time.binc_millis as f64)
        };

        // Get the number of moves until the next time control, or if
        // not available, guess the number of moves to the end of the
        // game.
        let n = match time.movestogo.unwrap_or(0) {
            0 => 40,
            n => n,
        } as f64;

        // Calculate the total time we have.
        let time_heap = t + inc * (n - 1.0);

        // Set a hard limit for the time we will spend on this
        // move. Thinking longer that that would be reckless.
        let hard_limit = (t / n.sqrt() + inc).min(t - 1000.0);

        StdTimeManager {
            started_at: SystemTime::now(),
            depth: 0,
            value: VALUE_UNKNOWN,
            extrapolation_points: Vec::with_capacity(32),
            hard_limit: if position.legal_moves().len() > 1 {
                hard_limit
            } else {
                // When there is only one legal move, the engine is
                // allowed to think just a fraction of a second in
                // order to find a good ponder move.
                hard_limit.min(500.0)
            },
            allotted_time: if *PONDERING_IS_ALLOWED.read().unwrap() {
                // Statistically, the move we ponder will be played in
                // 50% of the cases. Therefore, in principal we should
                // add half of opponent's thinking time to our time
                // heap. In reality we do not know how opponent's time
                // will be spend, so we speculatively increase our
                // time heap by 50%.
                1.5 * time_heap / n
            } else {
                time_heap / n
            },
            must_play: false,
        }
    }

    fn update(&mut self, report: &SearchReport<Vec<Variation>>) {
        let &SearchReport { ref depth, ref value, ref searched_nodes, .. } = report;
        if *depth > self.depth {
            self.depth = *depth;
            if *searched_nodes < 100 {
                return; // We ignore the first few depths.
            }
            let t = elapsed_millis(&self.started_at);
            let depth = *depth as f64;
            let searched_nodes = *searched_nodes as f64;

            // We maintain a list of data points so as to be able to
            // intelligently guess how much time it will take for the
            // next search depth to complete. (We apply an exponential
            // regression over the last `M` points in the list.)
            const M: usize = 5;
            self.extrapolation_points.push((depth, searched_nodes.ln()));
            let expected_duration = match self.extrapolation_points.len() {
                n if n >= M => {
                    let last_m = &self.extrapolation_points[n - M..];
                    let factor = extrapolate(last_m, depth + 1.0).exp() / searched_nodes;

                    // Update `BRANCHING_FACTOR`. This is an average
                    // branching factor calculated over the last few
                    // moves.
                    if n == M {
                        let mut bf = BRANCHING_FACTOR.write().unwrap();
                        *bf = (*bf * 2.0 + factor) / 3.0;
                    }

                    factor * t
                }
                _ => *BRANCHING_FACTOR.read().unwrap() * t,
            };

            // We may need to revise the allotted time if position's
            // evaluation has changed a lot with the newly completed
            // depth.
            if (expected_duration > self.allotted_time) &&
               (self.value != VALUE_UNKNOWN && *value != VALUE_UNKNOWN) &&
               (self.value as isize - *value as isize).abs() >= 25 {
                self.allotted_time = expected_duration;
            }
            self.value = *value;

            // We try to stay as close as possible to the allotted
            // time, without crossing the hard limit.
            self.must_play = expected_duration > self.hard_limit ||
                             expected_duration > self.allotted_time &&
                             expected_duration - self.allotted_time > self.allotted_time - t;
        }
    }

    #[allow(unused_variables)]
    fn must_play(&self, search: &S) -> bool {
        self.must_play || elapsed_millis(&self.started_at) > self.hard_limit
    }
}

impl SetOption for StdTimeManager {
    fn options() -> Vec<(String, OptionDescription)> {
        vec![("Ponder".to_string(), OptionDescription::Check { default: false })]
    }

    fn set_option(name: &str, value: &str) {
        if name == "Ponder" {
            match value {
                "true" => *PONDERING_IS_ALLOWED.write().unwrap() = true,
                "false" => *PONDERING_IS_ALLOWED.write().unwrap() = false,
                _ => (),
            }
        }
    }
}

lazy_static! {
    static ref PONDERING_IS_ALLOWED: RwLock<bool> = RwLock::new(false);
    static ref BRANCHING_FACTOR: RwLock<f64> = RwLock::new(2.0);
}

/// A helper function. It calculates the elapsed milliseconds since a
/// given time.
fn elapsed_millis(since: &SystemTime) -> f64 {
    let d = since.elapsed().unwrap();
    (1000 * d.as_secs()) as f64 + (d.subsec_nanos() / 1_000_000) as f64
}

/// A helper function. It linearly extrapolates the value y(x) using
/// the (x, y) values in `points` as a reference.
fn extrapolate(points: &[(f64, f64)], x: f64) -> f64 {
    debug_assert!(points.len() > 1);
    let sum_x = points.iter().fold(0.0, |acc, &p| acc + p.0);
    let sum_y = points.iter().fold(0.0, |acc, &p| acc + p.1);
    let sum_xx = points.iter().fold(0.0, |acc, &p| acc + p.0 * p.0);
    let sum_xy = points.iter().fold(0.0, |acc, &p| acc + p.0 * p.1);
    let n = points.len() as f64;
    let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x);
    let intercept = (sum_y - slope * sum_x) / n;
    slope * x + intercept
}

#[cfg(test)]
mod tests {
    #[test]
    fn linear_regression() {
        use super::extrapolate;
        let points = vec![(21.0, 1.0), (22.0, 2.0), (23.0, 3.0), (24.0, 4.0)];
        let x = 25.0;
        let y = extrapolate(&points, x);
        assert!(4.99 < y && y < 5.01);
    }
}
<|endoftext|>


<commit_before>use crate::health::{Event, HealthCheck, HealthCheckContext, ValidatorEvent};
use std::collections::{hash_map::Entry, HashMap, HashSet};

/// Verifies that commit history produced by validators is 'lineariazble'
/// This means that validators can be behind each other, but commits that they are producing
/// do not contradict each other
pub struct CommitHistoryHealthCheck {
    round_to_commit: HashMap<u64, CommitAndValidators>,
    latest_committed_round: HashMap<String, u64>,
}

struct CommitAndValidators {
    pub hash: String,
    pub validators: HashSet<String>,
}

impl CommitHistoryHealthCheck {
    pub fn new() -> Self {
        Self {
            round_to_commit: HashMap::new(),
            latest_committed_round: HashMap::new(),
        }
    }
}

impl HealthCheck for CommitHistoryHealthCheck {
    fn on_event(&mut self, ve: &ValidatorEvent, ctx: &mut HealthCheckContext) {
        let commit = if let Event::Commit(ref commit) = ve.event {
            commit
        } else {
            return;
        };
        let round_to_commit = self.round_to_commit.entry(commit.round);
        match round_to_commit {
            Entry::Occupied(mut oe) => {
                let commit_and_validators = oe.get_mut();
                if commit_and_validators.hash != commit.commit {
                    ctx.report_failure(
                        ve.validator.clone(),
                        format!(
                            "produced contradicting commit {} at round {}, expected: {}",
                            commit.commit, commit.round, commit_and_validators.hash
                        ),
                    );
                } else {
                    commit_and_validators
                        .validators
                        .insert(ve.validator.clone());
                }
            }
            Entry::Vacant(va) => {
                let mut validators = HashSet::new();
                validators.insert(ve.validator.clone());
                va.insert(CommitAndValidators {
                    hash: commit.commit.clone(),
                    validators,
                });
            }
        }
        let latest_committed_round = self.latest_committed_round.entry(ve.validator.clone());
        match latest_committed_round {
            Entry::Occupied(mut oe) => {
                let previous_round = *oe.get();
                if previous_round > commit.round {
                    ctx.report_failure(
                        ve.validator.clone(),
                        format!(
                            "committed round {} after committing {}",
                            commit.round, previous_round
                        ),
                    );
                }
                oe.insert(commit.round);
            }
            Entry::Vacant(va) => {
                va.insert(commit.round);
            }
        }
    }

    fn name(&self) -> &'static str {
        "commit_check"
    }
}
<commit_msg>[cluster_test] Fix commit_check OOMing cluster_test<commit_after>use crate::health::{Event, HealthCheck, HealthCheckContext, ValidatorEvent};
use std::collections::{hash_map::Entry, HashMap, HashSet};

/// Verifies that commit history produced by validators is 'lineariazble'
/// This means that validators can be behind each other, but commits that they are producing
/// do not contradict each other
pub struct CommitHistoryHealthCheck {
    round_to_commit: HashMap<u64, CommitAndValidators>,
    latest_committed_round: HashMap<String, u64>,
}

struct CommitAndValidators {
    pub hash: String,
    pub validators: HashSet<String>,
}

impl CommitHistoryHealthCheck {
    pub fn new() -> Self {
        Self {
            round_to_commit: HashMap::new(),
            latest_committed_round: HashMap::new(),
        }
    }
}

impl HealthCheck for CommitHistoryHealthCheck {
    fn on_event(&mut self, ve: &ValidatorEvent, ctx: &mut HealthCheckContext) {
        let commit = if let Event::Commit(ref commit) = ve.event {
            commit
        } else {
            return;
        };
        let round_to_commit = self.round_to_commit.entry(commit.round);
        match round_to_commit {
            Entry::Occupied(mut oe) => {
                let commit_and_validators = oe.get_mut();
                if commit_and_validators.hash != commit.commit {
                    ctx.report_failure(
                        ve.validator.clone(),
                        format!(
                            "produced contradicting commit {} at round {}, expected: {}",
                            commit.commit, commit.round, commit_and_validators.hash
                        ),
                    );
                } else {
                    commit_and_validators
                        .validators
                        .insert(ve.validator.clone());
                }
            }
            Entry::Vacant(va) => {
                let mut validators = HashSet::new();
                validators.insert(ve.validator.clone());
                va.insert(CommitAndValidators {
                    hash: commit.commit.clone(),
                    validators,
                });
            }
        }
        let latest_committed_round = self.latest_committed_round.entry(ve.validator.clone());
        match latest_committed_round {
            Entry::Occupied(mut oe) => {
                let previous_round = *oe.get();
                if previous_round > commit.round {
                    ctx.report_failure(
                        ve.validator.clone(),
                        format!(
                            "committed round {} after committing {}",
                            commit.round, previous_round
                        ),
                    );
                }
                oe.insert(commit.round);
            }
            Entry::Vacant(va) => {
                va.insert(commit.round);
            }
        }
        if let Some(min_round) = self.latest_committed_round.values().min() {
            self.round_to_commit.retain(|k, _v| *k >= *min_round);
        }
    }

    fn name(&self) -> &'static str {
        "commit_check"
    }
}
<|endoftext|>


<commit_before>use na::Real;
use pipeline::broad_phase::BroadPhasePairFilter;
use pipeline::world::CollisionObject;

const SELF_COLLISION: u32 = 1 << 31;
const ALL_GROUPS: u32 = (1 << 30) - 1;
const NO_GROUP: u32 = 0;

/// Groups of collision used to filter which object interact with which other one.
///
/// There are at most 30 groups indexed from 0 to 29 (included). This identifies collidable
/// entities by combining three attributes:
///    * A set of group this structure is member of.
///    * A collision group whitelist.
///    * A collision group blacklist.
///
/// For two entities to interact, they must be member of at least one group part of each-other's
/// whitelists, and must not be part of any blacklisted group. The blacklist always has priority on
/// the whitelist.
///
/// ### Example
/// For example if the object A is such that:
///    * It is part of the groups 1, 3, and 6.
///    * It whitelists the groups 6 and 7.
///    * It blacklists the group 1.
/// Let the object B be such that:
///    * It is part of the groups 1, 3, and 7.
///    * It whitelists the groups 3 and 7.
///    * It does not blacklist anything.
/// For example if the object C is such that:
///    * It is part of the groups 6 and 9.
///    * It whitelists the groups 3 and 7.
///    * It does not blacklist anything.
///
/// Then we have:
///    * A and C can interact because A whitelists the group 6 (which C is part of), and,
///    reciprocally, C whitelists the group 3 (which A is part of).
///    * A and B will **not** interact because B is part of the group 1 which is blacklisted by A.
///    * Finally, B and C will **not** interact either because, even if C whitelists the group 3
///    (which B is part of), B does not whitelists the groups 6 nor 9 (which B is part of).
#[derive(Clone, Debug, Copy)]
pub struct CollisionGroups {
    membership: u32,
    whitelist: u32,
    blacklist: u32,
}

impl CollisionGroups {
    /// Creates a new `CollisionGroups` that enables interactions with everything except
    /// self-interaction.
    #[inline]
    pub fn new() -> CollisionGroups {
        CollisionGroups {
            membership: ALL_GROUPS,
            whitelist: ALL_GROUPS,
            blacklist: NO_GROUP,
        }
    }

    /// The maximum allowed group identifier.
    #[inline]
    pub fn max_group_id() -> usize {
        29
    }

    #[inline]
    fn modify_mask(mask: &mut u32, group_id: usize, add: bool) {
        assert!(
            group_id < 30,
            "There are at most 30 groups indexed from 0 to 29 (included)."
        );

        if add {
            *mask = *mask | (1 << group_id)
        } else {
            *mask = *mask & !(1 << group_id)
        }
    }

    #[inline]
    fn set_mask(mask: &mut u32, groups: &[usize]) {
        *mask = 0;
        for g in groups.iter() {
            CollisionGroups::modify_mask(mask, *g, true);
        }
    }

    /// Adds or removes this entity from the given group.
    #[inline]
    pub fn modify_membership(&mut self, group_id: usize, add: bool) {
        CollisionGroups::modify_mask(&mut self.membership, group_id, add);
    }

    /// Adds or removes the given group from this entity whitelist.
    #[inline]
    pub fn modify_whitelist(&mut self, group_id: usize, add: bool) {
        CollisionGroups::modify_mask(&mut self.whitelist, group_id, add);
    }

    /// Adds or removes this entity from the given group.
    #[inline]
    pub fn modify_blacklist(&mut self, group_id: usize, add: bool) {
        CollisionGroups::modify_mask(&mut self.blacklist, group_id, add);
    }

    /// Make this object member of the given groups only.
    #[inline]
    pub fn set_membership(&mut self, groups: &[usize]) {
        CollisionGroups::set_mask(&mut self.membership, groups);
    }

    /// Whitelists the given groups only (others will be un-whitelisted).
    #[inline]
    pub fn set_whitelist(&mut self, groups: &[usize]) {
        CollisionGroups::set_mask(&mut self.whitelist, groups);
    }

    /// Blacklists the given groups only (others will be un-blacklisted).
    #[inline]
    pub fn set_blacklist(&mut self, groups: &[usize]) {
        CollisionGroups::set_mask(&mut self.blacklist, groups);
    }

    /// Copies the membership of another collision groups.
    #[inline]
    pub fn copy_membership(&mut self, other: &CollisionGroups) {
        self.membership = other.membership
    }

    /// Copies the whitelist of another collision groups.
    #[inline]
    pub fn copy_whitelist(&mut self, other: &CollisionGroups) {
        self.whitelist = other.whitelist
    }

    /// Copies the blacklist of another collision groups.
    #[inline]
    pub fn copy_blacklist(&mut self, other: &CollisionGroups) {
        self.blacklist = other.blacklist
    }

    /// Allows the object to interact with itself.
    #[inline]
    pub fn enable_self_interaction(&mut self) {
        self.whitelist = self.whitelist | SELF_COLLISION;
    }

    /// Prevents the object from interacting with itself.
    #[inline]
    pub fn disable_self_interaction(&mut self) {
        self.whitelist = self.whitelist & !SELF_COLLISION;
    }

    #[inline]
    fn is_inside_mask(mask: u32, group_id: usize) -> bool {
        assert!(
            group_id < 30,
            "There are at most 30 groups indexed from 0 to 29 (included)."
        );
        mask & (1 << group_id) != 0
    }

    /// Tests if this entity is part of the given group.
    #[inline]
    pub fn is_member_of(&self, group_id: usize) -> bool {
        CollisionGroups::is_inside_mask(self.membership, group_id)
    }

    /// Tests if the given group is whitelisted.
    #[inline]
    pub fn is_group_whitelisted(&self, group_id: usize) -> bool {
        CollisionGroups::is_inside_mask(self.whitelist, group_id)
    }

    /// Tests if the given group is blacklisted.
    #[inline]
    pub fn is_group_blacklisted(&self, group_id: usize) -> bool {
        CollisionGroups::is_inside_mask(self.blacklist, group_id)
    }

    /// Tests whether interactions with a given group is possible.
    ///
    /// Collision is possible if `group_id` is whitelisted but not blacklisted.
    #[inline]
    pub fn can_interact_with(&self, group_id: usize) -> bool {
        !CollisionGroups::is_inside_mask(self.blacklist, group_id)
            && CollisionGroups::is_inside_mask(self.whitelist, group_id)
    }

    /// Tests whether two collision groups have at least one group in common.
    #[inline]
    pub fn can_interact_with_groups(&self, other: &CollisionGroups) -> bool {
        // FIXME: is there a more bitwise-y way of doing this?
        self.membership & other.blacklist == 0
            && other.membership & self.blacklist == 0
            && self.membership & other.whitelist != 0
            && other.membership & self.whitelist != 0
    }

    /// Tests whether self-interaction is enabled.
    #[inline]
    pub fn can_interact_with_self(&self) -> bool {
        self.whitelist & SELF_COLLISION != 0
    }
}

/// A collision filter based collision groups.
pub struct CollisionGroupsPairFilter;

impl CollisionGroupsPairFilter {
    /// Creates a new collision filter based collision groups.
    #[inline]
    pub fn new() -> CollisionGroupsPairFilter {
        CollisionGroupsPairFilter
    }
}

impl<N: Real, T> BroadPhasePairFilter<N, T> for CollisionGroupsPairFilter {
    fn is_pair_valid(&self, co1: &CollisionObject<N, T>, co2: &CollisionObject<N, T>) -> bool {
        if co1.handle() == co2.handle() {
            co1.collision_groups().can_interact_with_self()
        } else {
            co1.collision_groups()
                .can_interact_with_groups(co2.collision_groups())
        }
    }
}
<commit_msg>Convenience methods for creating CollisionGroups<commit_after>use na::Real;
use pipeline::broad_phase::BroadPhasePairFilter;
use pipeline::world::CollisionObject;

const SELF_COLLISION: u32 = 1 << 31;
const ALL_GROUPS: u32 = (1 << 30) - 1;
const NO_GROUP: u32 = 0;

/// Groups of collision used to filter which object interact with which other one.
///
/// There are at most 30 groups indexed from 0 to 29 (included). This identifies collidable
/// entities by combining three attributes:
///    * A set of group this structure is member of.
///    * A collision group whitelist.
///    * A collision group blacklist.
///
/// For two entities to interact, they must be member of at least one group part of each-other's
/// whitelists, and must not be part of any blacklisted group. The blacklist always has priority on
/// the whitelist.
///
/// ### Example
/// For example if the object A is such that:
///    * It is part of the groups 1, 3, and 6.
///    * It whitelists the groups 6 and 7.
///    * It blacklists the group 1.
/// Let the object B be such that:
///    * It is part of the groups 1, 3, and 7.
///    * It whitelists the groups 3 and 7.
///    * It does not blacklist anything.
/// For example if the object C is such that:
///    * It is part of the groups 6 and 9.
///    * It whitelists the groups 3 and 7.
///    * It does not blacklist anything.
///
/// Then we have:
///    * A and C can interact because A whitelists the group 6 (which C is part of), and,
///    reciprocally, C whitelists the group 3 (which A is part of).
///    * A and B will **not** interact because B is part of the group 1 which is blacklisted by A.
///    * Finally, B and C will **not** interact either because, even if C whitelists the group 3
///    (which B is part of), B does not whitelists the groups 6 nor 9 (which B is part of).
#[derive(Clone, Debug, Copy)]
pub struct CollisionGroups {
    membership: u32,
    whitelist: u32,
    blacklist: u32,
}

impl CollisionGroups {
    /// Creates a new `CollisionGroups` that enables interactions with everything except
    /// self-interaction.
    #[inline]
    pub fn new() -> CollisionGroups {
        CollisionGroups {
            membership: ALL_GROUPS,
            whitelist: ALL_GROUPS,
            blacklist: NO_GROUP,
        }
    }

    /// Returns a copy of this object, updated with a new set of membership groups.
    ///
    /// # Examples
    ///
    /// ```
    /// const GROUP_A: usize = 0;
    /// const GROUP_B: usize = 29;
    /// const groups = CollisionGroups::new().with_membership(&[GROUP_A, GROUP_B]);
    /// assert!(groups.is_member_of(GROUP_A);
    /// assert!(groups.is_member_of(GROUP_B);
    /// ```
    #[inline]
    pub fn with_membership(self, groups: &[usize]) -> CollisionGroups {
        let membership: u32 = groups
            .iter()
            .fold(NO_GROUP, |acc, &group| acc | (1 << group)) as u32;
        assert!(membership < (1 << 30));
        CollisionGroups {
            membership,
            whitelist: self.whitelist,
            blacklist: self.blacklist,
        }
    }

    /// Returns a copy of this object, updated with a new set of whitelisted groups.
    ///
    /// # Examples
    ///
    /// ```
    /// const GROUP_A: usize = 0;
    /// const GROUP_B: usize = 29;
    /// const group_a = CollisionGroups::new().with_whitelist(&[GROUP_B]);
    /// assert!(!group_a.is_group_whitelisted(GROUP_A));
    /// assert!(group_a.is_group_whitelisted(GROUP_B));
    /// ```
    #[inline]
    pub fn with_whitelist(self, groups: &[usize]) -> CollisionGroups {
        let whitelist: u32 = groups
            .iter()
            .fold(NO_GROUP, |acc, &group| acc | (1 << group)) as u32;
        assert!(whitelist < (1 << 30));
        CollisionGroups {
            membership: self.membership,
            whitelist: whitelist,
            blacklist: self.blacklist,
        }
    }

    /// Returns a copy of this object, updated with a new set of blacklisted groups.
    ///
    /// # Examples
    ///
    /// ```
    /// const GROUP_A: usize = 0;
    /// const GROUP_B: usize = 29;
    /// const group_a = CollisionGroups::new().with_blacklist(&[GROUP_B]);
    /// assert!(!group_a.is_group_blacklisted(GROUP_A));
    /// assert!(group_a.is_group_blacklisted(GROUP_B));
    /// ```
    #[inline]
    pub fn with_blacklist(self, groups: &[usize]) -> CollisionGroups {
        let blacklist: u32 = groups
            .iter()
            .fold(NO_GROUP, |acc, &group| acc | (1 << group)) as u32;
        assert!(blacklist < (1 << 30));
        CollisionGroups {
            membership: self.membership,
            whitelist: self.whitelist,
            blacklist,
        }
    }

    /// The maximum allowed group identifier.
    #[inline]
    pub fn max_group_id() -> usize {
        29
    }

    #[inline]
    fn modify_mask(mask: &mut u32, group_id: usize, add: bool) {
        assert!(
            group_id < 30,
            "There are at most 30 groups indexed from 0 to 29 (included)."
        );

        if add {
            *mask = *mask | (1 << group_id)
        } else {
            *mask = *mask & !(1 << group_id)
        }
    }

    #[inline]
    fn set_mask(mask: &mut u32, groups: &[usize]) {
        *mask = 0;
        for g in groups.iter() {
            CollisionGroups::modify_mask(mask, *g, true);
        }
    }

    /// Adds or removes this entity from the given group.
    #[inline]
    pub fn modify_membership(&mut self, group_id: usize, add: bool) {
        CollisionGroups::modify_mask(&mut self.membership, group_id, add);
    }

    /// Adds or removes the given group from this entity whitelist.
    #[inline]
    pub fn modify_whitelist(&mut self, group_id: usize, add: bool) {
        CollisionGroups::modify_mask(&mut self.whitelist, group_id, add);
    }

    /// Adds or removes this entity from the given group.
    #[inline]
    pub fn modify_blacklist(&mut self, group_id: usize, add: bool) {
        CollisionGroups::modify_mask(&mut self.blacklist, group_id, add);
    }

    /// Make this object member of the given groups only.
    #[inline]
    pub fn set_membership(&mut self, groups: &[usize]) {
        CollisionGroups::set_mask(&mut self.membership, groups);
    }

    /// Whitelists the given groups only (others will be un-whitelisted).
    #[inline]
    pub fn set_whitelist(&mut self, groups: &[usize]) {
        CollisionGroups::set_mask(&mut self.whitelist, groups);
    }

    /// Blacklists the given groups only (others will be un-blacklisted).
    #[inline]
    pub fn set_blacklist(&mut self, groups: &[usize]) {
        CollisionGroups::set_mask(&mut self.blacklist, groups);
    }

    /// Copies the membership of another collision groups.
    #[inline]
    pub fn copy_membership(&mut self, other: &CollisionGroups) {
        self.membership = other.membership
    }

    /// Copies the whitelist of another collision groups.
    #[inline]
    pub fn copy_whitelist(&mut self, other: &CollisionGroups) {
        self.whitelist = other.whitelist
    }

    /// Copies the blacklist of another collision groups.
    #[inline]
    pub fn copy_blacklist(&mut self, other: &CollisionGroups) {
        self.blacklist = other.blacklist
    }

    /// Allows the object to interact with itself.
    #[inline]
    pub fn enable_self_interaction(&mut self) {
        self.whitelist = self.whitelist | SELF_COLLISION;
    }

    /// Prevents the object from interacting with itself.
    #[inline]
    pub fn disable_self_interaction(&mut self) {
        self.whitelist = self.whitelist & !SELF_COLLISION;
    }

    #[inline]
    fn is_inside_mask(mask: u32, group_id: usize) -> bool {
        assert!(
            group_id < 30,
            "There are at most 30 groups indexed from 0 to 29 (included)."
        );
        mask & (1 << group_id) != 0
    }

    /// Tests if this entity is part of the given group.
    #[inline]
    pub fn is_member_of(&self, group_id: usize) -> bool {
        CollisionGroups::is_inside_mask(self.membership, group_id)
    }

    /// Tests if the given group is whitelisted.
    #[inline]
    pub fn is_group_whitelisted(&self, group_id: usize) -> bool {
        CollisionGroups::is_inside_mask(self.whitelist, group_id)
    }

    /// Tests if the given group is blacklisted.
    #[inline]
    pub fn is_group_blacklisted(&self, group_id: usize) -> bool {
        CollisionGroups::is_inside_mask(self.blacklist, group_id)
    }

    /// Tests whether interactions with a given group is possible.
    ///
    /// Collision is possible if `group_id` is whitelisted but not blacklisted.
    #[inline]
    pub fn can_interact_with(&self, group_id: usize) -> bool {
        !CollisionGroups::is_inside_mask(self.blacklist, group_id)
            && CollisionGroups::is_inside_mask(self.whitelist, group_id)
    }

    /// Tests whether two collision groups have at least one group in common.
    #[inline]
    pub fn can_interact_with_groups(&self, other: &CollisionGroups) -> bool {
        // FIXME: is there a more bitwise-y way of doing this?
        self.membership & other.blacklist == 0
            && other.membership & self.blacklist == 0
            && self.membership & other.whitelist != 0
            && other.membership & self.whitelist != 0
    }

    /// Tests whether self-interaction is enabled.
    #[inline]
    pub fn can_interact_with_self(&self) -> bool {
        self.whitelist & SELF_COLLISION != 0
    }
}

/// A collision filter based collision groups.
pub struct CollisionGroupsPairFilter;

impl CollisionGroupsPairFilter {
    /// Creates a new collision filter based collision groups.
    #[inline]
    pub fn new() -> CollisionGroupsPairFilter {
        CollisionGroupsPairFilter
    }
}

impl<N: Real, T> BroadPhasePairFilter<N, T> for CollisionGroupsPairFilter {
    fn is_pair_valid(&self, co1: &CollisionObject<N, T>, co2: &CollisionObject<N, T>) -> bool {
        if co1.handle() == co2.handle() {
            co1.collision_groups().can_interact_with_self()
        } else {
            co1.collision_groups()
                .can_interact_with_groups(co2.collision_groups())
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2021 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://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 crate::buffer::BufferAccess;
use crate::buffer::BufferAccessObject;
use std::sync::Arc;

/// A collection of vertex buffers.
pub trait VertexBuffersCollection {
    /// Converts `self` into a list of buffers.
    // TODO: better than a Vec
    fn into_vec(self) -> Vec<Arc<dyn BufferAccess>>;
}

impl VertexBuffersCollection for () {
    #[inline]
    fn into_vec(self) -> Vec<Arc<dyn BufferAccess>> {
        Vec::new()
    }
}

impl<T: BufferAccessObject> VertexBuffersCollection for T {
    #[inline]
    fn into_vec(self) -> Vec<Arc<dyn BufferAccess>> {
        vec![self.as_buffer_access_object()]
    }
}

impl<T: BufferAccessObject> VertexBuffersCollection for Vec<T> {
    #[inline]
    fn into_vec(self) -> Vec<Arc<dyn BufferAccess>> {
        self.into_iter()
            .map(|src| src.as_buffer_access_object())
            .collect()
    }
}

macro_rules! impl_collection {
    ($first:ident $(, $others:ident)+) => (
        impl<$first$(, $others)+> VertexBuffersCollection for ($first, $($others),+)
            where $first: BufferAccessObject
                  $(, $others: BufferAccessObject)*
        {
            #[inline]
            fn into_vec(self) -> Vec<Arc<dyn BufferAccess>> {
                #![allow(non_snake_case)]

                let ($first, $($others,)*) = self;
                let mut list = Vec::new();
                list.push($first.as_buffer_access_object());

                $(
                    list.push($others.as_buffer_access_object());
                )+

                list
            }
        }

        impl_collection!($($others),+);
    );

    ($i:ident) => ();
}

impl_collection!(Z, Y, X, W, V, U, T, S, R, Q, P, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);
<commit_msg>arrays now implement VertexBufferCollection (#1852)<commit_after>// Copyright (c) 2021 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://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 crate::buffer::BufferAccess;
use crate::buffer::BufferAccessObject;
use std::sync::Arc;

/// A collection of vertex buffers.
pub trait VertexBuffersCollection {
    /// Converts `self` into a list of buffers.
    // TODO: better than a Vec
    fn into_vec(self) -> Vec<Arc<dyn BufferAccess>>;
}

impl VertexBuffersCollection for () {
    #[inline]
    fn into_vec(self) -> Vec<Arc<dyn BufferAccess>> {
        Vec::new()
    }
}

impl<T: BufferAccessObject> VertexBuffersCollection for T {
    #[inline]
    fn into_vec(self) -> Vec<Arc<dyn BufferAccess>> {
        vec![self.as_buffer_access_object()]
    }
}

impl<T: BufferAccessObject> VertexBuffersCollection for Vec<T> {
    #[inline]
    fn into_vec(self) -> Vec<Arc<dyn BufferAccess>> {
        self.into_iter()
            .map(|src| src.as_buffer_access_object())
            .collect()
    }
}

impl<T: BufferAccessObject, const N: usize> VertexBuffersCollection for [T; N] {
    #[inline]
    fn into_vec(self) -> Vec<Arc<dyn BufferAccess>> {
        self.into_iter()
            .map(|src| src.as_buffer_access_object())
            .collect()
    }
}

macro_rules! impl_collection {
    ($first:ident $(, $others:ident)+) => (
        impl<$first$(, $others)+> VertexBuffersCollection for ($first, $($others),+)
            where $first: BufferAccessObject
                  $(, $others: BufferAccessObject)*
        {
            #[inline]
            fn into_vec(self) -> Vec<Arc<dyn BufferAccess>> {
                #![allow(non_snake_case)]

                let ($first, $($others,)*) = self;
                let mut list = Vec::new();
                list.push($first.as_buffer_access_object());

                $(
                    list.push($others.as_buffer_access_object());
                )+

                list
            }
        }

        impl_collection!($($others),+);
    );

    ($i:ident) => ();
}

impl_collection!(Z, Y, X, W, V, U, T, S, R, Q, P, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);

#[cfg(test)]
mod tests {
    use super::VertexBuffersCollection;
    use crate::buffer::BufferAccess;
    use crate::buffer::BufferAccessObject;
    use crate::buffer::BufferInner;
    use crate::device::Device;
    use crate::device::DeviceOwned;
    use crate::device::Queue;
    use crate::sync::AccessError;
    use crate::DeviceSize;
    use std::sync::Arc;

    struct DummyBufferA {}
    struct DummyBufferB {}

    unsafe impl BufferAccess for DummyBufferA {
        fn inner(&self) -> BufferInner<'_> {
            unimplemented!()
        }

        fn size(&self) -> DeviceSize {
            unimplemented!()
        }

        fn conflict_key(&self) -> (u64, u64) {
            unimplemented!()
        }

        fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
            unimplemented!()
        }

        unsafe fn increase_gpu_lock(&self) {
            unimplemented!()
        }

        unsafe fn unlock(&self) {
            unimplemented!()
        }
    }

    unsafe impl DeviceOwned for DummyBufferA {
        fn device(&self) -> &Arc<Device> {
            unimplemented!()
        }
    }

    impl BufferAccessObject for Arc<DummyBufferA> {
        fn as_buffer_access_object(self: &Arc<DummyBufferA>) -> Arc<dyn BufferAccess> {
            self.clone()
        }
    }

    unsafe impl BufferAccess for DummyBufferB {
        fn inner(&self) -> BufferInner<'_> {
            unimplemented!()
        }

        fn size(&self) -> DeviceSize {
            unimplemented!()
        }

        fn conflict_key(&self) -> (u64, u64) {
            unimplemented!()
        }

        fn try_gpu_lock(&self, _: bool, _: &Queue) -> Result<(), AccessError> {
            unimplemented!()
        }

        unsafe fn increase_gpu_lock(&self) {
            unimplemented!()
        }

        unsafe fn unlock(&self) {
            unimplemented!()
        }
    }

    unsafe impl DeviceOwned for DummyBufferB {
        fn device(&self) -> &Arc<Device> {
            unimplemented!()
        }
    }

    impl BufferAccessObject for Arc<DummyBufferB> {
        fn as_buffer_access_object(self: &Arc<DummyBufferB>) -> Arc<dyn BufferAccess> {
            self.clone()
        }
    }

    fn takes_collection<C: VertexBuffersCollection>(_: C) {}

    #[test]
    fn vertex_buffer_collection() {
        let concrete_a = Arc::new(DummyBufferA {});
        let concrete_b = Arc::new(DummyBufferB {});
        let concrete_aa = Arc::new(DummyBufferA {});
        let dynamic_a = concrete_a.clone() as Arc<dyn BufferAccess>;
        let dynamic_b = concrete_b.clone() as Arc<dyn BufferAccess>;

        // Concrete/Dynamic alone are valid
        takes_collection(concrete_a.clone());
        takes_collection(dynamic_a.clone());

        // Tuples of any variation are valid
        takes_collection((concrete_a.clone(), concrete_b.clone()));
        takes_collection((concrete_a.clone(), dynamic_b.clone()));
        takes_collection((dynamic_a.clone(), dynamic_b.clone()));

        // Vec need all the same type
        takes_collection(vec![concrete_a.clone(), concrete_aa.clone()]);
        takes_collection(vec![dynamic_a.clone(), dynamic_b.clone()]);
        // But casting the first or starting off with a dynamic will allow all variations
        takes_collection(vec![
            concrete_a.clone() as Arc<dyn BufferAccess>,
            concrete_b.clone(),
        ]);
        takes_collection(vec![dynamic_a.clone(), concrete_b.clone()]);

        // Arrays are similar to Vecs
        takes_collection([concrete_a.clone(), concrete_aa.clone()]);
        takes_collection([dynamic_a.clone(), dynamic_b.clone()]);
        takes_collection([
            concrete_a.clone() as Arc<dyn BufferAccess>,
            concrete_b.clone(),
        ]);
        takes_collection([dynamic_a.clone(), concrete_b.clone()]);
    }
}
<|endoftext|>


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

///
/// Example cUrl request:
/// curl -X PUT -d '[[[{"id":"setter:talk@link.mozilla.org"}], {"String": "hello world"}]]' http://localhost:3000/api/v1/channels/set
///

use foxbox_taxonomy::adapter::*;
use foxbox_taxonomy::manager::AdapterManager;
use foxbox_taxonomy::api::{ Error, InternalError, User };
use foxbox_taxonomy::channel::*;
use foxbox_taxonomy::services::{ AdapterId, Id, Service, ServiceId };
use foxbox_taxonomy::util::Maybe;
use foxbox_taxonomy::values::{ Type, Value };
use std::collections::HashMap;
use std::sync::Arc;

pub mod engine;
pub use self::engine::TtsEngine;

// eSpeak is the only engine supported for now.
mod espeak;
use self::espeak::EspeakEngine;

static ADAPTER_ID: &'static str = "espeak_adapter@link.mozilla.org";
static ADAPTER_NAME: &'static str = "eSpeak adapter";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

pub struct TtsAdapter<T> {
    talk_setter_id: Id<Channel>,
    engine: T
}

impl<T: TtsEngine> Adapter for TtsAdapter<T> {
    fn id(&self) -> Id<AdapterId> {
        adapter_id!(ADAPTER_ID)
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User) -> ResultMap<Id<Channel>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, _: User) -> ResultMap<Id<Channel>, (), Error> {
        use core::ops::Deref;

        values.drain().map(|(id, value)| {
            if id == self.talk_setter_id {
                if let Value::String(text) = value {
                    self.engine.say(text.deref());
                    return (id, Ok(()));
                }
            }
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }
}

pub fn init(adapt: &Arc<AdapterManager>) -> Result<(), Error> {
    let engine = EspeakEngine { };
    if !engine.init() {
        warn!("eSpeak initialization failed!");
        return Err(Error::InternalError(InternalError::GenericError("eSpeak initialization failed!".to_owned())));
    }

    let talk_setter_id = Id::new("setter:talk@link.mozilla.org");
    try!(adapt.add_adapter(Arc::new(TtsAdapter {
        talk_setter_id: talk_setter_id.clone(),
        engine: engine
    })));
    let service_id = service_id!("espeak@link.mozilla.org");
    let adapter_id = adapter_id!(ADAPTER_ID);
    try!(adapt.add_service(Service::empty(&service_id, &adapter_id)));
    try!(adapt.add_channel(Channel {
        feature: Id::new("speak/sentence"),
        supports_send: Some(Signature::accepts(Maybe::Required(Type::String))),
        id: talk_setter_id,
        service: service_id,
        adapter: adapter_id,
        .. Channel::default()
    }));
    Ok(())
}
<commit_msg>TTS: Getting rid of centralized Type<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

///
/// Example cUrl request:
/// curl -X PUT -d '[[[{"id":"setter:talk@link.mozilla.org"}], {"String": "hello world"}]]' http://localhost:3000/api/v1/channels/set
///

use foxbox_taxonomy::adapter::*;
use foxbox_taxonomy::manager::AdapterManager;
use foxbox_taxonomy::api::{ Error, InternalError, User };
use foxbox_taxonomy::channel::*;
use foxbox_taxonomy::services::{ AdapterId, Id, Service, ServiceId };
use foxbox_taxonomy::util::Maybe;
use foxbox_taxonomy::values::{ format, Value };
use std::collections::HashMap;
use std::sync::Arc;

pub mod engine;
pub use self::engine::TtsEngine;

// eSpeak is the only engine supported for now.
mod espeak;
use self::espeak::EspeakEngine;

static ADAPTER_ID: &'static str = "espeak_adapter@link.mozilla.org";
static ADAPTER_NAME: &'static str = "eSpeak adapter";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

pub struct TtsAdapter<T> {
    talk_setter_id: Id<Channel>,
    engine: T
}

impl<T: TtsEngine> Adapter for TtsAdapter<T> {
    fn id(&self) -> Id<AdapterId> {
        adapter_id!(ADAPTER_ID)
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User) -> ResultMap<Id<Channel>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, _: User) -> ResultMap<Id<Channel>, (), Error> {
        use core::ops::Deref;

        values.drain().map(|(id, value)| {
            if id == self.talk_setter_id {
                if let Value::String(text) = value {
                    self.engine.say(text.deref());
                    return (id, Ok(()));
                }
            }
            (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
        }).collect()
    }
}

pub fn init(adapt: &Arc<AdapterManager>) -> Result<(), Error> {
    let engine = EspeakEngine { };
    if !engine.init() {
        warn!("eSpeak initialization failed!");
        return Err(Error::InternalError(InternalError::GenericError("eSpeak initialization failed!".to_owned())));
    }

    let talk_setter_id = Id::new("setter:talk@link.mozilla.org");
    try!(adapt.add_adapter(Arc::new(TtsAdapter {
        talk_setter_id: talk_setter_id.clone(),
        engine: engine
    })));
    let service_id = service_id!("espeak@link.mozilla.org");
    let adapter_id = adapter_id!(ADAPTER_ID);
    try!(adapt.add_service(Service::empty(&service_id, &adapter_id)));
    try!(adapt.add_channel(Channel {
        feature: Id::new("speak/sentence"),
        supports_send: Some(Signature::accepts(Maybe::Required(format::STRING.clone()))),
        id: talk_setter_id,
        service: service_id,
        adapter: adapter_id,
        .. Channel::default()
    }));
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

#![feature(generic_associated_types)]
#![feature(associated_type_defaults)]

//FIXME(#44265): "lifetime parameters are not allowed on this type" errors will be addressed in a
//follow-up PR

// A Collection trait and collection families.
// Based on http://smallcultfollowing.com/babysteps/blog/2016/11/03/associated-type-constructors-part-2-family-traits/

trait Collection<T> {
    fn empty() -> Self;
    fn add(&mut self, value: T);
    fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>;
    //~^ ERROR lifetime parameters are not allowed on this type [E0110]
    type Iter<'iter>: Iterator<Item=&'iter T>;
    type Family: CollectionFamily;
    // Test associated type defaults with parameters
    type Sibling<U>: Collection<U> = <<Self as Collection<T>>::Family as CollectionFamily>::Member<U>;
    //~^ ERROR type parameters are not allowed on this type [E0109]
}

trait CollectionFamily {
    type Member<T>: Collection<T, Family = Self>;
}

struct VecFamily;

impl CollectionFamily for VecFamily {
    type Member<T> = Vec<T>;
}

impl<T> Collection<T> for Vec<T> {
    fn empty() -> Self {
        Vec::new()
    }
    fn add(&mut self, value: T) {
        self.push(value)
    }
    fn iterate<'iter>(&'iter self) -> Self::Iter<'iter> {
    //~^ ERROR lifetime parameters are not allowed on this type [E0110]
        self.iter()
    }
    type Iter<'iter> = std::slice::Iter<'iter, T>;
    type Family = VecFamily;
}

fn floatify<C>(ints: &C) -> <<C as Collection<i32>>::Family as CollectionFamily>::Member<f32>
    //~^ ERROR type parameters are not allowed on this type [E0109]
    where C: Collection<i32> {
    let mut res = C::Family::Member::<f32>::empty();
    for &v in ints.iterate() {
        res.add(v as f32);
    }
    res
}

fn floatify_sibling<C>(ints: &C) -> <C as Collection<i32>>::Sibling<f32>
    //~^ ERROR type parameters are not allowed on this type [E0109]
    where C: Collection<i32> {
    let mut res = C::Family::Member::<f32>::empty();
    for &v in ints.iterate() {
        res.add(v as f32);
    }
    res
}

fn use_floatify() {
    let a = vec![1i32, 2, 3];
    let b = floatify(a);
    println!("{}", b.iterate().next());
    let c = floatify_sibling(a);
    println!("{}", c.iterate().next());
}

fn main() {}<commit_msg>Tidy up the code<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

#![feature(generic_associated_types)]
#![feature(associated_type_defaults)]

//FIXME(#44265): "lifetime parameters are not allowed on this type" errors will be addressed in a
//follow-up PR

// A Collection trait and collection families. Based on
// http://smallcultfollowing.com/babysteps/blog/2016/11/03/
// associated-type-constructors-part-2-family-traits/

trait Collection<T> {
    fn empty() -> Self;
    fn add(&mut self, value: T);
    fn iterate<'iter>(&'iter self) -> Self::Iter<'iter>;
    //~^ ERROR lifetime parameters are not allowed on this type [E0110]
    type Iter<'iter>: Iterator<Item=&'iter T>;
    type Family: CollectionFamily;
    // Test associated type defaults with parameters
    type Sibling<U>: Collection<U> = <<Self as Collection<T>>::Family as CollectionFamily>::
        Member<U>;
    //~^ ERROR type parameters are not allowed on this type [E0109]
}

trait CollectionFamily {
    type Member<T>: Collection<T, Family = Self>;
}

struct VecFamily;

impl CollectionFamily for VecFamily {
    type Member<T> = Vec<T>;
}

impl<T> Collection<T> for Vec<T> {
    fn empty() -> Self {
        Vec::new()
    }
    fn add(&mut self, value: T) {
        self.push(value)
    }
    fn iterate<'iter>(&'iter self) -> Self::Iter<'iter> {
    //~^ ERROR lifetime parameters are not allowed on this type [E0110]
        self.iter()
    }
    type Iter<'iter> = std::slice::Iter<'iter, T>;
    type Family = VecFamily;
}

fn floatify<C>(ints: &C) -> <<C as Collection<i32>>::Family as CollectionFamily>::Member<f32>
    //~^ ERROR type parameters are not allowed on this type [E0109]
    where C: Collection<i32> {
    let mut res = C::Family::Member::<f32>::empty();
    for &v in ints.iterate() {
        res.add(v as f32);
    }
    res
}

fn floatify_sibling<C>(ints: &C) -> <C as Collection<i32>>::Sibling<f32>
    //~^ ERROR type parameters are not allowed on this type [E0109]
    where C: Collection<i32> {
    let mut res = C::Family::Member::<f32>::empty();
    for &v in ints.iterate() {
        res.add(v as f32);
    }
    res
}

fn use_floatify() {
    let a = vec![1i32, 2, 3];
    let b = floatify(a);
    println!("{}", b.iterate().next());
    let c = floatify_sibling(a);
    println!("{}", c.iterate().next());
}

fn main() {}
<|endoftext|>