<commit_before>import std::os;
import std::fs;
import std::vec;
import metadata::cstore;
import driver::session;
import util::filesearch;
import std::map;

export get_rpath_flags, test;

#[cfg(target_os="linux")]
#[cfg(target_os="macos")]
fn get_rpath_flags(_sess: session::session, _out_filename: str) -> [str] {
    log "preparing the RPATH!";

    // FIXME
    /*
    let cwd = os::getcwd();
    let sysroot = sess.filesearch().sysroot();
    let output = out_filename;
    let libs = cstore::get_used_crate_files(sess.get_cstore());
    let target_triple = sess.get_opts().target_triple;
    let rpaths = get_rpaths(cwd, sysroot, output, libs, target_triple);
    */
    let rpaths = [];
    rpaths_to_flags(rpaths)
}

#[cfg(target_os="win32")]
fn get_rpath_flags(_sess: session::session, _out_filename: str) -> [str] {
    []
}

fn rpaths_to_flags(rpaths: [str]) -> [str] {
    vec::map({ |rpath| #fmt("-Wl,-rpath,%s",rpath)}, rpaths)
}

fn get_rpaths(cwd: fs::path, sysroot: fs::path,
              output: fs::path, libs: [fs::path],
              target_triple: str) -> [str] {
    log #fmt("cwd: %s", cwd);
    log #fmt("sysroot: %s", sysroot);
    log #fmt("output: %s", output);
    log #fmt("libs:");
    for libpath in libs {
        log #fmt("    %s", libpath);
    }
    log #fmt("target_triple: %s", target_triple);

    // Use relative paths to the libraries. Binaries can be moved
    // as long as they maintain the relative relationship to the
    // crates they depend on.
    let rpaths = get_rpaths_relative_to_output(cwd, output, libs);

    // Make backup absolute paths to the libraries. Binaries can
    // be moved as long as the crates they link against don't move.
    rpaths += get_absolute_rpaths(cwd, libs);

    // And a final backup rpath to the global library location.
    rpaths += [get_install_prefix_rpath(target_triple)];

    // Remove duplicates
    let rpaths = minimize_rpaths(rpaths);
    ret rpaths;
}

fn get_rpaths_relative_to_output(cwd: fs::path,
                                 output: fs::path,
                                 libs: [fs::path]) -> [str] {
    vec::map(bind get_rpath_relative_to_output(cwd, output, _), libs)
}

fn get_rpath_relative_to_output(_cwd: fs::path,
                                _output: fs::path,
                                _lib: fs::path) -> str {
    fail;
    /*get_relative_to(
        get_absolute(cwd, output),
        get_absolute(cwd, lib))*/
}

// Find the relative path from one file to another
fn get_relative_to(_abs1: fs::path, _abs2: fs::path) -> fs::path {
    fail;
}

fn get_absolute_rpaths(cwd: fs::path, libs: [fs::path]) -> [str] {
    vec::map(bind get_absolute_rpath(cwd, _), libs)
}

fn get_absolute_rpath(cwd: fs::path, lib: fs::path) -> str {
    get_absolute(cwd, lib)
}

fn get_absolute(cwd: fs::path, lib: fs::path) -> fs::path {
    if fs::path_is_absolute(lib) {
        lib
    } else {
        fs::connect(cwd, lib)
    }
}

fn get_install_prefix_rpath(target_triple: str) -> str {
    let install_prefix = #env("CFG_PREFIX");

    if install_prefix == "" {
        fail "rustc compiled without CFG_PREFIX environment variable";
    }

    let path = [install_prefix]
        + filesearch::relative_target_lib_path(target_triple);
    check vec::is_not_empty(path);
    fs::connect_many(path)
}

fn minimize_rpaths(rpaths: [str]) -> [str] {
    let set = map::new_str_hash::<()>();
    for rpath in rpaths { set.insert(rpath, ()); }
    let minimized = [];
    for each rpath in set.keys() { minimized += [rpath]; }
    ret minimized;
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
mod test {
    #[test]
    fn test_rpaths_to_flags() {
        let flags = rpaths_to_flags(["path1", "path2"]);
        assert flags == ["-Wl,-rpath,path1", "-Wl,-rpath,path2"];
    }

    #[test]
    fn test_get_absolute1() {
        let cwd = "/dir";
        let lib = "some/path/lib";
        let res = get_absolute(cwd, lib);
        assert res == "/dir/some/path/lib";
    }

    #[test]
    fn test_get_absolute2() {
        let cwd = "/dir";
        let lib = "/some/path/lib";
        let res = get_absolute(cwd, lib);
        assert res == "/some/path/lib";
    }

    #[test]
    fn test_prefix_rpath() {
        let res = get_install_prefix_rpath("triple");
        assert res == #env("CFG_PREFIX") + "/lib/rustc/triple/lib";
    }

    #[test]
    fn test_minimize() {
        let res = minimize_rpaths(["rpath1", "rpath2", "rpath1"]);
        assert res == ["rpath1", "rpath2"];
    }

    #[test]
    #[ignore]
    fn test_relative_to1() {
        let p1 = "/usr/bin/rustc";
        let p2 = "/usr/lib/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib";
    }

    #[test]
    #[ignore]
    fn test_relative_to2() {
        let p1 = "/usr/bin/rustc";
        let p2 = "/usr/bin/../lib/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib";
    }

    #[test]
    #[ignore]
    fn test_relative_to3() {
        let p1 = "/usr/bin/whatever/rustc";
        let p2 = "/usr/lib/whatever/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../../lib/whatever";
    }

    #[test]
    #[ignore]
    fn test_relative_to4() {
        let p1 = "/usr/bin/whatever/../rustc";
        let p2 = "/usr/lib/whatever/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib/whatever";
    }

    #[test]
    #[ignore]
    fn test_relative_to5() {
        let p1 = "/usr/bin/whatever/../rustc";
        let p2 = "/usr/lib/whatever/../mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib/whatever";
    }
}
<commit_msg>Figure out the relative path from output to each crate<commit_after>import std::os;
import std::fs;
import std::os_fs;
import std::vec;
import std::map;
import std::str;
import std::uint;
import metadata::cstore;
import driver::session;
import util::filesearch;

export get_rpath_flags, test;

#[cfg(target_os="linux")]
#[cfg(target_os="macos")]
fn get_rpath_flags(_sess: session::session, _out_filename: str) -> [str] {
    log "preparing the RPATH!";

    // FIXME
    /*
    let cwd = os::getcwd();
    let sysroot = sess.filesearch().sysroot();
    let output = out_filename;
    let libs = cstore::get_used_crate_files(sess.get_cstore());
    let target_triple = sess.get_opts().target_triple;
    let rpaths = get_rpaths(cwd, sysroot, output, libs, target_triple);
    */
    let rpaths = [];
    rpaths_to_flags(rpaths)
}

#[cfg(target_os="win32")]
fn get_rpath_flags(_sess: session::session, _out_filename: str) -> [str] {
    []
}

fn rpaths_to_flags(rpaths: [str]) -> [str] {
    vec::map({ |rpath| #fmt("-Wl,-rpath,%s",rpath)}, rpaths)
}

fn get_rpaths(cwd: fs::path, sysroot: fs::path,
              output: fs::path, libs: [fs::path],
              target_triple: str) -> [str] {
    log #fmt("cwd: %s", cwd);
    log #fmt("sysroot: %s", sysroot);
    log #fmt("output: %s", output);
    log #fmt("libs:");
    for libpath in libs {
        log #fmt("    %s", libpath);
    }
    log #fmt("target_triple: %s", target_triple);

    // Use relative paths to the libraries. Binaries can be moved
    // as long as they maintain the relative relationship to the
    // crates they depend on.
    let rpaths = get_rpaths_relative_to_output(cwd, output, libs);

    // Make backup absolute paths to the libraries. Binaries can
    // be moved as long as the crates they link against don't move.
    rpaths += get_absolute_rpaths(cwd, libs);

    // And a final backup rpath to the global library location.
    rpaths += [get_install_prefix_rpath(target_triple)];

    // Remove duplicates
    let rpaths = minimize_rpaths(rpaths);
    ret rpaths;
}

fn get_rpaths_relative_to_output(cwd: fs::path,
                                 output: fs::path,
                                 libs: [fs::path]) -> [str] {
    vec::map(bind get_rpath_relative_to_output(cwd, output, _), libs)
}

fn get_rpath_relative_to_output(cwd: fs::path,
                                output: fs::path,
                                lib: fs::path) -> str {
    "$ORIGIN" + fs::path_sep() + get_relative_to(
        get_absolute(cwd, output),
        get_absolute(cwd, lib))
}

// Find the relative path from one file to another
fn get_relative_to(abs1: fs::path, abs2: fs::path) -> fs::path {
    assert fs::path_is_absolute(abs1);
    assert fs::path_is_absolute(abs2);
    let normal1 = fs::normalize(abs1);
    let normal2 = fs::normalize(abs2);
    let split1 = str::split(normal1, os_fs::path_sep as u8);
    let split2 = str::split(normal2, os_fs::path_sep as u8);
    let len1 = vec::len(split1);
    let len2 = vec::len(split2);
    assert len1 > 0u;
    assert len2 > 0u;

    let max_common_path = uint::min(len1, len2) - 1u;
    let start_idx = 0u;
    while start_idx < max_common_path
        && split1[start_idx] == split2[start_idx] {
        start_idx += 1u;
    }

    let path = [];

    for each _ in uint::range(start_idx, len1 - 1u) {
        path += [".."];
    }

    path += vec::slice(split2, start_idx, len2 - 1u);

    check vec::is_not_empty(path);
    ret fs::connect_many(path);
}

fn get_absolute_rpaths(cwd: fs::path, libs: [fs::path]) -> [str] {
    vec::map(bind get_absolute_rpath(cwd, _), libs)
}

fn get_absolute_rpath(cwd: fs::path, lib: fs::path) -> str {
    get_absolute(cwd, lib)
}

fn get_absolute(cwd: fs::path, lib: fs::path) -> fs::path {
    if fs::path_is_absolute(lib) {
        lib
    } else {
        fs::connect(cwd, lib)
    }
}

fn get_install_prefix_rpath(target_triple: str) -> str {
    let install_prefix = #env("CFG_PREFIX");

    if install_prefix == "" {
        fail "rustc compiled without CFG_PREFIX environment variable";
    }

    let path = [install_prefix]
        + filesearch::relative_target_lib_path(target_triple);
    check vec::is_not_empty(path);
    fs::connect_many(path)
}

fn minimize_rpaths(rpaths: [str]) -> [str] {
    let set = map::new_str_hash::<()>();
    for rpath in rpaths { set.insert(rpath, ()); }
    let minimized = [];
    for each rpath in set.keys() { minimized += [rpath]; }
    ret minimized;
}

#[cfg(target_os = "linux")]
#[cfg(target_os = "macos")]
mod test {
    #[test]
    fn test_rpaths_to_flags() {
        let flags = rpaths_to_flags(["path1", "path2"]);
        assert flags == ["-Wl,-rpath,path1", "-Wl,-rpath,path2"];
    }

    #[test]
    fn test_get_absolute1() {
        let cwd = "/dir";
        let lib = "some/path/lib";
        let res = get_absolute(cwd, lib);
        assert res == "/dir/some/path/lib";
    }

    #[test]
    fn test_get_absolute2() {
        let cwd = "/dir";
        let lib = "/some/path/lib";
        let res = get_absolute(cwd, lib);
        assert res == "/some/path/lib";
    }

    #[test]
    fn test_prefix_rpath() {
        let res = get_install_prefix_rpath("triple");
        assert res == #env("CFG_PREFIX") + "/lib/rustc/triple/lib";
    }

    #[test]
    fn test_minimize() {
        let res = minimize_rpaths(["rpath1", "rpath2", "rpath1"]);
        assert res == ["rpath1", "rpath2"];
    }

    #[test]
    fn test_relative_to1() {
        let p1 = "/usr/bin/rustc";
        let p2 = "/usr/lib/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib";
    }

    #[test]
    fn test_relative_to2() {
        let p1 = "/usr/bin/rustc";
        let p2 = "/usr/bin/../lib/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib";
    }

    #[test]
    fn test_relative_to3() {
        let p1 = "/usr/bin/whatever/rustc";
        let p2 = "/usr/lib/whatever/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../../lib/whatever";
    }

    #[test]
    fn test_relative_to4() {
        let p1 = "/usr/bin/whatever/../rustc";
        let p2 = "/usr/lib/whatever/mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib/whatever";
    }

    #[test]
    fn test_relative_to5() {
        let p1 = "/usr/bin/whatever/../rustc";
        let p2 = "/usr/lib/whatever/../mylib";
        let res = get_relative_to(p1, p2);
        assert res == "../lib";
    }

    #[test]
    fn test_relative_to6() {
        let p1 = "/1";
        let p2 = "/2/3";
        let res = get_relative_to(p1, p2);
        assert res == "2";
    }

    #[test]
    fn test_relative_to7() {
        let p1 = "/1/2";
        let p2 = "/3";
        let res = get_relative_to(p1, p2);
        assert res == "..";
    }

    #[test]
    fn test_rpath_relative() {
        let res = get_rpath_relative_to_output(
            "/usr", "bin/rustc", "lib/libstd.so");
        assert res == "$ORIGIN/../lib";
    }
}
<|endoftext|>


<commit_before>use std::process::Command;
use std::io::Write;

pub fn read() -> Result<String, String> {
    match Command::new("which").arg("xclip").status() {
        Ok(status) => if !status.success() { return Err("missing xclip program".to_string()) },
        Err(e) => return Err(e.detail().unwrap_or("unknown IO error".to_string()))
    }

    let output = match Command::new("xclip").args(&["-out", "-selection", "clipboard"]).output() {
        Ok(output) => output,
        Err(e) => return Err(e.detail().unwrap_or("unknown IO error".to_string()))
    };

    let s = match String::from_utf8(output.stdout) {
        Ok(s) => s,
        Err(_) => return Err("clipboard contains invalid utf8 characters".to_string())
    };

    Ok(s)
}

pub fn write(s: &str) -> Result<(), String> {
    match Command::new("which").arg("xclip").status() {
        Ok(status) => if !status.success() { return Err("missing xclip program".to_string()) },
        Err(e) => return Err(e.detail().unwrap_or("unknown IO error".to_string()))
    }

    let mut child = match Command::new("xclip").args(&["-in", "-selection", "clipboard"]).spawn() {
        Ok(child) => child,
        Err(e) => return Err(e.detail().unwrap_or("unknown IO error".to_string()))
    };

    let stdin = match child.stdin {
        Some(ref mut stdin) =>  stdin,
        None => return Err("unable to get stdin of xclip".to_string())
    };

    match stdin.write(s.as_bytes()) {
        Ok(_) => Ok(()),
        Err(e) => return Err(e.detail().unwrap_or("error writing to xclip".to_string()))
    }
}
<commit_msg>capture stdin for xclip process<commit_after>use std::process::{Stdio, Command};
use std::io::Write;

pub fn read() -> Result<String, String> {
    match Command::new("which").arg("xclip").status() {
        Ok(status) => if !status.success() { return Err("missing xclip program".to_string()) },
        Err(e) => return Err(e.detail().unwrap_or("unknown IO error".to_string()))
    }

    let output = match Command::new("xclip").args(&["-out", "-selection", "clipboard"]).output() {
        Ok(output) => output,
        Err(e) => return Err(e.detail().unwrap_or("unknown IO error".to_string()))
    };

    let s = match String::from_utf8(output.stdout) {
        Ok(s) => s,
        Err(_) => return Err("clipboard contains invalid utf8 characters".to_string())
    };

    Ok(s)
}

pub fn write(s: &str) -> Result<(), String> {
    match Command::new("which").arg("xclip").status() {
        Ok(status) => if !status.success() { return Err("missing xclip program".to_string()) },
        Err(e) => return Err(e.detail().unwrap_or("unknown IO error".to_string()))
    }

    let mut child = match Command::new("xclip").args(&["-in", "-selection", "clipboard"]).stdin(Stdio::capture()).spawn() {
        Ok(child) => child,
        Err(e) => return Err(e.detail().unwrap_or("unknown IO error".to_string()))
    };

    let stdin = match child.stdin {
        Some(ref mut stdin) =>  stdin,
        None => return Err("unable to get stdin of xclip".to_string())
    };

    match stdin.write(s.as_bytes()) {
        Ok(_) => Ok(()),
        Err(e) => return Err(e.detail().unwrap_or("error writing to xclip".to_string()))
    }
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use analysis;
use analysis::symbols;
use analysis::namespaces::MAIN;
use case::CaseExt;
use env::Env;
use file_saver::save_to_file;
use library::*;
use library::Type as LType;
use nameutil;
use regex::{Captures, Regex};
use self::format::reformat_doc;
use stripper_lib::Type as SType;
use stripper_lib::{TypeStruct, write_file_name, write_item_doc};

mod format;

trait FunctionTraitType {
    fn functions(&self) -> &Vec<Function>;
    fn name(&self) -> &str;
}

macro_rules! impl_function_trait_type {
    ($ty:ident) => {
        impl FunctionTraitType for $ty {
            fn functions(&self) -> &Vec<Function> {
                &self.functions
            }

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

impl_function_trait_type!(Class);
impl_function_trait_type!(Interface);

trait ToStripperType {
    fn convert(&self) -> TypeStruct;
    fn doc(&self) -> Option<&str>;
    fn doc_deprecated(&self) -> Option<&str>;
}

macro_rules! impl_to_stripper_type {
    ($ty:ident, $enum_var:ident) => {
        impl ToStripperType for $ty {
            fn convert(&self) -> TypeStruct {
                TypeStruct::new(SType::$enum_var, &self.name)
            }

            fn doc(&self) -> Option<&str> {
                self.doc.as_ref().map(|s| &s[..])
            }

            fn doc_deprecated(&self) -> Option<&str> {
                self.doc_deprecated.as_ref().map(|s| &s[..])
            }
        }
    }
}

impl_to_stripper_type!(Alias, Struct);
impl_to_stripper_type!(Constant, Const);
impl_to_stripper_type!(Member, Variant);
impl_to_stripper_type!(Enumeration, Enum);
impl_to_stripper_type!(Bitfield, Type);
impl_to_stripper_type!(Record, Struct);
impl_to_stripper_type!(Field, Variant);
impl_to_stripper_type!(Union, Struct);
impl_to_stripper_type!(Function, Fn);
impl_to_stripper_type!(Interface, Trait);
impl_to_stripper_type!(Class, Struct);
impl_to_stripper_type!(Namespace, Mod);
impl_to_stripper_type!(Parameter, Variant);

pub fn generate(env: &Env) {
    let path =  env.config.target_path.join("docs.md");
    println!("Generating documentation {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_doc(w, &env));
}

fn generate_doc(mut w: &mut Write, env: &Env) -> Result<()> {
    try!(write_file_name(w, None));

    for (tid, type_) in env.library.namespace_types(MAIN) {
        let obj = env.config.objects.get(&tid.full_name(&env.library))
            .and_then(|obj| if obj.status.ignored() { None } else { Some(obj) });
        match *type_ {
            LType::Class(..) => {
                if let Some(obj) = obj {
                    try!(create_object_doc(w, env, &analysis::object::class(env, obj).unwrap()));
                }
            }
            LType::Interface(..) => {
                if let Some(obj) = obj {
                    try!(create_object_doc(w, env,
                                           &analysis::object::interface(env, obj).unwrap()));
                }
            }
            LType::Record(ref rec) => {
                if obj.is_some() {
                    let symbols = env.symbols.borrow();
                    try!(create_record_doc(w, rec, &symbols));
                }
            }
            _ => {
                let symbols = env.symbols.borrow();
                try!(handle_type(&mut w, type_, &symbols));
            }
        }
    }

    Ok(())
}

fn handle_type(w: &mut Write, ty: &LType, symbols: &symbols::Info) -> Result<()> {
    match *ty {
        LType::Alias(ref a) => create_sub_doc(w, a, symbols),
        LType::Enumeration(ref e) => create_enum_doc(w, &e, symbols),
        _ => Ok(()),
    }
}

fn create_object_doc(w: &mut Write, env: &Env, info: &analysis::object::Info) -> Result<()> {
    let symbols = env.symbols.borrow();
    let ty = TypeStruct::new(SType::Struct, &info.name);
    let ty_ext = TypeStruct::new(SType::Trait, &format!("{}Ext", info.name));
    let has_trait = info.has_children;
    let class: &ToStripperType;
    let functions: &[Function];

    match *env.library.type_(info.type_id) {
        Type::Class(ref cl) => {
            class = cl;
            functions = &cl.functions;
        }
        Type::Interface(ref iface) => {
            class = iface;
            functions = &iface.functions;
        }
        _ => unreachable!(),
    }

    if class.doc().is_some() || class.doc_deprecated().is_some() {
        try!(write_item_doc(w, &ty, |w| {
            if let Some(doc) = class.doc() {
                try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
            }

            try!(writeln!(w, "\n# Implements\n"));
            let impl_self = if has_trait { Some(info.type_id) } else { None };
            let implements = impl_self.iter()
                .chain(env.class_hierarchy.supertypes(info.type_id))
                .filter(|&tid| !env.type_status(&tid.full_name(&env.library)).ignored())
                .map(|&tid| format!("[`{name}Ext`](trait.{name}Ext.html)",
                                    name = env.library.type_(tid).get_name()))
                .collect::<Vec<_>>();
            try!(writeln!(w, "{}", &implements.join(", ")));

            if let Some(doc) = class.doc_deprecated() {
                try!(writeln!(w, "\n# Deprecated\n"));
                try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
            }
            Ok(())
        }));
    }

    if has_trait {
        try!(write_item_doc(w, &ty_ext, |w| {
            try!(writeln!(w, "Trait containing all `{}` methods.", ty.name));
            let mut implementors = Some(info.type_id).into_iter()
                .chain(env.class_hierarchy.subtypes(info.type_id))
                .filter(|&tid| !env.type_status(&tid.full_name(&env.library)).ignored())
                .map(|tid| format!("[`{name}`](struct.{name}.html)",
                                    name = env.library.type_(tid).get_name()))
                .collect::<Vec<_>>();
            implementors.sort();

            try!(writeln!(w, "\n# Implementors\n"));
            try!(writeln!(w, "{}", implementors.join(", ")));
            Ok(())
        }));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };

    for function in functions {
        let ty = if has_trait && function.parameters.iter().any(|p| p.instance_parameter) {
            ty_ext.clone()
        }
        else {
            ty.clone()
        };
        try!(create_fn_doc(w, &function, Some(Box::new(ty)), &symbols));
    }
    Ok(())
}

fn create_record_doc(w: &mut Write, record: &Record, symbols: &symbols::Info) -> Result<()> {
    let ty = record.convert();

    if record.doc().is_some() || record.doc_deprecated().is_some() {
        try!(write_item_doc(w, &ty, |w| {
            if let Some(doc) = record.doc() {
                try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
            }

            if let Some(doc) = record.doc_deprecated() {
                try!(writeln!(w, "\n# Deprecated\n"));
                try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
            }
            Ok(())
        }));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };
    for function in &record.functions {
        try!(create_fn_doc(w, &function, Some(Box::new(ty.clone())), &symbols));
    }
    Ok(())
}

fn create_enum_doc(w: &mut Write, enum_: &Enumeration, symbols: &symbols::Info) -> Result<()> {
    let ty = enum_.convert();

    try!(write_item_doc(w, &ty, |w| {
        if let Some(doc) = enum_.doc() {
            try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
        }
        if let Some(doc) = enum_.doc_deprecated() {
            try!(writeln!(w, "\n# Deprecated\n"));
            try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
        }
        Ok(())
    }));

    for member in enum_.members.iter() {
        let mut sub_ty = TypeStruct { name: member.name.to_camel(), ..member.convert()};

        if member.doc().is_some() || member.doc_deprecated().is_some() {
            sub_ty.parent = Some(Box::new(ty.clone()));
            try!(write_item_doc(w, &sub_ty, |w| {
                if let Some(doc) = member.doc() {
                    try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
                }
                if let Some(doc) = member.doc_deprecated() {
                    try!(writeln!(w, "\n# Deprecated\n"));
                    try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
                }
                Ok(())
            }));
        }
    }
    Ok(())
}

lazy_static! {
    static ref PARAM_NAME: Regex = Regex::new(r"@(\w+)\b").unwrap();
}

fn fix_param_names(doc: &str, self_name: &Option<String>) -> String {
    PARAM_NAME.replace_all(doc, |caps: &Captures| {
        if let Some(ref self_name) = *self_name {
            if &caps[1] == self_name {
                return "@self".into()
            }
        }
        format!("@{}", nameutil::mangle_keywords(&caps[1]))
    })
}

fn create_fn_doc(w: &mut Write, fn_: &Function, parent: Option<Box<TypeStruct>>,
                 symbols: &symbols::Info) -> Result<()> {
    if fn_.doc().is_none() && fn_.doc_deprecated().is_none() && fn_.ret.doc().is_none() {
        if fn_.parameters.iter().all(|x| {
            x.doc().is_none()
        }) {
            return Ok(());
        }
    }
    let mut sub_ty = fn_.convert();
    sub_ty.parent = parent;

    let self_name: Option<String> = fn_.parameters.iter()
        .find(|p| p.instance_parameter)
        .map(|p| p.name.clone());

    write_item_doc(w, &sub_ty, |w| {
        if let Some(doc) = fn_.doc() {
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), symbols)));
        };
        if let Some(doc) = fn_.doc_deprecated() {
            try!(writeln!(w, "\n# Deprecated\n"));
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), symbols)));
        };

        for parameter in fn_.parameters.iter() {
            if parameter.instance_parameter || parameter.name.is_empty() {
                continue
            }
            if let Some(doc) = parameter.doc() {
                try!(writeln!(w, "## `{}`", nameutil::mangle_keywords(&parameter.name[..])));
                try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), symbols)));
            }
        }

        if let Some(doc) = fn_.ret.doc() {
            try!(writeln!(w, "\n# Returns\n"));
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), symbols)));
        }
        Ok(())
    })
}

fn create_sub_doc<T: ToStripperType>(w: &mut Write, ty: &T, symbols: &symbols::Info) -> Result<()> {
    let sub_ty = ty.convert();

    if ty.doc().is_some() || ty.doc_deprecated().is_some() {
        try!(write_item_doc(w, &sub_ty, |w| {
            if let Some(doc) = ty.doc() {
                try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
            }
            if let Some(doc) = ty.doc_deprecated() {
                try!(writeln!(w, "\n# Deprecated\n"));
                try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
            }
            Ok(())
        }));
    }
    Ok(())
}
<commit_msg>Doc generator: avoid empty 'Implements' sections<commit_after>use std::io::{Result, Write};

use analysis;
use analysis::symbols;
use analysis::namespaces::MAIN;
use case::CaseExt;
use env::Env;
use file_saver::save_to_file;
use library::*;
use library::Type as LType;
use nameutil;
use regex::{Captures, Regex};
use self::format::reformat_doc;
use stripper_lib::Type as SType;
use stripper_lib::{TypeStruct, write_file_name, write_item_doc};

mod format;

trait FunctionTraitType {
    fn functions(&self) -> &Vec<Function>;
    fn name(&self) -> &str;
}

macro_rules! impl_function_trait_type {
    ($ty:ident) => {
        impl FunctionTraitType for $ty {
            fn functions(&self) -> &Vec<Function> {
                &self.functions
            }

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

impl_function_trait_type!(Class);
impl_function_trait_type!(Interface);

trait ToStripperType {
    fn convert(&self) -> TypeStruct;
    fn doc(&self) -> Option<&str>;
    fn doc_deprecated(&self) -> Option<&str>;
}

macro_rules! impl_to_stripper_type {
    ($ty:ident, $enum_var:ident) => {
        impl ToStripperType for $ty {
            fn convert(&self) -> TypeStruct {
                TypeStruct::new(SType::$enum_var, &self.name)
            }

            fn doc(&self) -> Option<&str> {
                self.doc.as_ref().map(|s| &s[..])
            }

            fn doc_deprecated(&self) -> Option<&str> {
                self.doc_deprecated.as_ref().map(|s| &s[..])
            }
        }
    }
}

impl_to_stripper_type!(Alias, Struct);
impl_to_stripper_type!(Constant, Const);
impl_to_stripper_type!(Member, Variant);
impl_to_stripper_type!(Enumeration, Enum);
impl_to_stripper_type!(Bitfield, Type);
impl_to_stripper_type!(Record, Struct);
impl_to_stripper_type!(Field, Variant);
impl_to_stripper_type!(Union, Struct);
impl_to_stripper_type!(Function, Fn);
impl_to_stripper_type!(Interface, Trait);
impl_to_stripper_type!(Class, Struct);
impl_to_stripper_type!(Namespace, Mod);
impl_to_stripper_type!(Parameter, Variant);

pub fn generate(env: &Env) {
    let path =  env.config.target_path.join("docs.md");
    println!("Generating documentation {:?}", path);
    save_to_file(&path, env.config.make_backup,
        |w| generate_doc(w, &env));
}

fn generate_doc(mut w: &mut Write, env: &Env) -> Result<()> {
    try!(write_file_name(w, None));

    for (tid, type_) in env.library.namespace_types(MAIN) {
        let obj = env.config.objects.get(&tid.full_name(&env.library))
            .and_then(|obj| if obj.status.ignored() { None } else { Some(obj) });
        match *type_ {
            LType::Class(..) => {
                if let Some(obj) = obj {
                    try!(create_object_doc(w, env, &analysis::object::class(env, obj).unwrap()));
                }
            }
            LType::Interface(..) => {
                if let Some(obj) = obj {
                    try!(create_object_doc(w, env,
                                           &analysis::object::interface(env, obj).unwrap()));
                }
            }
            LType::Record(ref rec) => {
                if obj.is_some() {
                    let symbols = env.symbols.borrow();
                    try!(create_record_doc(w, rec, &symbols));
                }
            }
            _ => {
                let symbols = env.symbols.borrow();
                try!(handle_type(&mut w, type_, &symbols));
            }
        }
    }

    Ok(())
}

fn handle_type(w: &mut Write, ty: &LType, symbols: &symbols::Info) -> Result<()> {
    match *ty {
        LType::Alias(ref a) => create_sub_doc(w, a, symbols),
        LType::Enumeration(ref e) => create_enum_doc(w, &e, symbols),
        _ => Ok(()),
    }
}

fn create_object_doc(w: &mut Write, env: &Env, info: &analysis::object::Info) -> Result<()> {
    let symbols = env.symbols.borrow();
    let ty = TypeStruct::new(SType::Struct, &info.name);
    let ty_ext = TypeStruct::new(SType::Trait, &format!("{}Ext", info.name));
    let has_trait = info.has_children;
    let class: &ToStripperType;
    let functions: &[Function];

    match *env.library.type_(info.type_id) {
        Type::Class(ref cl) => {
            class = cl;
            functions = &cl.functions;
        }
        Type::Interface(ref iface) => {
            class = iface;
            functions = &iface.functions;
        }
        _ => unreachable!(),
    }

    if class.doc().is_some() || class.doc_deprecated().is_some() {
        try!(write_item_doc(w, &ty, |w| {
            if let Some(doc) = class.doc() {
                try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
            }

            let impl_self = if has_trait { Some(info.type_id) } else { None };
            let implements = impl_self.iter()
                .chain(env.class_hierarchy.supertypes(info.type_id))
                .filter(|&tid| !env.type_status(&tid.full_name(&env.library)).ignored())
                .map(|&tid| format!("[`{name}Ext`](trait.{name}Ext.html)",
                                    name = env.library.type_(tid).get_name()))
                .collect::<Vec<_>>();
            if !implements.is_empty() {
                try!(writeln!(w, "\n# Implements\n"));
                try!(writeln!(w, "{}", &implements.join(", ")));
            }

            if let Some(doc) = class.doc_deprecated() {
                try!(writeln!(w, "\n# Deprecated\n"));
                try!(writeln!(w, "{}", reformat_doc(doc, &symbols)));
            }
            Ok(())
        }));
    }

    if has_trait {
        try!(write_item_doc(w, &ty_ext, |w| {
            try!(writeln!(w, "Trait containing all `{}` methods.", ty.name));
            let mut implementors = Some(info.type_id).into_iter()
                .chain(env.class_hierarchy.subtypes(info.type_id))
                .filter(|&tid| !env.type_status(&tid.full_name(&env.library)).ignored())
                .map(|tid| format!("[`{name}`](struct.{name}.html)",
                                    name = env.library.type_(tid).get_name()))
                .collect::<Vec<_>>();
            implementors.sort();

            try!(writeln!(w, "\n# Implementors\n"));
            try!(writeln!(w, "{}", implementors.join(", ")));
            Ok(())
        }));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };

    for function in functions {
        let ty = if has_trait && function.parameters.iter().any(|p| p.instance_parameter) {
            ty_ext.clone()
        }
        else {
            ty.clone()
        };
        try!(create_fn_doc(w, &function, Some(Box::new(ty)), &symbols));
    }
    Ok(())
}

fn create_record_doc(w: &mut Write, record: &Record, symbols: &symbols::Info) -> Result<()> {
    let ty = record.convert();

    if record.doc().is_some() || record.doc_deprecated().is_some() {
        try!(write_item_doc(w, &ty, |w| {
            if let Some(doc) = record.doc() {
                try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
            }

            if let Some(doc) = record.doc_deprecated() {
                try!(writeln!(w, "\n# Deprecated\n"));
                try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
            }
            Ok(())
        }));
    }

    let ty = TypeStruct { ty: SType::Impl, ..ty };
    for function in &record.functions {
        try!(create_fn_doc(w, &function, Some(Box::new(ty.clone())), &symbols));
    }
    Ok(())
}

fn create_enum_doc(w: &mut Write, enum_: &Enumeration, symbols: &symbols::Info) -> Result<()> {
    let ty = enum_.convert();

    try!(write_item_doc(w, &ty, |w| {
        if let Some(doc) = enum_.doc() {
            try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
        }
        if let Some(doc) = enum_.doc_deprecated() {
            try!(writeln!(w, "\n# Deprecated\n"));
            try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
        }
        Ok(())
    }));

    for member in enum_.members.iter() {
        let mut sub_ty = TypeStruct { name: member.name.to_camel(), ..member.convert()};

        if member.doc().is_some() || member.doc_deprecated().is_some() {
            sub_ty.parent = Some(Box::new(ty.clone()));
            try!(write_item_doc(w, &sub_ty, |w| {
                if let Some(doc) = member.doc() {
                    try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
                }
                if let Some(doc) = member.doc_deprecated() {
                    try!(writeln!(w, "\n# Deprecated\n"));
                    try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
                }
                Ok(())
            }));
        }
    }
    Ok(())
}

lazy_static! {
    static ref PARAM_NAME: Regex = Regex::new(r"@(\w+)\b").unwrap();
}

fn fix_param_names(doc: &str, self_name: &Option<String>) -> String {
    PARAM_NAME.replace_all(doc, |caps: &Captures| {
        if let Some(ref self_name) = *self_name {
            if &caps[1] == self_name {
                return "@self".into()
            }
        }
        format!("@{}", nameutil::mangle_keywords(&caps[1]))
    })
}

fn create_fn_doc(w: &mut Write, fn_: &Function, parent: Option<Box<TypeStruct>>,
                 symbols: &symbols::Info) -> Result<()> {
    if fn_.doc().is_none() && fn_.doc_deprecated().is_none() && fn_.ret.doc().is_none() {
        if fn_.parameters.iter().all(|x| {
            x.doc().is_none()
        }) {
            return Ok(());
        }
    }
    let mut sub_ty = fn_.convert();
    sub_ty.parent = parent;

    let self_name: Option<String> = fn_.parameters.iter()
        .find(|p| p.instance_parameter)
        .map(|p| p.name.clone());

    write_item_doc(w, &sub_ty, |w| {
        if let Some(doc) = fn_.doc() {
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), symbols)));
        };
        if let Some(doc) = fn_.doc_deprecated() {
            try!(writeln!(w, "\n# Deprecated\n"));
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), symbols)));
        };

        for parameter in fn_.parameters.iter() {
            if parameter.instance_parameter || parameter.name.is_empty() {
                continue
            }
            if let Some(doc) = parameter.doc() {
                try!(writeln!(w, "## `{}`", nameutil::mangle_keywords(&parameter.name[..])));
                try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), symbols)));
            }
        }

        if let Some(doc) = fn_.ret.doc() {
            try!(writeln!(w, "\n# Returns\n"));
            try!(writeln!(w, "{}", reformat_doc(&fix_param_names(doc, &self_name), symbols)));
        }
        Ok(())
    })
}

fn create_sub_doc<T: ToStripperType>(w: &mut Write, ty: &T, symbols: &symbols::Info) -> Result<()> {
    let sub_ty = ty.convert();

    if ty.doc().is_some() || ty.doc_deprecated().is_some() {
        try!(write_item_doc(w, &sub_ty, |w| {
            if let Some(doc) = ty.doc() {
                try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
            }
            if let Some(doc) = ty.doc_deprecated() {
                try!(writeln!(w, "\n# Deprecated\n"));
                try!(writeln!(w, "{}", reformat_doc(doc, symbols)));
            }
            Ok(())
        }));
    }
    Ok(())
}
<|endoftext|>


<commit_before>use super::{V2_COOKIE, V2_HEADER_SIZE};
use super::super::{Counter, Histogram};
use std::io::Write;
use std;
use super::byteorder::{BigEndian, WriteBytesExt};

/// Errors that occur during serialization.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SerializeError {
    /// A count above i64::max_value() cannot be zig-zag encoded, and therefore cannot be
    /// serialized.
    CountNotSerializable,
    /// Internal calculations cannot be represented in `usize`. Use smaller histograms or beefier
    /// hardware.
    UsizeTypeTooSmall,
    /// An i/o operation failed.
    IoError
}

impl std::convert::From<std::io::Error> for SerializeError {
    fn from(_: std::io::Error) -> Self {
        SerializeError::IoError
    }
}

/// Serializer for the V2 binary format.
/// Serializers are intended to be re-used for many histograms.
pub struct V2Serializer {
    buf: Vec<u8>
}

impl V2Serializer {
    /// Create a new serializer.
    pub fn new() -> V2Serializer {
        V2Serializer {
            buf: Vec::new()
        }
    }

    /// Serialize the histogram.
    /// Returns the number of bytes written, or an io error.
    pub fn serialize<T: Counter, W: Write>(&mut self, h: &Histogram<T>, writer: &mut W)
                                           -> Result<usize, SerializeError> {
        self.buf.clear();
        let max_size = max_encoded_size(h).ok_or(SerializeError::UsizeTypeTooSmall)?;
        self.buf.reserve(max_size);

        self.buf.write_u32::<BigEndian>(V2_COOKIE)?;
        // placeholder for length
        self.buf.write_u32::<BigEndian>(0)?;
        // normalizing index offset
        self.buf.write_u32::<BigEndian>(0)?;
        self.buf.write_u32::<BigEndian>(h.significant_value_digits as u32)?;
        self.buf.write_u64::<BigEndian>(h.lowest_discernible_value)?;
        self.buf.write_u64::<BigEndian>(h.highest_trackable_value)?;
        // int to double conversion
        self.buf.write_f64::<BigEndian>(1.0)?;

        debug_assert_eq!(V2_HEADER_SIZE, self.buf.len());

        unsafe {
            // want to treat the rest of the vec as a slice, and we've already reserved this
            // space, so this way we don't have to resize() on a lot of dummy bytes.
            self.buf.set_len(max_size);
        }

        let counts_len = encode_counts(h, &mut self.buf[V2_HEADER_SIZE..])?;
        let total_len = V2_HEADER_SIZE + counts_len;

        // TODO benchmark fastest buffer management scheme
        // counts is always under 2^24
        (&mut self.buf[4..8]).write_u32::<BigEndian>(counts_len as u32)?;

        writer.write_all(&mut self.buf[0..(total_len)])
            .map(|_| total_len)
            .map_err(|_| SerializeError::IoError)
    }
}

fn max_encoded_size<T: Counter>(h: &Histogram<T>) -> Option<usize> {
    counts_array_max_encoded_size(h.index_for(h.max()) + 1)
        .and_then(|x| x.checked_add(V2_HEADER_SIZE))
}

pub fn counts_array_max_encoded_size(length: usize) -> Option<usize> {
    // LEB128-64b9B uses at most 9 bytes
    // Won't overflow (except sometimes on 16 bit systems) because largest possible counts
    // len is 47 buckets, each with 2^17 half count, for a total of 6e6. This product will
    // therefore be about 5e7 (50 million) at most.
    length.checked_mul(9)
}

/// Encode counts array into slice.
/// The slice must be at least 9 * the number of counts that will be encoded.
pub fn encode_counts<T: Counter>(h: &Histogram<T>, buf: &mut [u8]) -> Result<usize, SerializeError> {
    let index_limit = h.index_for(h.max()) + 1;
    let mut index = 0;
    let mut bytes_written = 0;

    assert!(index_limit <= h.counts.len());

    while index < index_limit {
        // index is inside h.counts because of the assert above
        let count = unsafe { *(h.counts.get_unchecked(index)) };
        index += 1;

        // Non-negative values are positive counts or 1 zero, negative values are repeated zeros.

        let mut zero_count = 0;
        if count == T::zero() {
            zero_count = 1;

            // index is inside h.counts because of the assert above
            while (index < index_limit) && (unsafe { *(h.counts.get_unchecked(index)) } == T::zero()) {
                zero_count += 1;
                index += 1;
            }
        }

        let count_or_zeros: i64 = if zero_count > 1 {
            // zero count can be at most the entire counts array, which is at most 2^24, so will fit.
            -zero_count
        } else {
            count.to_i64().ok_or(SerializeError::CountNotSerializable)?
        };

        let zz = zig_zag_encode(count_or_zeros);

        bytes_written += varint_write(zz, &mut buf[bytes_written..]);
    }

    Ok(bytes_written)
}

/// Write a number as a LEB128-64b9B little endian base 128 varint to buf. This is not
/// quite the same as Protobuf's LEB128 as it encodes 64 bit values in a max of 9 bytes, not 10.
/// The first 8 7-bit chunks are encoded normally (up through the first 7 bytes of input). The last
/// byte is added to the buf as-is. This limits the input to 8 bytes, but that's all we need.
/// Returns the number of bytes written (in [1, 9]).
#[inline]
pub fn varint_write(input: u64, buf: &mut [u8]) -> usize {
    // The loop is unrolled because the special case is awkward to express in a loop, and it
    // probably makes the branch predictor happier to do it this way.
    // This way about twice as fast as the other "obvious" approach: a sequence of `if`s to detect
    // size directly with each branch encoding that number completely and returning.

    // TODO try bitwise and instead of shift
    if (input >> 7) == 0 {
        buf[0] = input as u8;
        return 1;
    } else {
        // set high bit because more bytes are coming, then next 7 bits of value.
        buf[0] = 0x80 | ((input & 0x7F) as u8);
        if (input >> 7 * 2) == 0 {
            // nothing above bottom 2 chunks, this is the last byte, so no high bit
            buf[1] = nth_7b_chunk(input, 1);
            return 2;
        } else {
            buf[1] = nth_7b_chunk_with_high_bit(input, 1);
            if (input >> 7 * 3) == 0 {
                buf[2] = nth_7b_chunk(input, 2);
                return 3;
            } else {
                buf[2] = nth_7b_chunk_with_high_bit(input, 2);
                if (input >> 7 * 4) == 0 {
                    buf[3] = nth_7b_chunk(input, 3);
                    return 4;
                } else {
                    buf[3] = nth_7b_chunk_with_high_bit(input, 3);
                    if (input >> 7 * 5) == 0 {
                        buf[4] = nth_7b_chunk(input, 4);
                        return 5;
                    } else {
                        buf[4] = nth_7b_chunk_with_high_bit(input, 4);
                        if (input >> 7 * 6) == 0 {
                            buf[5] = nth_7b_chunk(input, 5);
                            return 6;
                        } else {
                            buf[5] = nth_7b_chunk_with_high_bit(input, 5);
                            if (input >> 7 * 7) == 0 {
                                buf[6] = nth_7b_chunk(input, 6);
                                return 7;
                            } else {
                                buf[6] = nth_7b_chunk_with_high_bit(input, 6);
                                if (input >> 7 * 8) == 0 {
                                    buf[7] = nth_7b_chunk(input, 7);
                                    return 8;
                                } else {
                                    buf[7] = nth_7b_chunk_with_high_bit(input, 7);
                                    // special case: write last whole byte as is
                                    buf[8] = (input >> 56) as u8;
                                    return 9;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

/// input: a u64
/// n: >0, how many 7-bit shifts to do
/// Returns the n'th chunk (starting from least significant) of 7 bits as a byte with the the high
/// bit unchanged.
#[inline]
fn nth_7b_chunk(input: u64, n: u8) -> u8 {
    (input >> 7 * n) as u8
}

/// input: a u64
/// n: >0, how many 7-bit shifts to do
/// Returns the n'th chunk (starting from least significant) of 7 bits as a byte.
/// The high bit in the byte will be set (not one of the 7 bits that map to input bits).
#[inline]
fn nth_7b_chunk_with_high_bit(input: u64, n: u8) -> u8 {
    nth_7b_chunk(input, n) | 0x80
}

/// Map signed numbers to unsigned: 0 to 0, -1 to 1, 1 to 2, -2 to 3, etc
#[inline]
pub fn zig_zag_encode(num: i64) -> u64 {
    // If num < 0, num >> 63 is all 1 and vice versa.
    ((num << 1) ^ (num >> 63)) as u64
}
<commit_msg>Use helpers more consistently in varint_write<commit_after>use super::{V2_COOKIE, V2_HEADER_SIZE};
use super::super::{Counter, Histogram};
use std::io::Write;
use std;
use super::byteorder::{BigEndian, WriteBytesExt};

/// Errors that occur during serialization.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SerializeError {
    /// A count above i64::max_value() cannot be zig-zag encoded, and therefore cannot be
    /// serialized.
    CountNotSerializable,
    /// Internal calculations cannot be represented in `usize`. Use smaller histograms or beefier
    /// hardware.
    UsizeTypeTooSmall,
    /// An i/o operation failed.
    IoError
}

impl std::convert::From<std::io::Error> for SerializeError {
    fn from(_: std::io::Error) -> Self {
        SerializeError::IoError
    }
}

/// Serializer for the V2 binary format.
/// Serializers are intended to be re-used for many histograms.
pub struct V2Serializer {
    buf: Vec<u8>
}

impl V2Serializer {
    /// Create a new serializer.
    pub fn new() -> V2Serializer {
        V2Serializer {
            buf: Vec::new()
        }
    }

    /// Serialize the histogram.
    /// Returns the number of bytes written, or an io error.
    pub fn serialize<T: Counter, W: Write>(&mut self, h: &Histogram<T>, writer: &mut W)
                                           -> Result<usize, SerializeError> {
        self.buf.clear();
        let max_size = max_encoded_size(h).ok_or(SerializeError::UsizeTypeTooSmall)?;
        self.buf.reserve(max_size);

        self.buf.write_u32::<BigEndian>(V2_COOKIE)?;
        // placeholder for length
        self.buf.write_u32::<BigEndian>(0)?;
        // normalizing index offset
        self.buf.write_u32::<BigEndian>(0)?;
        self.buf.write_u32::<BigEndian>(h.significant_value_digits as u32)?;
        self.buf.write_u64::<BigEndian>(h.lowest_discernible_value)?;
        self.buf.write_u64::<BigEndian>(h.highest_trackable_value)?;
        // int to double conversion
        self.buf.write_f64::<BigEndian>(1.0)?;

        debug_assert_eq!(V2_HEADER_SIZE, self.buf.len());

        unsafe {
            // want to treat the rest of the vec as a slice, and we've already reserved this
            // space, so this way we don't have to resize() on a lot of dummy bytes.
            self.buf.set_len(max_size);
        }

        let counts_len = encode_counts(h, &mut self.buf[V2_HEADER_SIZE..])?;
        let total_len = V2_HEADER_SIZE + counts_len;

        // TODO benchmark fastest buffer management scheme
        // counts is always under 2^24
        (&mut self.buf[4..8]).write_u32::<BigEndian>(counts_len as u32)?;

        writer.write_all(&mut self.buf[0..(total_len)])
            .map(|_| total_len)
            .map_err(|_| SerializeError::IoError)
    }
}

fn max_encoded_size<T: Counter>(h: &Histogram<T>) -> Option<usize> {
    counts_array_max_encoded_size(h.index_for(h.max()) + 1)
        .and_then(|x| x.checked_add(V2_HEADER_SIZE))
}

pub fn counts_array_max_encoded_size(length: usize) -> Option<usize> {
    // LEB128-64b9B uses at most 9 bytes
    // Won't overflow (except sometimes on 16 bit systems) because largest possible counts
    // len is 47 buckets, each with 2^17 half count, for a total of 6e6. This product will
    // therefore be about 5e7 (50 million) at most.
    length.checked_mul(9)
}

/// Encode counts array into slice.
/// The slice must be at least 9 * the number of counts that will be encoded.
pub fn encode_counts<T: Counter>(h: &Histogram<T>, buf: &mut [u8]) -> Result<usize, SerializeError> {
    let index_limit = h.index_for(h.max()) + 1;
    let mut index = 0;
    let mut bytes_written = 0;

    assert!(index_limit <= h.counts.len());

    while index < index_limit {
        // index is inside h.counts because of the assert above
        let count = unsafe { *(h.counts.get_unchecked(index)) };
        index += 1;

        // Non-negative values are positive counts or 1 zero, negative values are repeated zeros.

        let mut zero_count = 0;
        if count == T::zero() {
            zero_count = 1;

            // index is inside h.counts because of the assert above
            while (index < index_limit) && (unsafe { *(h.counts.get_unchecked(index)) } == T::zero()) {
                zero_count += 1;
                index += 1;
            }
        }

        let count_or_zeros: i64 = if zero_count > 1 {
            // zero count can be at most the entire counts array, which is at most 2^24, so will fit.
            -zero_count
        } else {
            count.to_i64().ok_or(SerializeError::CountNotSerializable)?
        };

        let zz = zig_zag_encode(count_or_zeros);

        bytes_written += varint_write(zz, &mut buf[bytes_written..]);
    }

    Ok(bytes_written)
}

/// Write a number as a LEB128-64b9B little endian base 128 varint to buf. This is not
/// quite the same as Protobuf's LEB128 as it encodes 64 bit values in a max of 9 bytes, not 10.
/// The first 8 7-bit chunks are encoded normally (up through the first 7 bytes of input). The last
/// byte is added to the buf as-is. This limits the input to 8 bytes, but that's all we need.
/// Returns the number of bytes written (in [1, 9]).
#[inline]
pub fn varint_write(input: u64, buf: &mut [u8]) -> usize {
    // The loop is unrolled because the special case is awkward to express in a loop, and it
    // probably makes the branch predictor happier to do it this way.
    // This way about twice as fast as the other "obvious" approach: a sequence of `if`s to detect
    // size directly with each branch encoding that number completely and returning.

    if (input >> 7) == 0 {
        buf[0] = input as u8;
        return 1;
    } else {
        // set high bit because more bytes are coming, then next 7 bits of value.
        buf[0] = 0x80 | ((input & 0x7F) as u8);
        if shift_by_7s(input, 2) == 0 {
            // All zero above bottom 2 chunks, this is the last byte, so no high bit
            buf[1] = shift_by_7s(input, 1) as u8;
            return 2;
        } else {
            buf[1] = nth_7b_chunk_with_high_bit(input, 1);
            if shift_by_7s(input, 3) == 0 {
                buf[2] = shift_by_7s(input, 2) as u8;
                return 3;
            } else {
                buf[2] = nth_7b_chunk_with_high_bit(input, 2);
                if shift_by_7s(input, 4) == 0 {
                    buf[3] = shift_by_7s(input, 3) as u8;
                    return 4;
                } else {
                    buf[3] = nth_7b_chunk_with_high_bit(input, 3);
                    if shift_by_7s(input, 5) == 0 {
                        buf[4] = shift_by_7s(input, 4) as u8;
                        return 5;
                    } else {
                        buf[4] = nth_7b_chunk_with_high_bit(input, 4);
                        if shift_by_7s(input, 6) == 0 {
                            buf[5] = shift_by_7s(input, 5) as u8;
                            return 6;
                        } else {
                            buf[5] = nth_7b_chunk_with_high_bit(input, 5);
                            if shift_by_7s(input, 7) == 0 {
                                buf[6] = shift_by_7s(input, 6) as u8;
                                return 7;
                            } else {
                                buf[6] = nth_7b_chunk_with_high_bit(input, 6);
                                if shift_by_7s(input, 8) == 0 {
                                    buf[7] = shift_by_7s(input, 7) as u8;
                                    return 8;
                                } else {
                                    buf[7] = nth_7b_chunk_with_high_bit(input, 7);
                                    // special case: write last whole byte as is
                                    buf[8] = (input >> 56) as u8;
                                    return 9;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

/// input: a u64
/// n: >0, how many 7-bit shifts to do
/// Returns the input shifted over by `n` groups of 7 bits.
#[inline]
fn shift_by_7s(input: u64, n: u8) -> u64 {
    input >> (7 * n)
}

/// input: a u64
/// n: >0, how many 7-bit shifts to do
/// Returns the n'th chunk (starting from least significant) of 7 bits as a byte.
/// The high bit in the byte will be set (not one of the 7 bits that map to input bits).
#[inline]
fn nth_7b_chunk_with_high_bit(input: u64, n: u8) -> u8 {
    (shift_by_7s(input, n) as u8) | 0x80
}

/// Map signed numbers to unsigned: 0 to 0, -1 to 1, 1 to 2, -2 to 3, etc
#[inline]
pub fn zig_zag_encode(num: i64) -> u64 {
    // If num < 0, num >> 63 is all 1 and vice versa.
    ((num << 1) ^ (num >> 63)) as u64
}
<|endoftext|>


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

use vobject::parse_component;

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

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

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

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

    let mut outf = io::File::create(outfile);
    let entries = try!(io::fs::readdir(dir));
    for entry in entries.iter() {
        if !entry.is_file() {
            continue;
        }

        print!("Processing {}\n", entry.display());

        let itemstr = try!(io::File::open(entry).read_to_string());
        let item = match parse_component(&itemstr) {
            Ok(item) => item,
            Err(e) => {
                println!("Error: Failed to parse item {}: {}\n", entry.display(), e);
                os::set_exit_status(1);
                continue;
            }
        };

        let name = match item.single_prop("FN") {
            Some(name) => name.get_raw_value(),
            None => {
                print!("Warning: No name in {}, skipping.\n", entry.display());
                continue;
            }
        };

        let emails = item.all_props("EMAIL");
        for email in emails.iter() {
            try!(outf.write_str(
                format!("{}\t{}\t{}\n", email.get_raw_value(), name, entry.display()).as_slice()
            ))
        };
    };
    return Ok(());
}

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

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

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

    let env = get_env();

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

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

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

    } else if matches.opt_present("mutt-query") {
        // FIXME: Better way to write this? We already checked for presence of mutt-search before
        let query = matches.opt_str("mutt-query").expect("This should never happen and yet it did.");
        main_try!(mutt_query(env, query), "Failed to execute grep");
    } else {
        print_usage();
    };
}

fn mutt_query<'a>(env: HashMap<String, String>, query: String) -> io::IoResult<()> {
    println!("");  // For some reason mutt requires an empty line
    for item in try!(index_query(env, query)) {
        if(item.email.len() > 0 && item.name.len() > 0 && item.filepath.len() > 0) {
            println!("{}\t{}\t{}", item.email, item.name, item.filepath);
        };
    };
    Ok(())
}

fn index_query<'a>(env: HashMap<String, String>, query: String) -> io::IoResult<IndexIterator<'a>> {
    let index_file = expect_env(&env, "MATES_INDEX");
    let default_grep = "grep".to_owned();
    let grep_cmd = match env.get("MATES_GREP") {
        Some(x) => x,
        None => &default_grep
    };

    let mut process = try!(io::Command::new(grep_cmd.as_slice())
                           .arg(query.as_slice())
                           .arg(index_file.as_slice())
                           .stderr(io::process::InheritFd(2))
                           .spawn());

    let stream = match process.stdout.as_mut() {
        Some(x) => x,
        None => return Err(io::IoError {
            kind: io::IoUnavailable,
            desc: "Failed to get stdout from grep process.",
            detail: None
        })
    };

    let output = try!(stream.read_to_string());
    Ok(IndexIterator::new(&output))
}

struct IndexItem<'a> {
    pub email: String,
    pub name: String,
    pub filepath: String
}

impl<'a> IndexItem<'a> {
    fn new(line: String) -> IndexItem<'a> {
        let mut parts = line.split('\t');

        IndexItem {
            email: parts.next().unwrap_or("").to_string(),
            name: parts.next().unwrap_or("").to_string(),
            filepath: parts.next().unwrap_or("").to_string()
        }
    }
}

struct IndexIterator<'a> {
    linebuffer: Vec<String>
}

impl<'a> IndexIterator<'a> {
    fn new(output: &String) -> IndexIterator<'a> {

        let rv = output.split('\n').map(|x: &str| x.to_string()).collect();
        IndexIterator {
            linebuffer: rv
        }
    }
}

impl<'a> Iterator for IndexIterator<'a> {
    type Item = IndexItem<'a>;

    fn next(&mut self) -> Option<IndexItem<'a>> {
        match self.linebuffer.pop() {
            Some(x) => Some(IndexItem::new(x)),
            None => None
        }
    }
}
<commit_msg>Add file-query and email-query<commit_after>use getopts::{optflag,optopt,getopts,usage};
use std::os;
use std::collections::HashMap;
use std::io;
use std::io::fs::PathExtensions;
use std::borrow::ToOwned;
use std::str;

use vobject::parse_component;

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

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

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

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

    let mut outf = io::File::create(outfile);
    let entries = try!(io::fs::readdir(dir));
    for entry in entries.iter() {
        if !entry.is_file() {
            continue;
        }

        print!("Processing {}\n", entry.display());

        let itemstr = try!(io::File::open(entry).read_to_string());
        let item = match parse_component(&itemstr) {
            Ok(item) => item,
            Err(e) => {
                println!("Error: Failed to parse item {}: {}\n", entry.display(), e);
                os::set_exit_status(1);
                continue;
            }
        };

        let name = match item.single_prop("FN") {
            Some(name) => name.get_raw_value(),
            None => {
                print!("Warning: No name in {}, skipping.\n", entry.display());
                continue;
            }
        };

        let emails = item.all_props("EMAIL");
        for email in emails.iter() {
            try!(outf.write_str(
                format!("{}\t{}\t{}\n", email.get_raw_value(), name, entry.display()).as_slice()
            ))
        };
    };
    return Ok(());
}

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

    let program = args[0].as_slice();
    let opts = [
        optflag("i", "index", "Rewrite/create index."),
        optflag("h", "help", "Print help."),
        optopt("", "mutt-query", "Search for contact, for mutt's query_command.", ""),
        optopt("", "file-query", "Search for contact, return filepaths.", ""),
        optopt("", "email-query", "Search for contact, return \"name <email>\".", "")
    ];

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

    let env = get_env();

    let print_usage = |&:| {
        println!("{}", usage(program, &opts));
    };

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

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

    } else if matches.opt_present("mutt-query") {
        // FIXME: Better way to write this? We already checked for presence of mutt-search before
        let query = matches.opt_str("mutt-query").expect("This should never happen and yet it did.");
        main_try!(mutt_query(env, query), "Failed to execute grep");
    } else if matches.opt_present("file-query") {
        // FIXME: Better way to write this? We already checked for presence of mutt-search before
        let query = matches.opt_str("file-query").expect("This should never happen and yet it did.");
        main_try!(file_query(env, query), "Failed to execute grep");
    } else if matches.opt_present("email-query") {
        // FIXME: Better way to write this? We already checked for presence of mutt-search before
        let query = matches.opt_str("email-query").expect("This should never happen and yet it did.");
        main_try!(email_query(env, query), "Failed to execute grep");
    } else {
        print_usage();
    };
}

fn mutt_query<'a>(env: HashMap<String, String>, query: String) -> io::IoResult<()> {
    println!("");  // For some reason mutt requires an empty line
    for item in try!(index_query(env, query)) {
        if(item.email.len() > 0 && item.name.len() > 0) {
            println!("{}\t{}\t{}", item.email, item.name, item.filepath);
        };
    };
    Ok(())
}

fn file_query<'a>(env: HashMap<String, String>, query: String) -> io::IoResult<()> {
    for item in try!(index_query(env, query)) {
        if(item.filepath.len() > 0) {
            println!("{}", item.filepath)
        };
    };
    Ok(())
}

fn email_query<'a>(env: HashMap<String, String>, query: String) -> io::IoResult<()> {
    for item in try!(index_query(env, query)) {
        if(item.name.len() > 0 && item.email.len() > 0) {
            println!("{} <{}>", item.name, item.email)
        };
    };
    Ok(())
}

fn index_query<'a>(env: HashMap<String, String>, query: String) -> io::IoResult<IndexIterator<'a>> {
    let index_file = expect_env(&env, "MATES_INDEX");
    let default_grep = "grep".to_owned();
    let grep_cmd = match env.get("MATES_GREP") {
        Some(x) => x,
        None => &default_grep
    };

    let mut process = try!(io::Command::new(grep_cmd.as_slice())
                           .arg(query.as_slice())
                           .arg(index_file.as_slice())
                           .stderr(io::process::InheritFd(2))
                           .spawn());

    let stream = match process.stdout.as_mut() {
        Some(x) => x,
        None => return Err(io::IoError {
            kind: io::IoUnavailable,
            desc: "Failed to get stdout from grep process.",
            detail: None
        })
    };

    let output = try!(stream.read_to_string());
    Ok(IndexIterator::new(&output))
}

struct IndexItem<'a> {
    pub email: String,
    pub name: String,
    pub filepath: String
}

impl<'a> IndexItem<'a> {
    fn new(line: String) -> IndexItem<'a> {
        let mut parts = line.split('\t');

        IndexItem {
            email: parts.next().unwrap_or("").to_string(),
            name: parts.next().unwrap_or("").to_string(),
            filepath: parts.next().unwrap_or("").to_string()
        }
    }
}

struct IndexIterator<'a> {
    linebuffer: Vec<String>
}

impl<'a> IndexIterator<'a> {
    fn new(output: &String) -> IndexIterator<'a> {

        let rv = output.split('\n').map(|x: &str| x.to_string()).collect();
        IndexIterator {
            linebuffer: rv
        }
    }
}

impl<'a> Iterator for IndexIterator<'a> {
    type Item = IndexItem<'a>;

    fn next(&mut self) -> Option<IndexItem<'a>> {
        match self.linebuffer.pop() {
            Some(x) => Some(IndexItem::new(x)),
            None => None
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::path::Path;

use lazy_static::lazy_static;
use raw_cpuid::CpuId;

lazy_static! {
    static ref CPUID: CpuId = CpuId::new();
}

fn has_aesni() -> bool {
    cfg!(feature = "native")
        && CPUID
            .get_feature_info()
            .map_or(false, |features| features.has_aesni())
}

fn has_sse41() -> bool {
    cfg!(feature = "native")
        && CPUID
            .get_feature_info()
            .map_or(false, |features| features.has_sse41())
}

fn has_sse42() -> bool {
    cfg!(feature = "native")
        && CPUID
            .get_feature_info()
            .map_or(false, |features| features.has_sse42())
}

fn has_avx() -> bool {
    cfg!(feature = "native")
        && CPUID
            .get_feature_info()
            .map_or(false, |features| features.has_avx())
}

fn has_avx2() -> bool {
    cfg!(feature = "native")
        && CPUID
            .get_extended_feature_info()
            .map_or(false, |features| features.has_avx2())
}

fn support_aesni() -> bool {
    cfg!(any(feature = "aes", target_feature = "aes")) || has_aesni()
}

fn support_sse41() -> bool {
    cfg!(any(feature = "sse41", target_feature = "sse41")) || has_sse41()
}

fn support_sse42() -> bool {
    cfg!(any(feature = "sse42", target_feature = "sse42")) || has_sse42()
}

fn support_avx() -> bool {
    cfg!(any(feature = "avx", target_feature = "avx")) || has_avx()
}

fn support_avx2() -> bool {
    cfg!(any(feature = "avx2", target_feature = "avx2")) || has_avx2()
}

cfg_if::cfg_if! {
    if #[cfg(all(not(feature = "gen"), any(target_os = "macos", target_os = "linux")))] {
        fn generate_binding(_out_file: &Path) {
            cargo_emit::warning!("pregenerated binding file.");
        }
    } else {
        fn generate_binding(out_file: &Path) {
            cargo_emit::warning!("generate binding file @ {:?}.", out_file);

            let _ = bindgen::builder()
                .clang_args(&["-x", "c++", "-std=c++11"])
                .clang_args(&[
                    "-Dt1ha_EXPORTS",
                    "-DXXH_STATIC_LINKING_ONLY",
                    "-Isrc/highwayhash",
                ])
                .clang_args(if support_aesni() {
                    &[
                        "-maes",
                        "-DT1HA0_RUNTIME_SELECT=1",
                        "-DT1HA0_AESNI_AVAILABLE=1",
                    ][..]
                } else {
                    &[][..]
                })
                .clang_args(
                    vec![
                        if cfg!(feature = "native") {
                            Some("-march=native")
                        } else {
                            None
                        },
                        if support_sse42() {
                            Some("-msse4.2")
                        } else {
                            None
                        },
                        if support_avx() { Some("-mavx") } else { None },
                        if support_avx2() { Some("-mavx2") } else { None },
                        if cfg!(feature = "city") {
                            Some("-DCITY_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "farm") {
                            Some("-DFARM_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "highway") {
                            Some("-DHIGHWAY_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "komi") {
                            Some("-DKOMI_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "lookup3") {
                            Some("-DLOOKUP3=1")
                        } else {
                            None
                        },
                        if cfg!(all(feature = "meow", any(target_arch = "x86", target_arch = "x86_64"))) {
                            Some("-DMEOW_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "metro") {
                            Some("-DMETRO_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "mum") {
                            Some("-DMUM_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "murmur") {
                            Some("-DMURMUR_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "mx3") {
                            Some("-DMX3_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "nm") {
                            Some("-DNM_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "pengy") {
                            Some("-DPENGY_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "prv") {
                            Some("-DPRV_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "spooky") {
                            Some("-DSPOOKY_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "t1ha") {
                            Some("-DT1_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "wy") {
                            Some("-DWY_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "xx") {
                            Some("-DXX_HASH=1")
                        } else {
                            None
                        },
                    ]
                    .into_iter()
                    .flatten(),
                )
                .header("src/fasthash.hpp")
                .size_t_is_usize(true)
                .generate_inline_functions(true)
                .disable_name_namespacing()
                .allowlist_function("^CityHash.*")
                .allowlist_function("^farmhash.*")
                .allowlist_function("^HighwayHash.*")
                .allowlist_function("^komi.*")
                .allowlist_function("^lookup3.*")
                .allowlist_function("^metrohash.*")
                .allowlist_function("^mum_hash.*")
                .allowlist_function("^MurmurHash.*")
                .allowlist_function("^mx3hash.*")
                .allowlist_function("^NMHASH.*")
                .allowlist_function("^pengy.*")
                .allowlist_function("^prvhash.*")
                .allowlist_function("^SpookyHasher.*")
                .allowlist_function("^t1ha.*")
                .allowlist_function("^wyhash.*")
                .allowlist_function("^XXH.*")
                .allowlist_function("^Meow.*")
                .blocklist_function("^t1ha_selfcheck__.*")
                .allowlist_var("^Meow.*")
                .allowlist_var("^PRH64S_.*")
                .generate()
                .unwrap()
                .write_to_file(out_file)
                .expect("fail to write bindings");

            cargo_emit::warning!("generate binding file @ {:?}.", out_file);
        }
    }
}

fn build_fasthash() {
    let mut build = cc::Build::new();

    build
        .cpp(true)
        .include("src/highwayhash")
        .flag("-std=c++11")
        .flag_if_supported("-Wno-implicit-fallthrough")
        .flag_if_supported("-Wno-unknown-attributes")
        .flag_if_supported("-Wno-sign-compare")
        .file("src/fasthash.cpp");

    if cfg!(feature = "city") {
        build.flag("-DCITY_HASH=1").file("src/smhasher/City.cpp");
    }

    if cfg!(feature = "farm") {
        build
            .flag("-DFARM_HASH=1")
            .file("src/smhasher/farmhash-c.c");
    }

    if cfg!(feature = "komi") {
        build.flag("-DKOMI_HASH=1");
    }

    if cfg!(feature = "highway") {
        build.flag("-DHIGHWAY_HASH=1");
    }

    if cfg!(feature = "lookup3") {
        build.flag("-DLOOKUP3=1").file("src/smhasher/lookup3.cpp");
    }

    if cfg!(all(
        feature = "meow",
        any(target_arch = "x86", target_arch = "x86_64")
    )) {
        build.flag("-DMEOW_HASH=1");
    }

    if cfg!(feature = "mx3") {
        build.flag("-DMX3_HASH=1");
    }

    if cfg!(feature = "nm") {
        build.flag("-DNM_HASH=1");
    }

    if cfg!(feature = "pengy") {
        build
            .flag("-DPENGY_HASH=1")
            .file("src/pengyhash/pengyhash.c");
    }

    if cfg!(feature = "prv") {
        build.flag("-DPRV_HASH=1");
    }

    if cfg!(feature = "t1ha") {
        build.flag("-DT1_HASH=1");
    }

    if cfg!(feature = "metro") {
        build
            .flag("-DMETRO_HASH=1")
            .file("src/smhasher/metrohash/metrohash64.cpp")
            .file("src/smhasher/metrohash/metrohash128.cpp");

        if support_sse42() {
            build
                .file("src/smhasher/metrohash/metrohash64crc.cpp")
                .file("src/smhasher/metrohash/metrohash128crc.cpp");
        }
    }

    if cfg!(feature = "mum") {
        build.flag("-DMUM_HASH=1").file("src/smhasher/mum.cc");
    }

    if cfg!(feature = "murmur") {
        build
            .flag("-DMURMUR_HASH=1")
            .file("src/smhasher/MurmurHash1.cpp")
            .file("src/smhasher/MurmurHash2.cpp")
            .file("src/smhasher/MurmurHash3.cpp");
    }

    if cfg!(feature = "spooky") {
        build
            .flag("-DSPOOKY_HASH=1")
            .file("src/smhasher/Spooky.cpp");
    }

    if cfg!(feature = "wy") {
        build.flag("-DWY_HASH=1");
    }

    if cfg!(feature = "xx") {
        build.flag("-DXX_HASH=1").file("src/xxHash/xxhash.c");
    }

    if cfg!(feature = "native") {
        build.flag("-march=native");
    } else {
        if cfg!(target_feature = "aes") {
            build.flag("-maes");
        }
        if cfg!(target_feature = "sse41") {
            build.flag("-msse41");
        }
        if cfg!(target_feature = "sse42") {
            build.flag("-msse4.2");
        }
        if cfg!(target_feature = "avx") {
            build.flag("-mavx");
        }
        if cfg!(target_feature = "avx2") {
            build.flag("-mavx2");
        }
    }

    build.static_flag(true).compile("fasthash");
}

fn build_t1() {
    let mut build = cc::Build::new();

    build
        .file("src/t1ha/src/t1ha0.c")
        .file("src/t1ha/src/t1ha1.c")
        .file("src/t1ha/src/t1ha2.c");

    // indirect functions are not supported on all targets (e.g. x86_64-unknown-linux-musl)
    #[cfg(target_env = "musl")]
    build.define("T1HA_USE_INDIRECT_FUNCTIONS", Some("0"));

    if support_aesni() {
        build
            .define("T1HA0_RUNTIME_SELECT", Some("1"))
            .define("T1HA0_AESNI_AVAILABLE", Some("1"))
            .flag("-maes")
            .file("src/t1ha/src/t1ha0_ia32aes_noavx.c")
            .file("src/t1ha/src/t1ha0_ia32aes_avx.c")
            .file("src/t1ha/src/t1ha0_ia32aes_avx2.c");

        if support_avx() {
            build.flag("-mavx");
        }

        if support_avx2() {
            build.flag("-mavx2");
        }
    }

    build.static_flag(true).compile("t1ha");
}

fn build_highway() {
    let mut build = cc::Build::new();

    build
        .cpp(true)
        .flag("-std=c++11")
        .flag_if_supported("-Wno-sign-compare")
        .include("src/highwayhash")
        .file("src/highwayhash/highwayhash/arch_specific.cc")
        .file("src/highwayhash/highwayhash/instruction_sets.cc")
        .file("src/highwayhash/highwayhash/os_specific.cc")
        .file("src/highwayhash/highwayhash/hh_portable.cc")
        .file("src/highwayhash/highwayhash/c_bindings.cc");

    if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
        if support_sse41() {
            build
                .flag("-msse4.1")
                .file("src/highwayhash/highwayhash/hh_sse41.cc");
        }

        if support_avx2() {
            build
                .flag("-mavx2")
                .file("src/highwayhash/highwayhash/hh_avx2.cc");
        }
    } else if cfg!(target_arch = "aarch64") {
        build.file("src/highwayhash/highwayhash/hh_neon.cc");
    } else if cfg!(target_arch = "powerpc64") {
        build.file("src/highwayhash/highwayhash/hh_vsx.cc");
    }

    build.static_flag(true).compile("highwayhash");
}

fn main() {
    if has_aesni() {
        cargo_emit::rustc_cfg!("aes");
    }
    if has_sse41() {
        cargo_emit::rustc_cfg!("sse41");
    }
    if has_sse42() {
        cargo_emit::rustc_cfg!("sse42");
    }
    if has_avx() {
        cargo_emit::rustc_cfg!("avx");
    }
    if has_avx2() {
        cargo_emit::rustc_cfg!("avx2");
    }

    build_fasthash();
    if cfg!(feature = "t1ha") {
        build_t1();
    }
    if cfg!(feature = "highway") {
        build_highway();
    }

    let out_dir = env::var("OUT_DIR").unwrap();
    let out_file = Path::new(&out_dir).join("fasthash.rs");

    cargo_emit::rerun_if_changed!("src/fasthash.hpp");
    cargo_emit::rerun_if_changed!("src/fasthash.cpp");

    generate_binding(&out_file);
}
<commit_msg>force to link sse4.1/avx2 version for highwayhash<commit_after>use std::env;
use std::path::Path;

use lazy_static::lazy_static;
use raw_cpuid::CpuId;

lazy_static! {
    static ref CPUID: CpuId = CpuId::new();
}

fn has_aesni() -> bool {
    cfg!(feature = "native")
        && CPUID
            .get_feature_info()
            .map_or(false, |features| features.has_aesni())
}

fn has_sse41() -> bool {
    cfg!(feature = "native")
        && CPUID
            .get_feature_info()
            .map_or(false, |features| features.has_sse41())
}

fn has_sse42() -> bool {
    cfg!(feature = "native")
        && CPUID
            .get_feature_info()
            .map_or(false, |features| features.has_sse42())
}

fn has_avx() -> bool {
    cfg!(feature = "native")
        && CPUID
            .get_feature_info()
            .map_or(false, |features| features.has_avx())
}

fn has_avx2() -> bool {
    cfg!(feature = "native")
        && CPUID
            .get_extended_feature_info()
            .map_or(false, |features| features.has_avx2())
}

fn support_aesni() -> bool {
    cfg!(any(feature = "aes", target_feature = "aes")) || has_aesni()
}

fn support_sse41() -> bool {
    cfg!(any(feature = "sse41", target_feature = "sse41")) || has_sse41()
}

fn support_sse42() -> bool {
    cfg!(any(feature = "sse42", target_feature = "sse42")) || has_sse42()
}

fn support_avx() -> bool {
    cfg!(any(feature = "avx", target_feature = "avx")) || has_avx()
}

fn support_avx2() -> bool {
    cfg!(any(feature = "avx2", target_feature = "avx2")) || has_avx2()
}

cfg_if::cfg_if! {
    if #[cfg(all(not(feature = "gen"), any(target_os = "macos", target_os = "linux")))] {
        fn generate_binding(_out_file: &Path) {
            cargo_emit::warning!("pregenerated binding file.");
        }
    } else {
        fn generate_binding(out_file: &Path) {
            cargo_emit::warning!("generate binding file @ {:?}.", out_file);

            let _ = bindgen::builder()
                .clang_args(&["-x", "c++", "-std=c++11"])
                .clang_args(&[
                    "-Dt1ha_EXPORTS",
                    "-DXXH_STATIC_LINKING_ONLY",
                    "-Isrc/highwayhash",
                ])
                .clang_args(if support_aesni() {
                    &[
                        "-maes",
                        "-DT1HA0_RUNTIME_SELECT=1",
                        "-DT1HA0_AESNI_AVAILABLE=1",
                    ][..]
                } else {
                    &[][..]
                })
                .clang_args(
                    vec![
                        if cfg!(feature = "native") {
                            Some("-march=native")
                        } else {
                            None
                        },
                        if support_sse41() {
                            Some("-msse4.1")
                        } else {
                            None
                        },
                        if support_sse42() {
                            Some("-msse4.2")
                        } else {
                            None
                        },
                        if support_avx() { Some("-mavx") } else { None },
                        if support_avx2() { Some("-mavx2") } else { None },
                        if cfg!(feature = "city") {
                            Some("-DCITY_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "farm") {
                            Some("-DFARM_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "highway") {
                            Some("-DHIGHWAY_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "komi") {
                            Some("-DKOMI_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "lookup3") {
                            Some("-DLOOKUP3=1")
                        } else {
                            None
                        },
                        if cfg!(all(feature = "meow", any(target_arch = "x86", target_arch = "x86_64"))) {
                            Some("-DMEOW_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "metro") {
                            Some("-DMETRO_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "mum") {
                            Some("-DMUM_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "murmur") {
                            Some("-DMURMUR_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "mx3") {
                            Some("-DMX3_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "nm") {
                            Some("-DNM_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "pengy") {
                            Some("-DPENGY_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "prv") {
                            Some("-DPRV_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "spooky") {
                            Some("-DSPOOKY_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "t1ha") {
                            Some("-DT1_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "wy") {
                            Some("-DWY_HASH=1")
                        } else {
                            None
                        },
                        if cfg!(feature = "xx") {
                            Some("-DXX_HASH=1")
                        } else {
                            None
                        },
                    ]
                    .into_iter()
                    .flatten(),
                )
                .header("src/fasthash.hpp")
                .size_t_is_usize(true)
                .generate_inline_functions(true)
                .disable_name_namespacing()
                .allowlist_function("^CityHash.*")
                .allowlist_function("^farmhash.*")
                .allowlist_function("^HighwayHash.*")
                .allowlist_function("^komi.*")
                .allowlist_function("^lookup3.*")
                .allowlist_function("^metrohash.*")
                .allowlist_function("^mum_hash.*")
                .allowlist_function("^MurmurHash.*")
                .allowlist_function("^mx3hash.*")
                .allowlist_function("^NMHASH.*")
                .allowlist_function("^pengy.*")
                .allowlist_function("^prvhash.*")
                .allowlist_function("^SpookyHasher.*")
                .allowlist_function("^t1ha.*")
                .allowlist_function("^wyhash.*")
                .allowlist_function("^XXH.*")
                .allowlist_function("^Meow.*")
                .blocklist_function("^t1ha_selfcheck__.*")
                .allowlist_var("^Meow.*")
                .allowlist_var("^PRH64S_.*")
                .generate()
                .unwrap()
                .write_to_file(out_file)
                .expect("fail to write bindings");

            cargo_emit::warning!("generate binding file @ {:?}.", out_file);
        }
    }
}

fn build_fasthash() {
    let mut build = cc::Build::new();

    build
        .cpp(true)
        .include("src/highwayhash")
        .flag("-std=c++11")
        .flag_if_supported("-Wno-implicit-fallthrough")
        .flag_if_supported("-Wno-unknown-attributes")
        .flag_if_supported("-Wno-sign-compare")
        .file("src/fasthash.cpp");

    if cfg!(feature = "city") {
        build.flag("-DCITY_HASH=1").file("src/smhasher/City.cpp");
    }

    if cfg!(feature = "farm") {
        build
            .flag("-DFARM_HASH=1")
            .file("src/smhasher/farmhash-c.c");
    }

    if cfg!(feature = "komi") {
        build.flag("-DKOMI_HASH=1");
    }

    if cfg!(feature = "highway") {
        build.flag("-DHIGHWAY_HASH=1");
    }

    if cfg!(feature = "lookup3") {
        build.flag("-DLOOKUP3=1").file("src/smhasher/lookup3.cpp");
    }

    if cfg!(all(
        feature = "meow",
        any(target_arch = "x86", target_arch = "x86_64")
    )) {
        build.flag("-DMEOW_HASH=1");
    }

    if cfg!(feature = "mx3") {
        build.flag("-DMX3_HASH=1");
    }

    if cfg!(feature = "nm") {
        build.flag("-DNM_HASH=1");
    }

    if cfg!(feature = "pengy") {
        build
            .flag("-DPENGY_HASH=1")
            .file("src/pengyhash/pengyhash.c");
    }

    if cfg!(feature = "prv") {
        build.flag("-DPRV_HASH=1");
    }

    if cfg!(feature = "t1ha") {
        build.flag("-DT1_HASH=1");
    }

    if cfg!(feature = "metro") {
        build
            .flag("-DMETRO_HASH=1")
            .file("src/smhasher/metrohash/metrohash64.cpp")
            .file("src/smhasher/metrohash/metrohash128.cpp");

        if support_sse42() {
            build
                .file("src/smhasher/metrohash/metrohash64crc.cpp")
                .file("src/smhasher/metrohash/metrohash128crc.cpp");
        }
    }

    if cfg!(feature = "mum") {
        build.flag("-DMUM_HASH=1").file("src/smhasher/mum.cc");
    }

    if cfg!(feature = "murmur") {
        build
            .flag("-DMURMUR_HASH=1")
            .file("src/smhasher/MurmurHash1.cpp")
            .file("src/smhasher/MurmurHash2.cpp")
            .file("src/smhasher/MurmurHash3.cpp");
    }

    if cfg!(feature = "spooky") {
        build
            .flag("-DSPOOKY_HASH=1")
            .file("src/smhasher/Spooky.cpp");
    }

    if cfg!(feature = "wy") {
        build.flag("-DWY_HASH=1");
    }

    if cfg!(feature = "xx") {
        build.flag("-DXX_HASH=1").file("src/xxHash/xxhash.c");
    }

    if cfg!(feature = "native") {
        build.flag("-march=native");
    } else {
        if cfg!(target_feature = "aes") {
            build.flag("-maes");
        }
        if cfg!(target_feature = "sse41") {
            build.flag("-msse41");
        }
        if cfg!(target_feature = "sse42") {
            build.flag("-msse4.2");
        }
        if cfg!(target_feature = "avx") {
            build.flag("-mavx");
        }
        if cfg!(target_feature = "avx2") {
            build.flag("-mavx2");
        }
    }

    build.static_flag(true).compile("fasthash");
}

fn build_t1() {
    let mut build = cc::Build::new();

    build
        .file("src/t1ha/src/t1ha0.c")
        .file("src/t1ha/src/t1ha1.c")
        .file("src/t1ha/src/t1ha2.c");

    // indirect functions are not supported on all targets (e.g. x86_64-unknown-linux-musl)
    #[cfg(target_env = "musl")]
    build.define("T1HA_USE_INDIRECT_FUNCTIONS", Some("0"));

    if support_aesni() {
        build
            .define("T1HA0_RUNTIME_SELECT", Some("1"))
            .define("T1HA0_AESNI_AVAILABLE", Some("1"))
            .flag("-maes")
            .file("src/t1ha/src/t1ha0_ia32aes_noavx.c")
            .file("src/t1ha/src/t1ha0_ia32aes_avx.c")
            .file("src/t1ha/src/t1ha0_ia32aes_avx2.c");

        if support_avx() {
            build.flag("-mavx");
        }

        if support_avx2() {
            build.flag("-mavx2");
        }
    }

    build.static_flag(true).compile("t1ha");
}

fn build_highway() {
    let mut build = cc::Build::new();

    build
        .cpp(true)
        .flag("-std=c++11")
        .flag_if_supported("-Wno-sign-compare")
        .include("src/highwayhash")
        .file("src/highwayhash/highwayhash/arch_specific.cc")
        .file("src/highwayhash/highwayhash/instruction_sets.cc")
        .file("src/highwayhash/highwayhash/os_specific.cc")
        .file("src/highwayhash/highwayhash/hh_portable.cc")
        .file("src/highwayhash/highwayhash/c_bindings.cc");

    if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
        build
            .flag("-msse4.1")
            .flag("-mavx2")
            .file("src/highwayhash/highwayhash/hh_sse41.cc")
            .file("src/highwayhash/highwayhash/hh_avx2.cc");
    } else if cfg!(target_arch = "aarch64") {
        build.file("src/highwayhash/highwayhash/hh_neon.cc");
    } else if cfg!(target_arch = "powerpc64") {
        build.file("src/highwayhash/highwayhash/hh_vsx.cc");
    }

    build.static_flag(true).compile("highwayhash");
}

fn main() {
    if has_aesni() {
        cargo_emit::rustc_cfg!("aes");
    }
    if has_sse41() {
        cargo_emit::rustc_cfg!("sse41");
    }
    if has_sse42() {
        cargo_emit::rustc_cfg!("sse42");
    }
    if has_avx() {
        cargo_emit::rustc_cfg!("avx");
    }
    if has_avx2() {
        cargo_emit::rustc_cfg!("avx2");
    }

    build_fasthash();
    if cfg!(feature = "t1ha") {
        build_t1();
    }
    if cfg!(feature = "highway") {
        build_highway();
    }

    let out_dir = env::var("OUT_DIR").unwrap();
    let out_file = Path::new(&out_dir).join("fasthash.rs");

    cargo_emit::rerun_if_changed!("src/fasthash.hpp");
    cargo_emit::rerun_if_changed!("src/fasthash.cpp");

    generate_binding(&out_file);
}
<|endoftext|>