<commit_before>use std::fs::{self, File};
use std::io::prelude::*;
use std::env;
use tempdir::TempDir;
use support::{execs, paths, cargo_dir};
use hamcrest::{assert_that, existing_file, existing_dir, is_not};

use cargo::util::{process, ProcessBuilder};

fn setup() {
}

fn cargo_process(s: &str) -> ProcessBuilder {
    let mut p = process(&cargo_dir().join("cargo"));
    p.arg(s).cwd(&paths::root()).env("HOME", &paths::home());
    return p;
}

test!(simple_lib {
    assert_that(cargo_process("init").arg("--vcs").arg("none")
                                    .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("Cargo.toml"), existing_file());
    assert_that(&paths::root().join("src/lib.rs"), existing_file());
    assert_that(&paths::root().join(".gitignore"), is_not(existing_file()));

    assert_that(cargo_process("build"),
                execs().with_status(0));
});

test!(simple_bin {
    let path = paths::root().join("foo");
    fs::create_dir(&path).unwrap();
    assert_that(cargo_process("init").arg("--bin").arg("--vcs").arg("none")
                                    .env("USER", "foo").cwd(&path),
                execs().with_status(0));

    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/main.rs"), existing_file());

    assert_that(cargo_process("build").cwd(&path),
                execs().with_status(0));
    assert_that(&paths::root().join(&format!("foo/target/debug/foo{}",
                                             env::consts::EXE_SUFFIX)),
                existing_file());
});

fn bin_already_exists(explicit: bool, rellocation: &str) {
    let path = paths::root().join("foo");
    fs::create_dir_all(&path.join("src")).unwrap();

    let sourcefile_path = path.join(rellocation);

    let content = br#"
        fn main() {
            println!("Hello, world 2!");
        }
    "#;

    File::create(&sourcefile_path).unwrap().write_all(content).unwrap();

    if explicit {
        assert_that(cargo_process("init").arg("--bin").arg("--vcs").arg("none")
                                        .env("USER", "foo").cwd(&path),
                    execs().with_status(0));
    } else {
        assert_that(cargo_process("init").arg("--vcs").arg("none")
                                        .env("USER", "foo").cwd(&path),
                    execs().with_status(0));
    }

    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/lib.rs"), is_not(existing_file()));

    // Check that our file is not overwritten
    let mut new_content = Vec::new();
    File::open(&sourcefile_path).unwrap().read_to_end(&mut new_content).unwrap();
    assert_eq!(Vec::from(content as &[u8]), new_content);
}

test!(bin_already_exists_explicit {
    bin_already_exists(true, "src/main.rs")
});

test!(bin_already_exists_implicit {
    bin_already_exists(false, "src/main.rs")
});

test!(bin_already_exists_explicit_nosrc {
    bin_already_exists(true, "main.rs")
});

test!(bin_already_exists_implicit_nosrc {
    bin_already_exists(false, "main.rs")
});

test!(bin_already_exists_implicit_namenosrc {
    bin_already_exists(false, "foo.rs")
});

test!(bin_already_exists_implicit_namesrc {
    bin_already_exists(false, "src/foo.rs")
});

test!(confused_by_multiple_lib_files {
    let path = paths::root().join("foo");
    fs::create_dir_all(&path.join("src")).unwrap();

    let sourcefile_path1 = path.join("src/lib.rs");

    File::create(&sourcefile_path1).unwrap().write_all(br#"
        fn qqq () {
            println!("Hello, world 2!");
        }
    "#).unwrap();

    let sourcefile_path2 = path.join("lib.rs");

    File::create(&sourcefile_path2).unwrap().write_all(br#"
        fn qqq () {
            println!("Hello, world 3!");
        }
    "#).unwrap();

    assert_that(cargo_process("init").arg("--vcs").arg("none")
                                    .env("USER", "foo").cwd(&path),
                execs().with_status(101).with_stderr("\
cannot have a project with multiple libraries, found both `src/lib.rs` and `lib.rs`
"));

    assert_that(&paths::root().join("foo/Cargo.toml"), is_not(existing_file()));
});

test!(multibin_project_name_clash {
    let path = paths::root().join("foo");
    fs::create_dir(&path).unwrap();

    let sourcefile_path1 = path.join("foo.rs");

    File::create(&sourcefile_path1).unwrap().write_all(br#"
        fn main () {
            println!("Hello, world 2!");
        }
    "#).unwrap();

    let sourcefile_path2 = path.join("main.rs");

    File::create(&sourcefile_path2).unwrap().write_all(br#"
        fn main () {
            println!("Hello, world 3!");
        }
    "#).unwrap();

    assert_that(cargo_process("init").arg("--vcs").arg("none")
                                    .env("USER", "foo").cwd(&path),
                execs().with_status(101).with_stderr("\
multiple possible binary sources found:
  main.rs
  foo.rs
cannot automatically generate Cargo.toml as the main target would be ambiguous
"));

    assert_that(&paths::root().join("foo/Cargo.toml"), is_not(existing_file()));
});

fn lib_already_exists(rellocation: &str) {
    let path = paths::root().join("foo");
    fs::create_dir_all(&path.join("src")).unwrap();

    let sourcefile_path = path.join(rellocation);

    let content = br#"
        pub fn qqq() {}
    "#;

    File::create(&sourcefile_path).unwrap().write_all(content).unwrap();

    assert_that(cargo_process("init").arg("--vcs").arg("none")
                                    .env("USER", "foo").cwd(&path),
                execs().with_status(0));

    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/main.rs"), is_not(existing_file()));

    // Check that our file is not overwritten
    let mut new_content = Vec::new();
    File::open(&sourcefile_path).unwrap().read_to_end(&mut new_content).unwrap();
    assert_eq!(Vec::from(content as &[u8]), new_content);
}

test!(lib_already_exists_src {
    lib_already_exists("src/lib.rs")
});

test!(lib_already_exists_nosrc {
    lib_already_exists("lib.rs")
});

test!(simple_git {
    assert_that(cargo_process("init").arg("--vcs").arg("git")
                                    .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("Cargo.toml"), existing_file());
    assert_that(&paths::root().join("src/lib.rs"), existing_file());
    assert_that(&paths::root().join(".git"), existing_dir());
    assert_that(&paths::root().join(".gitignore"), existing_file());
});

test!(auto_git {
    let td = TempDir::new("cargo").unwrap();
    let foo = &td.path().join("foo");
    fs::create_dir_all(&foo).unwrap();
    assert_that(cargo_process("init").cwd(foo.clone())
                                     .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&foo.join("Cargo.toml"), existing_file());
    assert_that(&foo.join("src/lib.rs"), existing_file());
    assert_that(&foo.join(".git"), existing_dir());
    assert_that(&foo.join(".gitignore"), existing_file());
});

test!(invalid_dir_name {
    let foo = &paths::root().join("foo.bar");
    fs::create_dir_all(&foo).unwrap();
    assert_that(cargo_process("init").cwd(foo.clone())
                                     .env("USER", "foo"),
                execs().with_status(101).with_stderr("\
Invalid character `.` in crate name: `foo.bar`
use --name to override crate name
"));

    assert_that(&foo.join("Cargo.toml"), is_not(existing_file()));
});

test!(git_autodetect {
    fs::create_dir(&paths::root().join(".git")).unwrap();

    assert_that(cargo_process("init")
                                    .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("Cargo.toml"), existing_file());
    assert_that(&paths::root().join("src/lib.rs"), existing_file());
    assert_that(&paths::root().join(".git"), existing_dir());
    assert_that(&paths::root().join(".gitignore"), existing_file());
});

test!(mercurial_autodetect {
    fs::create_dir(&paths::root().join(".hg")).unwrap();

    assert_that(cargo_process("init")
                                    .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("Cargo.toml"), existing_file());
    assert_that(&paths::root().join("src/lib.rs"), existing_file());
    assert_that(&paths::root().join(".git"), is_not(existing_dir()));
    assert_that(&paths::root().join(".hgignore"), existing_file());
});

test!(gitignore_appended_not_replaced {
    fs::create_dir(&paths::root().join(".git")).unwrap();

    File::create(&paths::root().join(".gitignore")).unwrap().write_all(b"qqqqqq\n").unwrap();

    assert_that(cargo_process("init")
                                    .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("Cargo.toml"), existing_file());
    assert_that(&paths::root().join("src/lib.rs"), existing_file());
    assert_that(&paths::root().join(".git"), existing_dir());
    assert_that(&paths::root().join(".gitignore"), existing_file());

    let mut contents = String::new();
    File::open(&paths::root().join(".gitignore")).unwrap().read_to_string(&mut contents).unwrap();
    assert!(contents.contains(r#"qqqqqq"#));
});

test!(with_argument {
    assert_that(cargo_process("init").arg("foo").arg("--vcs").arg("none")
                                     .env("USER", "foo"),
                execs().with_status(0));
    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
});

test!(unknown_flags {
    assert_that(cargo_process("init").arg("foo").arg("--flag"),
                execs().with_status(1)
                       .with_stderr("\
Unknown flag: '--flag'

Usage:
    cargo init [options] [<path>]
    cargo init -h | --help
"));
});

#[cfg(not(windows))]
test!(no_filename {
    assert_that(cargo_process("init").arg("/"),
                execs().with_status(101)
                       .with_stderr("\
cannot auto-detect project name from path \"/\" ; use --name to override
"));
});
<commit_msg>cargo init: Implement test for conditional Cargo.lock ignoring<commit_after>use std::fs::{self, File};
use std::io::prelude::*;
use std::env;
use tempdir::TempDir;
use support::{execs, paths, cargo_dir};
use hamcrest::{assert_that, existing_file, existing_dir, is_not};

use cargo::util::{process, ProcessBuilder};

fn setup() {
}

fn cargo_process(s: &str) -> ProcessBuilder {
    let mut p = process(&cargo_dir().join("cargo"));
    p.arg(s).cwd(&paths::root()).env("HOME", &paths::home());
    return p;
}

test!(simple_lib {
    assert_that(cargo_process("init").arg("--vcs").arg("none")
                                    .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("Cargo.toml"), existing_file());
    assert_that(&paths::root().join("src/lib.rs"), existing_file());
    assert_that(&paths::root().join(".gitignore"), is_not(existing_file()));

    assert_that(cargo_process("build"),
                execs().with_status(0));
});

test!(simple_bin {
    let path = paths::root().join("foo");
    fs::create_dir(&path).unwrap();
    assert_that(cargo_process("init").arg("--bin").arg("--vcs").arg("none")
                                    .env("USER", "foo").cwd(&path),
                execs().with_status(0));

    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/main.rs"), existing_file());

    assert_that(cargo_process("build").cwd(&path),
                execs().with_status(0));
    assert_that(&paths::root().join(&format!("foo/target/debug/foo{}",
                                             env::consts::EXE_SUFFIX)),
                existing_file());
});

fn bin_already_exists(explicit: bool, rellocation: &str) {
    let path = paths::root().join("foo");
    fs::create_dir_all(&path.join("src")).unwrap();

    let sourcefile_path = path.join(rellocation);

    let content = br#"
        fn main() {
            println!("Hello, world 2!");
        }
    "#;

    File::create(&sourcefile_path).unwrap().write_all(content).unwrap();

    if explicit {
        assert_that(cargo_process("init").arg("--bin").arg("--vcs").arg("none")
                                        .env("USER", "foo").cwd(&path),
                    execs().with_status(0));
    } else {
        assert_that(cargo_process("init").arg("--vcs").arg("none")
                                        .env("USER", "foo").cwd(&path),
                    execs().with_status(0));
    }

    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/lib.rs"), is_not(existing_file()));

    // Check that our file is not overwritten
    let mut new_content = Vec::new();
    File::open(&sourcefile_path).unwrap().read_to_end(&mut new_content).unwrap();
    assert_eq!(Vec::from(content as &[u8]), new_content);
}

test!(bin_already_exists_explicit {
    bin_already_exists(true, "src/main.rs")
});

test!(bin_already_exists_implicit {
    bin_already_exists(false, "src/main.rs")
});

test!(bin_already_exists_explicit_nosrc {
    bin_already_exists(true, "main.rs")
});

test!(bin_already_exists_implicit_nosrc {
    bin_already_exists(false, "main.rs")
});

test!(bin_already_exists_implicit_namenosrc {
    bin_already_exists(false, "foo.rs")
});

test!(bin_already_exists_implicit_namesrc {
    bin_already_exists(false, "src/foo.rs")
});

test!(confused_by_multiple_lib_files {
    let path = paths::root().join("foo");
    fs::create_dir_all(&path.join("src")).unwrap();

    let sourcefile_path1 = path.join("src/lib.rs");

    File::create(&sourcefile_path1).unwrap().write_all(br#"
        fn qqq () {
            println!("Hello, world 2!");
        }
    "#).unwrap();

    let sourcefile_path2 = path.join("lib.rs");

    File::create(&sourcefile_path2).unwrap().write_all(br#"
        fn qqq () {
            println!("Hello, world 3!");
        }
    "#).unwrap();

    assert_that(cargo_process("init").arg("--vcs").arg("none")
                                    .env("USER", "foo").cwd(&path),
                execs().with_status(101).with_stderr("\
cannot have a project with multiple libraries, found both `src/lib.rs` and `lib.rs`
"));

    assert_that(&paths::root().join("foo/Cargo.toml"), is_not(existing_file()));
});

test!(multibin_project_name_clash {
    let path = paths::root().join("foo");
    fs::create_dir(&path).unwrap();

    let sourcefile_path1 = path.join("foo.rs");

    File::create(&sourcefile_path1).unwrap().write_all(br#"
        fn main () {
            println!("Hello, world 2!");
        }
    "#).unwrap();

    let sourcefile_path2 = path.join("main.rs");

    File::create(&sourcefile_path2).unwrap().write_all(br#"
        fn main () {
            println!("Hello, world 3!");
        }
    "#).unwrap();

    assert_that(cargo_process("init").arg("--vcs").arg("none")
                                    .env("USER", "foo").cwd(&path),
                execs().with_status(101).with_stderr("\
multiple possible binary sources found:
  main.rs
  foo.rs
cannot automatically generate Cargo.toml as the main target would be ambiguous
"));

    assert_that(&paths::root().join("foo/Cargo.toml"), is_not(existing_file()));
});

fn lib_already_exists(rellocation: &str) {
    let path = paths::root().join("foo");
    fs::create_dir_all(&path.join("src")).unwrap();

    let sourcefile_path = path.join(rellocation);

    let content = br#"
        pub fn qqq() {}
    "#;

    File::create(&sourcefile_path).unwrap().write_all(content).unwrap();

    assert_that(cargo_process("init").arg("--vcs").arg("none")
                                    .env("USER", "foo").cwd(&path),
                execs().with_status(0));

    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
    assert_that(&paths::root().join("foo/src/main.rs"), is_not(existing_file()));

    // Check that our file is not overwritten
    let mut new_content = Vec::new();
    File::open(&sourcefile_path).unwrap().read_to_end(&mut new_content).unwrap();
    assert_eq!(Vec::from(content as &[u8]), new_content);
}

test!(lib_already_exists_src {
    lib_already_exists("src/lib.rs")
});

test!(lib_already_exists_nosrc {
    lib_already_exists("lib.rs")
});

test!(simple_git {
    assert_that(cargo_process("init").arg("--vcs").arg("git")
                                    .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("Cargo.toml"), existing_file());
    assert_that(&paths::root().join("src/lib.rs"), existing_file());
    assert_that(&paths::root().join(".git"), existing_dir());
    assert_that(&paths::root().join(".gitignore"), existing_file());
});

test!(auto_git {
    let td = TempDir::new("cargo").unwrap();
    let foo = &td.path().join("foo");
    fs::create_dir_all(&foo).unwrap();
    assert_that(cargo_process("init").cwd(foo.clone())
                                     .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&foo.join("Cargo.toml"), existing_file());
    assert_that(&foo.join("src/lib.rs"), existing_file());
    assert_that(&foo.join(".git"), existing_dir());
    assert_that(&foo.join(".gitignore"), existing_file());
});

test!(invalid_dir_name {
    let foo = &paths::root().join("foo.bar");
    fs::create_dir_all(&foo).unwrap();
    assert_that(cargo_process("init").cwd(foo.clone())
                                     .env("USER", "foo"),
                execs().with_status(101).with_stderr("\
Invalid character `.` in crate name: `foo.bar`
use --name to override crate name
"));

    assert_that(&foo.join("Cargo.toml"), is_not(existing_file()));
});

test!(git_autodetect {
    fs::create_dir(&paths::root().join(".git")).unwrap();

    assert_that(cargo_process("init")
                                    .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("Cargo.toml"), existing_file());
    assert_that(&paths::root().join("src/lib.rs"), existing_file());
    assert_that(&paths::root().join(".git"), existing_dir());
    assert_that(&paths::root().join(".gitignore"), existing_file());
});

test!(mercurial_autodetect {
    fs::create_dir(&paths::root().join(".hg")).unwrap();

    assert_that(cargo_process("init")
                                    .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("Cargo.toml"), existing_file());
    assert_that(&paths::root().join("src/lib.rs"), existing_file());
    assert_that(&paths::root().join(".git"), is_not(existing_dir()));
    assert_that(&paths::root().join(".hgignore"), existing_file());
});

test!(gitignore_appended_not_replaced {
    fs::create_dir(&paths::root().join(".git")).unwrap();

    File::create(&paths::root().join(".gitignore")).unwrap().write_all(b"qqqqqq\n").unwrap();

    assert_that(cargo_process("init")
                                    .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join("Cargo.toml"), existing_file());
    assert_that(&paths::root().join("src/lib.rs"), existing_file());
    assert_that(&paths::root().join(".git"), existing_dir());
    assert_that(&paths::root().join(".gitignore"), existing_file());

    let mut contents = String::new();
    File::open(&paths::root().join(".gitignore")).unwrap().read_to_string(&mut contents).unwrap();
    assert!(contents.contains(r#"qqqqqq"#));
});

test!(cargo_lock_gitignored_if_lib1 {
    fs::create_dir(&paths::root().join(".git")).unwrap();

    assert_that(cargo_process("init").arg("--vcs").arg("git")
                                     .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join(".gitignore"), existing_file());

    let mut contents = String::new();
    File::open(&paths::root().join(".gitignore")).unwrap().read_to_string(&mut contents).unwrap();
    assert!(contents.contains(r#"Cargo.lock"#));
});

test!(cargo_lock_gitignored_if_lib2 {
    fs::create_dir(&paths::root().join(".git")).unwrap();

    File::create(&paths::root().join("lib.rs")).unwrap().write_all(br#""#).unwrap();

    assert_that(cargo_process("init").arg("--vcs").arg("git")
                                     .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join(".gitignore"), existing_file());

    let mut contents = String::new();
    File::open(&paths::root().join(".gitignore")).unwrap().read_to_string(&mut contents).unwrap();
    assert!(contents.contains(r#"Cargo.lock"#));
});

test!(cargo_lock_not_gitignored_if_bin1 {
    fs::create_dir(&paths::root().join(".git")).unwrap();

    assert_that(cargo_process("init").arg("--vcs").arg("git")
                                     .arg("--bin")
                                     .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join(".gitignore"), existing_file());

    let mut contents = String::new();
    File::open(&paths::root().join(".gitignore")).unwrap().read_to_string(&mut contents).unwrap();
    assert!(!contents.contains(r#"Cargo.lock"#));
});

test!(cargo_lock_not_gitignored_if_bin2 {
    fs::create_dir(&paths::root().join(".git")).unwrap();

    File::create(&paths::root().join("main.rs")).unwrap().write_all(br#""#).unwrap();

    assert_that(cargo_process("init").arg("--vcs").arg("git")
                                     .env("USER", "foo"),
                execs().with_status(0));

    assert_that(&paths::root().join(".gitignore"), existing_file());

    let mut contents = String::new();
    File::open(&paths::root().join(".gitignore")).unwrap().read_to_string(&mut contents).unwrap();
    assert!(!contents.contains(r#"Cargo.lock"#));
});

test!(with_argument {
    assert_that(cargo_process("init").arg("foo").arg("--vcs").arg("none")
                                     .env("USER", "foo"),
                execs().with_status(0));
    assert_that(&paths::root().join("foo/Cargo.toml"), existing_file());
});

test!(unknown_flags {
    assert_that(cargo_process("init").arg("foo").arg("--flag"),
                execs().with_status(1)
                       .with_stderr("\
Unknown flag: '--flag'

Usage:
    cargo init [options] [<path>]
    cargo init -h | --help
"));
});

#[cfg(not(windows))]
test!(no_filename {
    assert_that(cargo_process("init").arg("/"),
                execs().with_status(101)
                       .with_stderr("\
cannot auto-detect project name from path \"/\" ; use --name to override
"));
});
<|endoftext|>


<commit_before>use kvm::CpuId;
use std::result;

// constants for setting the fields of kvm_cpuid2 structures
// CPUID bits in ebx, ecx, and edx.
const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
const EBX_CLFLUSH_SIZE_SHIFT: u32 = 8; // Bytes flushed when executing CLFLUSH.
const EBX_CPU_COUNT_SHIFT: u32 = 16; // The logical processor count .
const EBX_APICID_SHIFT: u32 = 24; // The (fixed) default APIC ID.
const ECX_EPB_SHIFT: u32 = 3; // "Energy Performance Bias" bit.
const ECX_TSC_DEADLINE_TIMER_SHIFT: u32 = 24;
const ECX_HYPERVISOR_SHIFT: u32 = 31; // Flag to be set when the cpu is running on a hypervisor.
const ECX_LEVEL_TYPE_SHIFT: u32 = 8; // Shift for setting level type for leaf 11
const EDX_HTT_SHIFT: u32 = 28; // Hyper Threading Enabled.

// Deterministic Cache Parameters Leaf
const EAX_CACHE_LEVEL: u32 = 5;
const EAX_MAX_ADDR_IDS_SHARING_CACHE: u32 = 14;
const EAX_MAX_ADDR_IDS_IN_PACKAGE: u32 = 26;

const LEAFBH_LEVEL_TYPE_INVALID: u32 = 0;
const LEAFBH_LEVEL_TYPE_THREAD: u32 = 1;
const LEAFBH_LEVEL_TYPE_CORE: u32 = 2;

// The APIC ID shift in leaf 0xBh specifies the number of bits to shit the x2APIC ID to get a
// unique topology of the next level. This allows 64 logical processors/package.
const LEAFBH_INDEX1_APICID_SHIFT: u32 = 6;

pub type Result<T> = result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
    VcpuCountOverflow,
}

/// This function is used for setting leaf 01H EBX[23-16]
/// The maximum number of addressable logical CPUs is computed as the closest power of 2
/// higher or equal to the CPU count configured by the user
fn get_max_addressable_lprocessors(cpu_count: u8) -> result::Result<u8, Error> {
    let mut max_addressable_lcpu = (cpu_count as f64).log2().ceil();
    max_addressable_lcpu = (2 as f64).powf(max_addressable_lcpu);
    // check that this number is still an u8
    if max_addressable_lcpu > u8::max_value().into() {
        return Err(Error::VcpuCountOverflow);
    }
    Ok(max_addressable_lcpu as u8)
}

/// Sets up the cpuid entries for the given vcpu
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn filter_cpuid(cpu_id: u8, cpu_count: u8, kvm_cpuid: &mut CpuId) -> Result<()> {
    let entries = kvm_cpuid.mut_entries_slice();
    let max_addr_cpu = get_max_addressable_lprocessors(cpu_count).unwrap() as u32;
    for entry in entries.iter_mut() {
        match entry.function {
            1 => {
                // X86 hypervisor feature
                if entry.index == 0 {
                    entry.ecx |= 1 << ECX_TSC_DEADLINE_TIMER_SHIFT;
                    entry.ecx |= 1 << ECX_HYPERVISOR_SHIFT;
                }
                entry.ebx = ((cpu_id as u32) << EBX_APICID_SHIFT) as u32
                    | (EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT);
                entry.ebx |= max_addr_cpu << EBX_CPU_COUNT_SHIFT;
                // Make sure that Hyperthreading is disabled
                entry.edx &= !(1 << EDX_HTT_SHIFT);
                // Enable Hyperthreading for even vCPU count so you don't end up with
                // an even and > 1 number of sibilings
                if cpu_count > 1 && cpu_count % 2 == 0 {
                    entry.edx |= 1 << EDX_HTT_SHIFT;
                }
            }
            4 => {
                // Deterministic Cache Parameters Leaf
                // Only use the last 3 bits of EAX[5:32] because the level is encoded in EAX[5:7]
                let cache_level = (entry.eax >> EAX_CACHE_LEVEL) & (0b111 as u32);
                match cache_level {
                    // L1 & L2 Cache
                    1 | 2 => {
                        // Set the maximum addressable IDS sharing the data cache to zero
                        // when you only have 1 vcpu because there are no other threads on
                        // the machine to share the data/instruction cache
                        // This sets EAX[25:14]
                        entry.eax &= !(0b111111111111 << EAX_MAX_ADDR_IDS_SHARING_CACHE);
                        if cpu_count > 1 {
                            // Hyperthreading is enabled by default for vcpu_count > 2
                            entry.eax |= 1 << EAX_MAX_ADDR_IDS_SHARING_CACHE;
                        }
                    }
                    // L3 Cache
                    3 => {
                        // Set the maximum addressable IDS sharing the data cache to zero
                        // when you only have 1 vcpu because there are no other threads on
                        // the machine to share the data/instruction cache
                        // This sets EAX[25:14]
                        entry.eax &= !(0b111111111111 << EAX_MAX_ADDR_IDS_SHARING_CACHE);
                        if cpu_count > 1 {
                            entry.eax |= ((cpu_count - 1) as u32) << EAX_MAX_ADDR_IDS_SHARING_CACHE;
                        }
                    }
                    _ => (),
                }

                // Maximum number of addressable IDs for processor cores in the physical package
                // should be the same on all cache levels
                // set this to 0 because there is only 1 core available for vcpu_count <= 2
                // This sets EAX[31:26]
                entry.eax &= !(0b111111 << EAX_MAX_ADDR_IDS_IN_PACKAGE);
                if cpu_count > 2 {
                    // we have HT enabled by default, so we will have cpu_count/2 cores in package
                    entry.eax |= (((cpu_count >> 1) - 1) as u32) << EAX_MAX_ADDR_IDS_IN_PACKAGE;
                }
            }
            6 => {
                // Clear X86 EPB feature.  No frequency selection in the hypervisor.
                entry.ecx &= !(1 << ECX_EPB_SHIFT);
            }
            11 => {
                // Hide the actual topology of the underlying host
                match entry.index {
                    0 => {
                        // Thread Level Topology; index = 0
                        if cpu_count == 1 {
                            // No APIC ID at the next level, set EAX to 0
                            entry.eax = 0;
                            // Set the numbers of logical processors to 1
                            entry.ebx = 1;
                            // There are no hyperthreads for 1 VCPU, set the level type = 2 (Core)
                            entry.ecx = LEAFBH_LEVEL_TYPE_CORE << ECX_LEVEL_TYPE_SHIFT;
                        } else {
                            // To get the next level APIC ID, shift right with 1 because we have
                            // maximum 2 hyperthreads per core that can be represented with 1 bit
                            entry.eax = 1;
                            // 2 logical cores at this level
                            entry.ebx = 2;
                            // enforce this level to be of type thread
                            entry.ecx = LEAFBH_LEVEL_TYPE_THREAD << ECX_LEVEL_TYPE_SHIFT;
                        }
                    }
                    1 => {
                        // Core Level Processor Topology; index = 1
                        entry.eax = LEAFBH_INDEX1_APICID_SHIFT;
                        if cpu_count == 1 {
                            // For 1 vCPU, this level is invalid
                            entry.ebx = 0;
                            // ECX[7:0] = entry.index; ECX[15:8] = 0 (Invalid Level)
                            entry.ecx = (entry.index as u32)
                                | (LEAFBH_LEVEL_TYPE_INVALID << ECX_LEVEL_TYPE_SHIFT);
                        } else {
                            entry.ebx = cpu_count as u32;
                            entry.ecx = (entry.index as u32)
                                | (LEAFBH_LEVEL_TYPE_CORE << ECX_LEVEL_TYPE_SHIFT);
                        }
                    }
                    level => {
                        // Core Level Processor Topology; index >=2
                        // No other levels available; This should already be set to correctly,
                        // and it is added here as a "re-enforcement" in case we run on
                        // different hardware
                        entry.eax = 0;
                        entry.ebx = 0;
                        entry.ecx = level;
                    }
                }
                // EDX bits 31..0 contain x2APIC ID of current logical processor
                // x2APIC increases the size of the APIC ID from 8 bits to 32 bits
                entry.edx = cpu_id as u32;
            }
            _ => (),
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use kvm::{Kvm, MAX_KVM_CPUID_ENTRIES};
    use kvm_sys::kvm_cpuid_entry2;

    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    #[test]
    fn test_get_max_addressable_lprocessors() {
        assert_eq!(get_max_addressable_lprocessors(1).unwrap(), 1);
        assert_eq!(get_max_addressable_lprocessors(2).unwrap(), 2);
        assert_eq!(get_max_addressable_lprocessors(4).unwrap(), 4);
        assert_eq!(get_max_addressable_lprocessors(6).unwrap(), 8);
        assert!(get_max_addressable_lprocessors(u8::max_value()).is_err());
    }

    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    #[test]
    fn test_filter_cpuid() {
        let kvm = Kvm::new().unwrap();
        let mut kvm_cpuid: CpuId = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap();
        match filter_cpuid(0, 1, &mut kvm_cpuid) {
            Ok(_) => (),
            _ => assert!(false),
        };

        let entries = kvm_cpuid.mut_entries_slice();
        // TODO: add tests for the other cpuid leaves
        // Test the extended topology
        // See https://www.scss.tcd.ie/~jones/CS4021/processor-identification-cpuid-instruction-note.pdf
        let leaf11_index0 = kvm_cpuid_entry2 {
            function: 11,
            index: 0,
            flags: 1,
            eax: 0,
            ebx: 1,                                              // nr of hyperthreads/core
            ecx: LEAFBH_LEVEL_TYPE_CORE << ECX_LEVEL_TYPE_SHIFT, // ECX[15:8] = 2 (Core Level)
            edx: 0,                                              // EDX = APIC ID = 0
            padding: [0, 0, 0],
        };
        assert!(entries.contains(&leaf11_index0));
        let leaf11_index1 = kvm_cpuid_entry2 {
            function: 11,
            index: 1,
            flags: 1,
            eax: LEAFBH_INDEX1_APICID_SHIFT,
            ebx: 0,
            ecx: 1, // ECX[15:8] = 0 (Invalid Level) & ECX[7:0] = 1 (Level Number)
            edx: 0, // EDX = APIC ID = 0
            padding: [0, 0, 0],
        };
        assert!(entries.contains(&leaf11_index1));
        let leaf11_index2 = kvm_cpuid_entry2 {
            function: 11,
            index: 2,
            flags: 1,
            eax: 0,
            ebx: 0, // nr of hyperthreads/core
            ecx: 2, // ECX[15:8] = 0 (Invalid Level) & ECX[7:0] = 2 (Level Number)
            edx: 0, // EDX = APIC ID = 0
            padding: [0, 0, 0],
        };
        assert!(entries.contains(&leaf11_index2));
    }
}
<commit_msg>cpuid: match function in hexa<commit_after>use kvm::CpuId;
use std::result;

// constants for setting the fields of kvm_cpuid2 structures
// CPUID bits in ebx, ecx, and edx.
const EBX_CLFLUSH_CACHELINE: u32 = 8; // Flush a cache line size.
const EBX_CLFLUSH_SIZE_SHIFT: u32 = 8; // Bytes flushed when executing CLFLUSH.
const EBX_CPU_COUNT_SHIFT: u32 = 16; // The logical processor count .
const EBX_APICID_SHIFT: u32 = 24; // The (fixed) default APIC ID.
const ECX_EPB_SHIFT: u32 = 3; // "Energy Performance Bias" bit.
const ECX_TSC_DEADLINE_TIMER_SHIFT: u32 = 24;
const ECX_HYPERVISOR_SHIFT: u32 = 31; // Flag to be set when the cpu is running on a hypervisor.
const ECX_LEVEL_TYPE_SHIFT: u32 = 8; // Shift for setting level type for leaf 11
const EDX_HTT_SHIFT: u32 = 28; // Hyper Threading Enabled.

// Deterministic Cache Parameters Leaf
const EAX_CACHE_LEVEL: u32 = 5;
const EAX_MAX_ADDR_IDS_SHARING_CACHE: u32 = 14;
const EAX_MAX_ADDR_IDS_IN_PACKAGE: u32 = 26;

const LEAFBH_LEVEL_TYPE_INVALID: u32 = 0;
const LEAFBH_LEVEL_TYPE_THREAD: u32 = 1;
const LEAFBH_LEVEL_TYPE_CORE: u32 = 2;

// The APIC ID shift in leaf 0xBh specifies the number of bits to shit the x2APIC ID to get a
// unique topology of the next level. This allows 64 logical processors/package.
const LEAFBH_INDEX1_APICID_SHIFT: u32 = 6;

pub type Result<T> = result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
    VcpuCountOverflow,
}

/// This function is used for setting leaf 01H EBX[23-16]
/// The maximum number of addressable logical CPUs is computed as the closest power of 2
/// higher or equal to the CPU count configured by the user
fn get_max_addressable_lprocessors(cpu_count: u8) -> result::Result<u8, Error> {
    let mut max_addressable_lcpu = (cpu_count as f64).log2().ceil();
    max_addressable_lcpu = (2 as f64).powf(max_addressable_lcpu);
    // check that this number is still an u8
    if max_addressable_lcpu > u8::max_value().into() {
        return Err(Error::VcpuCountOverflow);
    }
    Ok(max_addressable_lcpu as u8)
}

/// Sets up the cpuid entries for the given vcpu
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub fn filter_cpuid(cpu_id: u8, cpu_count: u8, kvm_cpuid: &mut CpuId) -> Result<()> {
    let entries = kvm_cpuid.mut_entries_slice();
    let max_addr_cpu = get_max_addressable_lprocessors(cpu_count).unwrap() as u32;
    for entry in entries.iter_mut() {
        match entry.function {
            0x1 => {
                // X86 hypervisor feature
                if entry.index == 0 {
                    entry.ecx |= 1 << ECX_TSC_DEADLINE_TIMER_SHIFT;
                    entry.ecx |= 1 << ECX_HYPERVISOR_SHIFT;
                }
                entry.ebx = ((cpu_id as u32) << EBX_APICID_SHIFT) as u32
                    | (EBX_CLFLUSH_CACHELINE << EBX_CLFLUSH_SIZE_SHIFT);
                entry.ebx |= max_addr_cpu << EBX_CPU_COUNT_SHIFT;
                // Make sure that Hyperthreading is disabled
                entry.edx &= !(1 << EDX_HTT_SHIFT);
                // Enable Hyperthreading for even vCPU count so you don't end up with
                // an even and > 1 number of sibilings
                if cpu_count > 1 && cpu_count % 2 == 0 {
                    entry.edx |= 1 << EDX_HTT_SHIFT;
                }
            }
            0x4 => {
                // Deterministic Cache Parameters Leaf
                // Only use the last 3 bits of EAX[5:32] because the level is encoded in EAX[5:7]
                let cache_level = (entry.eax >> EAX_CACHE_LEVEL) & (0b111 as u32);
                match cache_level {
                    // L1 & L2 Cache
                    1 | 2 => {
                        // Set the maximum addressable IDS sharing the data cache to zero
                        // when you only have 1 vcpu because there are no other threads on
                        // the machine to share the data/instruction cache
                        // This sets EAX[25:14]
                        entry.eax &= !(0b111111111111 << EAX_MAX_ADDR_IDS_SHARING_CACHE);
                        if cpu_count > 1 {
                            // Hyperthreading is enabled by default for vcpu_count > 2
                            entry.eax |= 1 << EAX_MAX_ADDR_IDS_SHARING_CACHE;
                        }
                    }
                    // L3 Cache
                    3 => {
                        // Set the maximum addressable IDS sharing the data cache to zero
                        // when you only have 1 vcpu because there are no other threads on
                        // the machine to share the data/instruction cache
                        // This sets EAX[25:14]
                        entry.eax &= !(0b111111111111 << EAX_MAX_ADDR_IDS_SHARING_CACHE);
                        if cpu_count > 1 {
                            entry.eax |= ((cpu_count - 1) as u32) << EAX_MAX_ADDR_IDS_SHARING_CACHE;
                        }
                    }
                    _ => (),
                }

                // Maximum number of addressable IDs for processor cores in the physical package
                // should be the same on all cache levels
                // set this to 0 because there is only 1 core available for vcpu_count <= 2
                // This sets EAX[31:26]
                entry.eax &= !(0b111111 << EAX_MAX_ADDR_IDS_IN_PACKAGE);
                if cpu_count > 2 {
                    // we have HT enabled by default, so we will have cpu_count/2 cores in package
                    entry.eax |= (((cpu_count >> 1) - 1) as u32) << EAX_MAX_ADDR_IDS_IN_PACKAGE;
                }
            }
            0x6 => {
                // Clear X86 EPB feature.  No frequency selection in the hypervisor.
                entry.ecx &= !(1 << ECX_EPB_SHIFT);
            }
            0xB => {
                // Hide the actual topology of the underlying host
                match entry.index {
                    0 => {
                        // Thread Level Topology; index = 0
                        if cpu_count == 1 {
                            // No APIC ID at the next level, set EAX to 0
                            entry.eax = 0;
                            // Set the numbers of logical processors to 1
                            entry.ebx = 1;
                            // There are no hyperthreads for 1 VCPU, set the level type = 2 (Core)
                            entry.ecx = LEAFBH_LEVEL_TYPE_CORE << ECX_LEVEL_TYPE_SHIFT;
                        } else {
                            // To get the next level APIC ID, shift right with 1 because we have
                            // maximum 2 hyperthreads per core that can be represented with 1 bit
                            entry.eax = 1;
                            // 2 logical cores at this level
                            entry.ebx = 2;
                            // enforce this level to be of type thread
                            entry.ecx = LEAFBH_LEVEL_TYPE_THREAD << ECX_LEVEL_TYPE_SHIFT;
                        }
                    }
                    1 => {
                        // Core Level Processor Topology; index = 1
                        entry.eax = LEAFBH_INDEX1_APICID_SHIFT;
                        if cpu_count == 1 {
                            // For 1 vCPU, this level is invalid
                            entry.ebx = 0;
                            // ECX[7:0] = entry.index; ECX[15:8] = 0 (Invalid Level)
                            entry.ecx = (entry.index as u32)
                                | (LEAFBH_LEVEL_TYPE_INVALID << ECX_LEVEL_TYPE_SHIFT);
                        } else {
                            entry.ebx = cpu_count as u32;
                            entry.ecx = (entry.index as u32)
                                | (LEAFBH_LEVEL_TYPE_CORE << ECX_LEVEL_TYPE_SHIFT);
                        }
                    }
                    level => {
                        // Core Level Processor Topology; index >=2
                        // No other levels available; This should already be set to correctly,
                        // and it is added here as a "re-enforcement" in case we run on
                        // different hardware
                        entry.eax = 0;
                        entry.ebx = 0;
                        entry.ecx = level;
                    }
                }
                // EDX bits 31..0 contain x2APIC ID of current logical processor
                // x2APIC increases the size of the APIC ID from 8 bits to 32 bits
                entry.edx = cpu_id as u32;
            }
            _ => (),
        }
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;
    use kvm::{Kvm, MAX_KVM_CPUID_ENTRIES};
    use kvm_sys::kvm_cpuid_entry2;

    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    #[test]
    fn test_get_max_addressable_lprocessors() {
        assert_eq!(get_max_addressable_lprocessors(1).unwrap(), 1);
        assert_eq!(get_max_addressable_lprocessors(2).unwrap(), 2);
        assert_eq!(get_max_addressable_lprocessors(4).unwrap(), 4);
        assert_eq!(get_max_addressable_lprocessors(6).unwrap(), 8);
        assert!(get_max_addressable_lprocessors(u8::max_value()).is_err());
    }

    #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
    #[test]
    fn test_filter_cpuid() {
        let kvm = Kvm::new().unwrap();
        let mut kvm_cpuid: CpuId = kvm.get_supported_cpuid(MAX_KVM_CPUID_ENTRIES).unwrap();
        match filter_cpuid(0, 1, &mut kvm_cpuid) {
            Ok(_) => (),
            _ => assert!(false),
        };

        let entries = kvm_cpuid.mut_entries_slice();
        // TODO: add tests for the other cpuid leaves
        // Test the extended topology
        // See https://www.scss.tcd.ie/~jones/CS4021/processor-identification-cpuid-instruction-note.pdf
        let leaf11_index0 = kvm_cpuid_entry2 {
            function: 11,
            index: 0,
            flags: 1,
            eax: 0,
            ebx: 1,                                              // nr of hyperthreads/core
            ecx: LEAFBH_LEVEL_TYPE_CORE << ECX_LEVEL_TYPE_SHIFT, // ECX[15:8] = 2 (Core Level)
            edx: 0,                                              // EDX = APIC ID = 0
            padding: [0, 0, 0],
        };
        assert!(entries.contains(&leaf11_index0));
        let leaf11_index1 = kvm_cpuid_entry2 {
            function: 11,
            index: 1,
            flags: 1,
            eax: LEAFBH_INDEX1_APICID_SHIFT,
            ebx: 0,
            ecx: 1, // ECX[15:8] = 0 (Invalid Level) & ECX[7:0] = 1 (Level Number)
            edx: 0, // EDX = APIC ID = 0
            padding: [0, 0, 0],
        };
        assert!(entries.contains(&leaf11_index1));
        let leaf11_index2 = kvm_cpuid_entry2 {
            function: 11,
            index: 2,
            flags: 1,
            eax: 0,
            ebx: 0, // nr of hyperthreads/core
            ecx: 2, // ECX[15:8] = 0 (Invalid Level) & ECX[7:0] = 2 (Level Number)
            edx: 0, // EDX = APIC ID = 0
            padding: [0, 0, 0],
        };
        assert!(entries.contains(&leaf11_index2));
    }
}
<|endoftext|>


<commit_before>mod types;
mod parser;

pub use self::types::{
  MAX_CHANNELS,
  ChannelAssignment, NumberType,
  Frame,
  Header, Footer,
};

pub use self::parser::frame_parser;
<commit_msg>Add subframe module to the parent frame module<commit_after>mod types;
mod parser;
pub mod subframe;

pub use self::types::{
  MAX_CHANNELS,
  ChannelAssignment, NumberType,
  Frame,
  Header, Footer,
};

pub use self::parser::frame_parser;
pub use self::subframe::SubFrame;
<|endoftext|>


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

// For lazy_static
#![allow(unsafe_code)]

use dom::TElement;
use element_state::*;
use error_reporting::{ParseErrorReporter, StdoutErrorReporter};
use media_queries::{Device, MediaType};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use restyle_hints::{ElementSnapshot, RestyleHint, DependencySet};
use selector_impl::{SelectorImplExt, ServoSelectorImpl};
use selectors::Element;
use selectors::bloom::BloomFilter;
use selectors::matching::DeclarationBlock as GenericDeclarationBlock;
use selectors::matching::{Rule, SelectorMap};
use selectors::parser::SelectorImpl;
use smallvec::VecLike;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::process;
use std::sync::Arc;
use style_traits::viewport::ViewportConstraints;
use stylesheets::{CSSRuleIteratorExt, Origin, Stylesheet};
use url::Url;
use util::opts;
use util::resource_files::read_resource_file;
use viewport::{MaybeNew, ViewportRuleCascade};

pub type DeclarationBlock = GenericDeclarationBlock<Vec<PropertyDeclaration>>;

lazy_static! {
    pub static ref USER_OR_USER_AGENT_STYLESHEETS: Vec<Stylesheet<ServoSelectorImpl>> = {
        let mut stylesheets = vec!();
        // FIXME: presentational-hints.css should be at author origin with zero specificity.
        //        (Does it make a difference?)
        for &filename in &["user-agent.css", "servo.css", "presentational-hints.css"] {
            match read_resource_file(&[filename]) {
                Ok(res) => {
                    let ua_stylesheet = Stylesheet::from_bytes(
                        &res,
                        Url::parse(&format!("chrome:///{:?}", filename)).unwrap(),
                        None,
                        None,
                        Origin::UserAgent,
                        box StdoutErrorReporter);
                    stylesheets.push(ua_stylesheet);
                }
                Err(..) => {
                    error!("Failed to load UA stylesheet {}!", filename);
                    process::exit(1);
                }
            }
        }
        for &(ref contents, ref url) in &opts::get().user_stylesheets {
            stylesheets.push(Stylesheet::from_bytes(
                &contents, url.clone(), None, None, Origin::User, box StdoutErrorReporter));
        }
        stylesheets
    };
}

lazy_static! {
    pub static ref QUIRKS_MODE_STYLESHEET: Stylesheet<ServoSelectorImpl> = {
        match read_resource_file(&["quirks-mode.css"]) {
            Ok(res) => {
                Stylesheet::from_bytes(
                    &res,
                    url!("chrome:///quirks-mode.css"),
                    None,
                    None,
                    Origin::UserAgent,
                    box StdoutErrorReporter)
            },
            Err(..) => {
                error!("Stylist failed to load 'quirks-mode.css'!");
                process::exit(1);
            }
        }
    };
}

#[derive(HeapSizeOf)]
pub struct Stylist<Impl: SelectorImplExt> {
    // Device that the stylist is currently evaluating against.
    pub device: Device,

    // Viewport constraints based on the current device.
    viewport_constraints: Option<ViewportConstraints>,

    // If true, the quirks-mode stylesheet is applied.
    quirks_mode: bool,

    // If true, the device has changed, and the stylist needs to be updated.
    is_device_dirty: bool,

    // The current selector maps, after evaluating media
    // rules against the current device.
    element_map: PerPseudoElementSelectorMap<Impl>,
    pseudos_map: HashMap<Impl::PseudoElement, PerPseudoElementSelectorMap<Impl>, BuildHasherDefault<::fnv::FnvHasher>>,
    rules_source_order: usize,

    // Selector dependencies used to compute restyle hints.
    state_deps: DependencySet<Impl>,
}

impl<Impl: SelectorImplExt> Stylist<Impl> {
    #[inline]
    pub fn new(device: Device) -> Stylist<Impl> {
        let mut stylist = Stylist {
            viewport_constraints: None,
            device: device,
            is_device_dirty: true,
            quirks_mode: false,

            element_map: PerPseudoElementSelectorMap::new(),
            pseudos_map: HashMap::with_hasher(Default::default()),
            rules_source_order: 0,
            state_deps: DependencySet::new(),
        };

        Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
            stylist.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
        });

        // FIXME: Add iso-8859-9.css when the document’s encoding is ISO-8859-8.

        stylist
    }

    pub fn update(&mut self, doc_stylesheets: &[Arc<Stylesheet<Impl>>],
                  stylesheets_changed: bool) -> bool
                  where Impl: 'static {
        if !(self.is_device_dirty || stylesheets_changed) {
            return false;
        }
        self.element_map = PerPseudoElementSelectorMap::new();
        self.pseudos_map = HashMap::with_hasher(Default::default());
        self.rules_source_order = 0;
        self.state_deps.clear();

        for ref stylesheet in Impl::get_user_or_user_agent_stylesheets().iter() {
            self.add_stylesheet(&stylesheet);
        }

        if self.quirks_mode {
            if let Some(s) = Impl::get_quirks_mode_stylesheet() {
                self.add_stylesheet(s);
            }
        }

        for ref stylesheet in doc_stylesheets.iter() {
            self.add_stylesheet(stylesheet);
        }

        self.is_device_dirty = false;
        true
    }

    fn add_stylesheet(&mut self, stylesheet: &Stylesheet<Impl>) {
        let device = &self.device;
        if !stylesheet.is_effective_for_device(device) {
            return;
        }
        let mut rules_source_order = self.rules_source_order;

        // Take apart the StyleRule into individual Rules and insert
        // them into the SelectorMap of that priority.
        macro_rules! append(
            ($style_rule: ident, $priority: ident) => {
                if $style_rule.declarations.$priority.len() > 0 {
                    for selector in &$style_rule.selectors {
                        let map = if let Some(ref pseudo) = selector.pseudo_element {
                            self.pseudos_map.entry(pseudo.clone())
                                            .or_insert_with(PerPseudoElementSelectorMap::new)
                                            .borrow_for_origin(&stylesheet.origin)
                        } else {
                            self.element_map.borrow_for_origin(&stylesheet.origin)
                        };

                        map.$priority.insert(Rule {
                                selector: selector.compound_selectors.clone(),
                                declarations: DeclarationBlock {
                                    specificity: selector.specificity,
                                    declarations: $style_rule.declarations.$priority.clone(),
                                    source_order: rules_source_order,
                                },
                        });
                    }
                }
            };
        );

        for style_rule in stylesheet.effective_rules(&self.device).style() {
            append!(style_rule, normal);
            append!(style_rule, important);
            rules_source_order += 1;
            for selector in &style_rule.selectors {
                self.state_deps.note_selector(selector.compound_selectors.clone());
            }
        }
        self.rules_source_order = rules_source_order;
    }

    pub fn compute_restyle_hint<E>(&self, element: &E,
                                   snapshot: &ElementSnapshot,
                                   // NB: We need to pass current_state as an argument because
                                   // selectors::Element doesn't provide access to ElementState
                                   // directly, and computing it from the ElementState would be
                                   // more expensive than getting it directly from the caller.
                                   current_state: ElementState)
                                   -> RestyleHint
                                   where E: Element<Impl=Impl> + Clone {
        self.state_deps.compute_hint(element, snapshot, current_state)
    }

    pub fn set_device(&mut self, mut device: Device, stylesheets: &[Arc<Stylesheet<Impl>>]) {
        let cascaded_rule = stylesheets.iter()
            .flat_map(|s| s.effective_rules(&self.device).viewport())
            .cascade();

        self.viewport_constraints = ViewportConstraints::maybe_new(device.viewport_size, &cascaded_rule);
        if let Some(ref constraints) = self.viewport_constraints {
            device = Device::new(MediaType::Screen, constraints.size);
        }

        self.is_device_dirty |= stylesheets.iter().any(|stylesheet| {
                stylesheet.rules().media().any(|media_rule|
                    media_rule.evaluate(&self.device) != media_rule.evaluate(&device))
        });

        self.device = device;
    }

    pub fn viewport_constraints(&self) -> &Option<ViewportConstraints> {
        &self.viewport_constraints
    }

    pub fn set_quirks_mode(&mut self, enabled: bool) {
        self.quirks_mode = enabled;
    }

    /// Returns the applicable CSS declarations for the given element. This corresponds to
    /// `ElementRuleCollector` in WebKit.
    ///
    /// The returned boolean indicates whether the style is *shareable*; that is, whether the
    /// matched selectors are simple enough to allow the matching logic to be reduced to the logic
    /// in `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`.
    pub fn push_applicable_declarations<E, V>(
                                        &self,
                                        element: &E,
                                        parent_bf: Option<&BloomFilter>,
                                        style_attribute: Option<&PropertyDeclarationBlock>,
                                        pseudo_element: Option<Impl::PseudoElement>,
                                        applicable_declarations: &mut V)
                                        -> bool
                                        where E: Element<Impl=Impl> + TElement,
                                              V: VecLike<DeclarationBlock> {
        assert!(!self.is_device_dirty);
        assert!(style_attribute.is_none() || pseudo_element.is_none(),
                "Style attributes do not apply to pseudo-elements");

        let map = match pseudo_element {
            Some(ref pseudo) => match self.pseudos_map.get(pseudo) {
                Some(map) => map,
                // TODO(emilio): get non eagerly-cascaded pseudo-element rules here.
                // Actually assume there are no rules applicable.
                None => return true,
            },
            None => &self.element_map,
        };

        let mut shareable = true;

        // Step 1: Normal user-agent rules.
        map.user_agent.normal.get_all_matching_rules(element,
                                                     parent_bf,
                                                     applicable_declarations,
                                                     &mut shareable);

        // Step 2: Presentational hints.
        let length = applicable_declarations.len();
        element.synthesize_presentational_hints_for_legacy_attributes(applicable_declarations);
        if applicable_declarations.len() != length {
            // Never share style for elements with preshints
            shareable = false;
        }

        // Step 3: User and author normal rules.
        map.user.normal.get_all_matching_rules(element,
                                               parent_bf,
                                               applicable_declarations,
                                               &mut shareable);
        map.author.normal.get_all_matching_rules(element,
                                                 parent_bf,
                                                 applicable_declarations,
                                                 &mut shareable);

        // Step 4: Normal style attributes.
        style_attribute.map(|sa| {
            shareable = false;
            applicable_declarations.push(
                GenericDeclarationBlock::from_declarations(sa.normal.clone()))
        });

        // Step 5: Author-supplied `!important` rules.
        map.author.important.get_all_matching_rules(element,
                                                    parent_bf,
                                                    applicable_declarations,
                                                    &mut shareable);

        // Step 6: `!important` style attributes.
        style_attribute.map(|sa| {
            shareable = false;
            applicable_declarations.push(
                GenericDeclarationBlock::from_declarations(sa.important.clone()))
        });

        // Step 7: User and UA `!important` rules.
        map.user.important.get_all_matching_rules(element,
                                                  parent_bf,
                                                  applicable_declarations,
                                                  &mut shareable);
        map.user_agent.important.get_all_matching_rules(element,
                                                        parent_bf,
                                                        applicable_declarations,
                                                        &mut shareable);

        shareable
    }

    pub fn is_device_dirty(&self) -> bool {
        self.is_device_dirty
    }
}

#[derive(HeapSizeOf)]
struct PerOriginSelectorMap<Impl: SelectorImpl> {
    normal: SelectorMap<Vec<PropertyDeclaration>, Impl>,
    important: SelectorMap<Vec<PropertyDeclaration>, Impl>,
}

impl<Impl: SelectorImpl> PerOriginSelectorMap<Impl> {
    #[inline]
    fn new() -> PerOriginSelectorMap<Impl> {
        PerOriginSelectorMap {
            normal: SelectorMap::new(),
            important: SelectorMap::new(),
        }
    }
}

#[derive(HeapSizeOf)]
struct PerPseudoElementSelectorMap<Impl: SelectorImpl> {
    user_agent: PerOriginSelectorMap<Impl>,
    author: PerOriginSelectorMap<Impl>,
    user: PerOriginSelectorMap<Impl>,
}

impl<Impl: SelectorImpl> PerPseudoElementSelectorMap<Impl> {
    #[inline]
    fn new() -> PerPseudoElementSelectorMap<Impl> {
        PerPseudoElementSelectorMap {
            user_agent: PerOriginSelectorMap::new(),
            author: PerOriginSelectorMap::new(),
            user: PerOriginSelectorMap::new(),
        }
    }

    #[inline]
    fn borrow_for_origin(&mut self, origin: &Origin) -> &mut PerOriginSelectorMap<Impl> {
        match *origin {
            Origin::UserAgent => &mut self.user_agent,
            Origin::Author => &mut self.author,
            Origin::User => &mut self.user,
        }
    }
}
<commit_msg>style: Improve style::selector_matching documentation<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// For lazy_static
#![allow(unsafe_code)]

use dom::TElement;
use element_state::*;
use error_reporting::{ParseErrorReporter, StdoutErrorReporter};
use media_queries::{Device, MediaType};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use restyle_hints::{ElementSnapshot, RestyleHint, DependencySet};
use selector_impl::{SelectorImplExt, ServoSelectorImpl};
use selectors::Element;
use selectors::bloom::BloomFilter;
use selectors::matching::DeclarationBlock as GenericDeclarationBlock;
use selectors::matching::{Rule, SelectorMap};
use selectors::parser::SelectorImpl;
use smallvec::VecLike;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::process;
use std::sync::Arc;
use style_traits::viewport::ViewportConstraints;
use stylesheets::{CSSRuleIteratorExt, Origin, Stylesheet};
use url::Url;
use util::opts;
use util::resource_files::read_resource_file;
use viewport::{MaybeNew, ViewportRuleCascade};

pub type DeclarationBlock = GenericDeclarationBlock<Vec<PropertyDeclaration>>;

lazy_static! {
    pub static ref USER_OR_USER_AGENT_STYLESHEETS: Vec<Stylesheet<ServoSelectorImpl>> = {
        let mut stylesheets = vec!();
        // FIXME: presentational-hints.css should be at author origin with zero specificity.
        //        (Does it make a difference?)
        for &filename in &["user-agent.css", "servo.css", "presentational-hints.css"] {
            match read_resource_file(&[filename]) {
                Ok(res) => {
                    let ua_stylesheet = Stylesheet::from_bytes(
                        &res,
                        Url::parse(&format!("chrome:///{:?}", filename)).unwrap(),
                        None,
                        None,
                        Origin::UserAgent,
                        box StdoutErrorReporter);
                    stylesheets.push(ua_stylesheet);
                }
                Err(..) => {
                    error!("Failed to load UA stylesheet {}!", filename);
                    process::exit(1);
                }
            }
        }
        for &(ref contents, ref url) in &opts::get().user_stylesheets {
            stylesheets.push(Stylesheet::from_bytes(
                &contents, url.clone(), None, None, Origin::User, box StdoutErrorReporter));
        }
        stylesheets
    };
}

lazy_static! {
    pub static ref QUIRKS_MODE_STYLESHEET: Stylesheet<ServoSelectorImpl> = {
        match read_resource_file(&["quirks-mode.css"]) {
            Ok(res) => {
                Stylesheet::from_bytes(
                    &res,
                    url!("chrome:///quirks-mode.css"),
                    None,
                    None,
                    Origin::UserAgent,
                    box StdoutErrorReporter)
            },
            Err(..) => {
                error!("Stylist failed to load 'quirks-mode.css'!");
                process::exit(1);
            }
        }
    };
}

/// This structure holds all the selectors and device characteristics
/// for a given document. The selectors are converted into `Rule`s
/// (defined in rust-selectors), and introduced in a `SelectorMap`
/// depending on the pseudo-element (see `PerPseudoElementSelectorMap`),
/// stylesheet origin (see `PerOriginSelectorMap`), and priority
/// (see the `normal` and `important` fields in `PerOriginSelectorMap`).
///
/// This structure is effectively created once per pipeline, in the
/// LayoutThread corresponding to that pipeline.
///
/// The stylist is parameterized on `SelectorImplExt`, a trait that extends
/// `selectors::parser::SelectorImpl`, and that allows to customise what
/// pseudo-classes and pseudo-elements are parsed. This is actually either
/// `ServoSelectorImpl`, the implementation used by Servo's layout system in
/// regular builds, or `GeckoSelectorImpl`, the implementation used in the
/// geckolib port.
#[derive(HeapSizeOf)]
pub struct Stylist<Impl: SelectorImplExt> {
    /// Device that the stylist is currently evaluating against.
    pub device: Device,

    /// Viewport constraints based on the current device.
    viewport_constraints: Option<ViewportConstraints>,

    /// If true, the quirks-mode stylesheet is applied.
    quirks_mode: bool,

    /// If true, the device has changed, and the stylist needs to be updated.
    is_device_dirty: bool,

    /// The current selector maps, after evaluating media
    /// rules against the current device.
    element_map: PerPseudoElementSelectorMap<Impl>,
    /// The selector maps corresponding to a given pseudo-element
    /// (depending on the implementation)
    pseudos_map: HashMap<Impl::PseudoElement,
                         PerPseudoElementSelectorMap<Impl>,
                         BuildHasherDefault<::fnv::FnvHasher>>,
    rules_source_order: usize,

    /// Selector dependencies used to compute restyle hints.
    state_deps: DependencySet<Impl>,
}

impl<Impl: SelectorImplExt> Stylist<Impl> {
    #[inline]
    pub fn new(device: Device) -> Stylist<Impl> {
        let mut stylist = Stylist {
            viewport_constraints: None,
            device: device,
            is_device_dirty: true,
            quirks_mode: false,

            element_map: PerPseudoElementSelectorMap::new(),
            pseudos_map: HashMap::with_hasher(Default::default()),
            rules_source_order: 0,
            state_deps: DependencySet::new(),
        };

        Impl::each_eagerly_cascaded_pseudo_element(|pseudo| {
            stylist.pseudos_map.insert(pseudo, PerPseudoElementSelectorMap::new());
        });

        // FIXME: Add iso-8859-9.css when the document’s encoding is ISO-8859-8.

        stylist
    }

    pub fn update(&mut self, doc_stylesheets: &[Arc<Stylesheet<Impl>>],
                  stylesheets_changed: bool) -> bool
                  where Impl: 'static {
        if !(self.is_device_dirty || stylesheets_changed) {
            return false;
        }

        self.element_map = PerPseudoElementSelectorMap::new();
        self.pseudos_map = HashMap::with_hasher(Default::default());
        self.rules_source_order = 0;
        self.state_deps.clear();

        for ref stylesheet in Impl::get_user_or_user_agent_stylesheets().iter() {
            self.add_stylesheet(&stylesheet);
        }

        if self.quirks_mode {
            if let Some(s) = Impl::get_quirks_mode_stylesheet() {
                self.add_stylesheet(s);
            }
        }

        for ref stylesheet in doc_stylesheets.iter() {
            self.add_stylesheet(stylesheet);
        }

        self.is_device_dirty = false;
        true
    }

    fn add_stylesheet(&mut self, stylesheet: &Stylesheet<Impl>) {
        let device = &self.device;
        if !stylesheet.is_effective_for_device(device) {
            return;
        }
        let mut rules_source_order = self.rules_source_order;

        // Take apart the StyleRule into individual Rules and insert
        // them into the SelectorMap of that priority.
        macro_rules! append(
            ($style_rule: ident, $priority: ident) => {
                if !$style_rule.declarations.$priority.is_empty() {
                    for selector in &$style_rule.selectors {
                        let map = if let Some(ref pseudo) = selector.pseudo_element {
                            self.pseudos_map.entry(pseudo.clone())
                                            .or_insert_with(PerPseudoElementSelectorMap::new)
                                            .borrow_for_origin(&stylesheet.origin)
                        } else {
                            self.element_map.borrow_for_origin(&stylesheet.origin)
                        };

                        map.$priority.insert(Rule {
                                selector: selector.compound_selectors.clone(),
                                declarations: DeclarationBlock {
                                    specificity: selector.specificity,
                                    declarations: $style_rule.declarations.$priority.clone(),
                                    source_order: rules_source_order,
                                },
                        });
                    }
                }
            };
        );

        for style_rule in stylesheet.effective_rules(&self.device).style() {
            append!(style_rule, normal);
            append!(style_rule, important);
            rules_source_order += 1;
            for selector in &style_rule.selectors {
                self.state_deps.note_selector(selector.compound_selectors.clone());
            }
        }
        self.rules_source_order = rules_source_order;
    }

    pub fn compute_restyle_hint<E>(&self, element: &E,
                                   snapshot: &ElementSnapshot,
                                   // NB: We need to pass current_state as an argument because
                                   // selectors::Element doesn't provide access to ElementState
                                   // directly, and computing it from the ElementState would be
                                   // more expensive than getting it directly from the caller.
                                   current_state: ElementState)
                                   -> RestyleHint
                                   where E: Element<Impl=Impl> + Clone {
        self.state_deps.compute_hint(element, snapshot, current_state)
    }

    pub fn set_device(&mut self, mut device: Device, stylesheets: &[Arc<Stylesheet<Impl>>]) {
        let cascaded_rule = stylesheets.iter()
            .flat_map(|s| s.effective_rules(&self.device).viewport())
            .cascade();

        self.viewport_constraints = ViewportConstraints::maybe_new(device.viewport_size, &cascaded_rule);
        if let Some(ref constraints) = self.viewport_constraints {
            device = Device::new(MediaType::Screen, constraints.size);
        }

        self.is_device_dirty |= stylesheets.iter().any(|stylesheet| {
                stylesheet.rules().media().any(|media_rule|
                    media_rule.evaluate(&self.device) != media_rule.evaluate(&device))
        });

        self.device = device;
    }

    pub fn viewport_constraints(&self) -> &Option<ViewportConstraints> {
        &self.viewport_constraints
    }

    pub fn set_quirks_mode(&mut self, enabled: bool) {
        self.quirks_mode = enabled;
    }

    /// Returns the applicable CSS declarations for the given element.
    /// This corresponds to `ElementRuleCollector` in WebKit.
    ///
    /// The returned boolean indicates whether the style is *shareable*;
    /// that is, whether the matched selectors are simple enough to allow the
    /// matching logic to be reduced to the logic in
    /// `css::matching::PrivateMatchMethods::candidate_element_allows_for_style_sharing`.
    pub fn push_applicable_declarations<E, V>(
                                        &self,
                                        element: &E,
                                        parent_bf: Option<&BloomFilter>,
                                        style_attribute: Option<&PropertyDeclarationBlock>,
                                        pseudo_element: Option<Impl::PseudoElement>,
                                        applicable_declarations: &mut V)
                                        -> bool
                                        where E: Element<Impl=Impl> + TElement,
                                              V: VecLike<DeclarationBlock> {
        assert!(!self.is_device_dirty);
        assert!(style_attribute.is_none() || pseudo_element.is_none(),
                "Style attributes do not apply to pseudo-elements");

        let map = match pseudo_element {
            Some(ref pseudo) => match self.pseudos_map.get(pseudo) {
                Some(map) => map,
                // TODO(emilio): get non eagerly-cascaded pseudo-element rules here.
                // Actually assume there are no rules applicable.
                None => return true,
            },
            None => &self.element_map,
        };

        let mut shareable = true;

        // Step 1: Normal user-agent rules.
        map.user_agent.normal.get_all_matching_rules(element,
                                                     parent_bf,
                                                     applicable_declarations,
                                                     &mut shareable);

        // Step 2: Presentational hints.
        let length = applicable_declarations.len();
        element.synthesize_presentational_hints_for_legacy_attributes(applicable_declarations);
        if applicable_declarations.len() != length {
            // Never share style for elements with preshints
            shareable = false;
        }

        // Step 3: User and author normal rules.
        map.user.normal.get_all_matching_rules(element,
                                               parent_bf,
                                               applicable_declarations,
                                               &mut shareable);
        map.author.normal.get_all_matching_rules(element,
                                                 parent_bf,
                                                 applicable_declarations,
                                                 &mut shareable);

        // Step 4: Normal style attributes.
        style_attribute.map(|sa| {
            shareable = false;
            applicable_declarations.push(
                GenericDeclarationBlock::from_declarations(sa.normal.clone()))
        });

        // Step 5: Author-supplied `!important` rules.
        map.author.important.get_all_matching_rules(element,
                                                    parent_bf,
                                                    applicable_declarations,
                                                    &mut shareable);

        // Step 6: `!important` style attributes.
        style_attribute.map(|sa| {
            shareable = false;
            applicable_declarations.push(
                GenericDeclarationBlock::from_declarations(sa.important.clone()))
        });

        // Step 7: User and UA `!important` rules.
        map.user.important.get_all_matching_rules(element,
                                                  parent_bf,
                                                  applicable_declarations,
                                                  &mut shareable);
        map.user_agent.important.get_all_matching_rules(element,
                                                        parent_bf,
                                                        applicable_declarations,
                                                        &mut shareable);

        shareable
    }

    #[inline]
    pub fn is_device_dirty(&self) -> bool {
        self.is_device_dirty
    }
}

/// Map that contains the CSS rules for a given origin.
#[derive(HeapSizeOf)]
struct PerOriginSelectorMap<Impl: SelectorImpl> {
    /// Rules that contains at least one property declararion with
    /// normal importance.
    normal: SelectorMap<Vec<PropertyDeclaration>, Impl>,
    /// Rules that contains at least one property declararion with
    /// !important.
    important: SelectorMap<Vec<PropertyDeclaration>, Impl>,
}

impl<Impl: SelectorImpl> PerOriginSelectorMap<Impl> {
    #[inline]
    fn new() -> PerOriginSelectorMap<Impl> {
        PerOriginSelectorMap {
            normal: SelectorMap::new(),
            important: SelectorMap::new(),
        }
    }
}

/// Map that contains the CSS rules for a specific PseudoElement
/// (or lack of PseudoElement).
#[derive(HeapSizeOf)]
struct PerPseudoElementSelectorMap<Impl: SelectorImpl> {
    /// Rules from user agent stylesheets
    user_agent: PerOriginSelectorMap<Impl>,
    /// Rules from author stylesheets
    author: PerOriginSelectorMap<Impl>,
    /// Rules from user stylesheets
    user: PerOriginSelectorMap<Impl>,
}

impl<Impl: SelectorImpl> PerPseudoElementSelectorMap<Impl> {
    #[inline]
    fn new() -> PerPseudoElementSelectorMap<Impl> {
        PerPseudoElementSelectorMap {
            user_agent: PerOriginSelectorMap::new(),
            author: PerOriginSelectorMap::new(),
            user: PerOriginSelectorMap::new(),
        }
    }

    #[inline]
    fn borrow_for_origin(&mut self, origin: &Origin) -> &mut PerOriginSelectorMap<Impl> {
        match *origin {
            Origin::UserAgent => &mut self.user_agent,
            Origin::Author => &mut self.author,
            Origin::User => &mut self.user,
        }
    }
}
<|endoftext|>


<commit_before>//! # OpenTelemetry Propagator interface
//!
//! Propagators API consists of two main formats:
//!
//! - `BinaryFormat` is used to serialize and deserialize a value
//! into a binary representation.
//! - `TextMapFormat` is used to inject and extract a value as
//! text into injectors and extractors that travel in-band across process boundaries.
//!
//! Deserializing must set `is_remote` to true on the returned
//! `SpanContext`.
//!
//! ## Binary Format
//!
//! `BinaryFormat` is a formatter to serialize and deserialize a value
//! into a binary format.
//!
//! `BinaryFormat` MUST expose the APIs that serializes values into bytes,
//! and deserializes values from bytes.
//!
//! ### ToBytes
//!
//! Serializes the given value into the on-the-wire representation.
//!
//! Required arguments:
//!
//! - the value to serialize, can be `SpanContext` or `DistributedContext`.
//!
//! Returns the on-the-wire byte representation of the value.
//!
//! ### FromBytes
//!
//! Creates a value from the given on-the-wire encoded representation.
//!
//! If the value could not be parsed, the underlying implementation
//! SHOULD decide to return ether an empty value, an invalid value, or
//! a valid value.
//!
//! Required arguments:
//!
//! - on-the-wire byte representation of the value.
//!
//! Returns a value deserialized from bytes.
//!
//! ## TextMap Format
//!
//! `TextMapFormat` is a formatter that injects and extracts a value
//! as text into injectors and extractors that travel in-band across process boundaries.
//!
//! Encoding is expected to conform to the HTTP Header Field semantics.
//! Values are often encoded as RPC/HTTP request headers.
//!
//! The carrier of propagated data on both the client (injector) and
//! server (extractor) side is usually a http request. Propagation is
//! usually implemented via library-specific request interceptors, where
//! the client-side injects values and the server-side extracts them.
//!
//! `TextMapFormat` MUST expose the APIs that injects values into injectors,
//! and extracts values from extractors.
//!
//! ### Fields
//!
//! The propagation fields defined. If your injector is reused, you should
//! delete the fields here before calling `inject`.
//!
//! For example, if the injector is a single-use or immutable request object,
//! you don't need to clear fields as they couldn't have been set before.
//! If it is a mutable, retryable object, successive calls should clear
//! these fields first.
//!
//! The use cases of this are:
//!
//! - allow pre-allocation of fields, especially in systems like gRPC
//! Metadata
//! - allow a single-pass over an iterator
//!
//! Returns list of fields that will be used by this formatter.
//!
//! ### Inject
//!
//! Injects the value downstream. For example, as http headers.
//!
//! Required arguments:
//!
//! - the `SpanContext` to be injected.
//! - the injector that holds propagation fields. For example, an outgoing
//! message or http request.
//! - the `Setter` invoked for each propagation key to add or remove.
//!
//! #### Setter argument
//!
//! Setter is an argument in `Inject` that puts value into given field.
//!
//! `Setter` allows a `TextMapFormat` to set propagated fields into a
//! injector.
//!
//! `Setter` MUST be stateless and allowed to be saved as a constant to
//! avoid runtime allocations. One of the ways to implement it is `Setter`
//! class with `Put` method as described below.
//!
//! ##### Put
//!
//! Replaces a propagated field with the given value.
//!
//! Required arguments:
//!
//! - the injector holds propagation fields. For example, an outgoing message
//! or http request.
//! - the key of the field.
//! - the value of the field.
//!
//! The implementation SHOULD preserve casing (e.g. it should not transform
//! `Content-Type` to `content-type`) if the used protocol is case insensitive,
//! otherwise it MUST preserve casing.
//!
//! ### Extract
//!
//! Extracts the value from upstream. For example, as http headers.
//!
//! If the value could not be parsed, the underlying implementation will
//! decide to return an object representing either an empty value, an invalid
//! value, or a valid value.
//!
//! Required arguments:
//!
//! - the extractor holds propagation fields. For example, an outgoing message
//! or http request.
//! - the instance of `Getter` invoked for each propagation key to get.
//!
//! Returns the non-null extracted value.
//!
//! #### Getter argument
//!
//! Getter is an argument in `Extract` that get value from given field
//!
//! `Getter` allows a `TextMapFormat` to read propagated fields from a
//! extractor.
//!
//! `Getter` MUST be stateless and allowed to be saved as a constant to avoid
//! runtime allocations. One of the ways to implement it is `Getter` class
//! with `Get` method as described below.
//!
//! ##### Get
//!
//!  The Get function MUST return the first value of the given propagation
//! key or return `None` if the key doesn't exist.
//!
//! Required arguments:
//!
//! - the extractor of propagation fields, such as an HTTP request.
//! - the key of the field.
//!
//! The `get` function is responsible for handling case sensitivity. If
//! the getter is intended to work with an HTTP request object, the getter
//! MUST be case insensitive. To improve compatibility with other text-based
//! protocols, text format implementations MUST ensure to always use the
//! canonical casing for their attributes. NOTE: Canonical casing for HTTP
//! headers is usually title case (e.g. `Content-Type` instead of `content-type`).
//!
use crate::api;
use std::collections::HashMap;

pub mod composite_propagator;
pub mod text_propagator;

/// Injector provides an interface for adding fields from an underlying struct like `HashMap`
pub trait Injector {
    /// Add a key and value to the underlying.
    fn set(&mut self, key: &str, value: String);
}

/// Extractor provides an interface for removing fields from an underlying struct like `HashMap`
pub trait Extractor {
    /// Get a value from a key from the underlying data.
    fn get(&self, key: &str) -> Option<&str>;
}

impl<S: std::hash::BuildHasher> api::Injector for HashMap<String, String, S> {
    /// Set a key and value in the HashMap.
    fn set(&mut self, key: &str, value: String) {
        self.insert(key.to_lowercase(), value);
    }
}

impl<S: std::hash::BuildHasher> api::Extractor for HashMap<String, String, S> {
    /// Get a value for a key from the HashMap.
    fn get(&self, key: &str) -> Option<&str> {
        self.get(&key.to_lowercase()).map(|v| v.as_str())
    }
}

#[cfg(feature = "http")]
impl api::Injector for http::HeaderMap {
    /// Set a key and value in the HeaderMap.  Does nothing if the key or value are not valid inputs.
    fn set(&mut self, key: &str, value: String) {
        if let Ok(name) = http::header::HeaderName::from_bytes(key.as_bytes()) {
            if let Ok(val) = http::header::HeaderValue::from_str(&value) {
                self.insert(name, val);
            }
        }
    }
}

#[cfg(feature = "http")]
impl api::Extractor for http::HeaderMap {
    /// Get a value for a key from the HeaderMap.  If the value is not valid ASCII, returns None.
    fn get(&self, key: &str) -> Option<&str> {
        self.get(key).and_then(|value| value.to_str().ok())
    }
}

#[cfg(feature = "tonic")]
impl api::Injector for tonic::metadata::MetadataMap {
    /// Set a key and value in the MetadataMap.  Does nothing if the key or value are not valid inputs
    fn set(&mut self, key: &str, value: String) {
        if let Ok(key) = tonic::metadata::MetadataKey::from_bytes(key.as_bytes()) {
            if let Ok(val) = tonic::metadata::MetadataValue::from_str(&value) {
                self.insert(key, val);
            }
        }
    }
}

#[cfg(feature = "tonic")]
impl api::Extractor for tonic::metadata::MetadataMap {
    /// Get a value for a key from the MetadataMap.  If the value can't be converted to &str, returns None
    fn get(&self, key: &str) -> Option<&str> {
        self.get(key).and_then(|metadata| metadata.to_str().ok())
    }
}

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

    #[test]
    fn hash_map() {
        let mut carrier = HashMap::new();
        carrier.set("headerName", "value".to_string());

        assert_eq!(
            Extractor::get(&carrier, "HEADERNAME"),
            Some("value"),
            "case insensitive extraction"
        )
    }

    #[test]
    #[cfg(feature = "http")]
    fn http_headers() {
        let mut carrier = http::HeaderMap::new();
        carrier.set("headerName", "value".to_string());

        assert_eq!(
            Extractor::get(&carrier, "HEADERNAME"),
            Some("value"),
            "case insensitive extraction"
        )
    }

    #[test]
    #[cfg(feature = "tonic")]
    fn tonic_headers() {
        let mut carrier = tonic::metadata::MetadataMap::new();
        carrier.set("headerName", "value".to_string());

        assert_eq!(
            Extractor::get(&carrier, "HEADERNAME"),
            Some("value"),
            "case insensitive extraction"
        )
    }
}
<commit_msg>Add keys method to extractors (#209)<commit_after>//! # OpenTelemetry Propagator interface
//!
//! Propagators API consists of two main formats:
//!
//! - `BinaryFormat` is used to serialize and deserialize a value
//! into a binary representation.
//! - `TextMapFormat` is used to inject and extract a value as
//! text into injectors and extractors that travel in-band across process boundaries.
//!
//! Deserializing must set `is_remote` to true on the returned
//! `SpanContext`.
//!
//! ## Binary Format
//!
//! `BinaryFormat` is a formatter to serialize and deserialize a value
//! into a binary format.
//!
//! `BinaryFormat` MUST expose the APIs that serializes values into bytes,
//! and deserializes values from bytes.
//!
//! ### ToBytes
//!
//! Serializes the given value into the on-the-wire representation.
//!
//! Required arguments:
//!
//! - the value to serialize, can be `SpanContext` or `DistributedContext`.
//!
//! Returns the on-the-wire byte representation of the value.
//!
//! ### FromBytes
//!
//! Creates a value from the given on-the-wire encoded representation.
//!
//! If the value could not be parsed, the underlying implementation
//! SHOULD decide to return ether an empty value, an invalid value, or
//! a valid value.
//!
//! Required arguments:
//!
//! - on-the-wire byte representation of the value.
//!
//! Returns a value deserialized from bytes.
//!
//! ## TextMap Format
//!
//! `TextMapFormat` is a formatter that injects and extracts a value
//! as text into injectors and extractors that travel in-band across process boundaries.
//!
//! Encoding is expected to conform to the HTTP Header Field semantics.
//! Values are often encoded as RPC/HTTP request headers.
//!
//! The carrier of propagated data on both the client (injector) and
//! server (extractor) side is usually a http request. Propagation is
//! usually implemented via library-specific request interceptors, where
//! the client-side injects values and the server-side extracts them.
//!
//! `TextMapFormat` MUST expose the APIs that injects values into injectors,
//! and extracts values from extractors.
//!
//! ### Fields
//!
//! The propagation fields defined. If your injector is reused, you should
//! delete the fields here before calling `inject`.
//!
//! For example, if the injector is a single-use or immutable request object,
//! you don't need to clear fields as they couldn't have been set before.
//! If it is a mutable, retryable object, successive calls should clear
//! these fields first.
//!
//! The use cases of this are:
//!
//! - allow pre-allocation of fields, especially in systems like gRPC
//! Metadata
//! - allow a single-pass over an iterator
//!
//! Returns list of fields that will be used by this formatter.
//!
//! ### Inject
//!
//! Injects the value downstream. For example, as http headers.
//!
//! Required arguments:
//!
//! - the `SpanContext` to be injected.
//! - the injector that holds propagation fields. For example, an outgoing
//! message or http request.
//! - the `Setter` invoked for each propagation key to add or remove.
//!
//! #### Setter argument
//!
//! Setter is an argument in `Inject` that puts value into given field.
//!
//! `Setter` allows a `TextMapFormat` to set propagated fields into a
//! injector.
//!
//! `Setter` MUST be stateless and allowed to be saved as a constant to
//! avoid runtime allocations. One of the ways to implement it is `Setter`
//! class with `Put` method as described below.
//!
//! ##### Put
//!
//! Replaces a propagated field with the given value.
//!
//! Required arguments:
//!
//! - the injector holds propagation fields. For example, an outgoing message
//! or http request.
//! - the key of the field.
//! - the value of the field.
//!
//! The implementation SHOULD preserve casing (e.g. it should not transform
//! `Content-Type` to `content-type`) if the used protocol is case insensitive,
//! otherwise it MUST preserve casing.
//!
//! ### Extract
//!
//! Extracts the value from upstream. For example, as http headers.
//!
//! If the value could not be parsed, the underlying implementation will
//! decide to return an object representing either an empty value, an invalid
//! value, or a valid value.
//!
//! Required arguments:
//!
//! - the extractor holds propagation fields. For example, an outgoing message
//! or http request.
//! - the instance of `Getter` invoked for each propagation key to get.
//!
//! Returns the non-null extracted value.
//!
//! #### Getter argument
//!
//! Getter is an argument in `Extract` that get value from given field
//!
//! `Getter` allows a `TextMapFormat` to read propagated fields from a
//! extractor.
//!
//! `Getter` MUST be stateless and allowed to be saved as a constant to avoid
//! runtime allocations. One of the ways to implement it is `Getter` class
//! with `Get` method as described below.
//!
//! ##### Get
//!
//! The Get function MUST return the first value of the given propagation
//! key or return `None` if the key doesn't exist.
//!
//! Required arguments:
//!
//! - the extractor of propagation fields, such as an HTTP request.
//! - the key of the field.
//!
//! The `get` function is responsible for handling case sensitivity. If
//! the getter is intended to work with an HTTP request object, the getter
//! MUST be case insensitive. To improve compatibility with other text-based
//! protocols, text format implementations MUST ensure to always use the
//! canonical casing for their attributes. NOTE: Canonical casing for HTTP
//! headers is usually title case (e.g. `Content-Type` instead of `content-type`).
//!
//! ##### Keys
//!
//! The Keys function returns a vector of the propagation keys.
//!
use crate::api;
use std::collections::HashMap;

pub mod composite_propagator;
pub mod text_propagator;

/// Injector provides an interface for adding fields from an underlying struct like `HashMap`
pub trait Injector {
    /// Add a key and value to the underlying data.
    fn set(&mut self, key: &str, value: String);
}

/// Extractor provides an interface for removing fields from an underlying struct like `HashMap`
pub trait Extractor {
    /// Get a value from a key from the underlying data.
    fn get(&self, key: &str) -> Option<&str>;

    /// Collect all the keys from the underlying data.
    fn keys(&self) -> Vec<&str>;
}

impl<S: std::hash::BuildHasher> api::Injector for HashMap<String, String, S> {
    /// Set a key and value in the HashMap.
    fn set(&mut self, key: &str, value: String) {
        self.insert(key.to_lowercase(), value);
    }
}

impl<S: std::hash::BuildHasher> api::Extractor for HashMap<String, String, S> {
    /// Get a value for a key from the HashMap.
    fn get(&self, key: &str) -> Option<&str> {
        self.get(&key.to_lowercase()).map(|v| v.as_str())
    }

    /// Collect all the keys from the HashMap.
    fn keys(&self) -> Vec<&str> {
        self.keys().map(|k| k.as_str()).collect::<Vec<_>>()
    }
}

#[cfg(feature = "http")]
impl api::Injector for http::HeaderMap {
    /// Set a key and value in the HeaderMap.  Does nothing if the key or value are not valid inputs.
    fn set(&mut self, key: &str, value: String) {
        if let Ok(name) = http::header::HeaderName::from_bytes(key.as_bytes()) {
            if let Ok(val) = http::header::HeaderValue::from_str(&value) {
                self.insert(name, val);
            }
        }
    }
}

#[cfg(feature = "http")]
impl api::Extractor for http::HeaderMap {
    /// Get a value for a key from the HeaderMap.  If the value is not valid ASCII, returns None.
    fn get(&self, key: &str) -> Option<&str> {
        self.get(key).and_then(|value| value.to_str().ok())
    }

    /// Collect all the keys from the HeaderMap.
    fn keys(&self) -> Vec<&str> {
        self.keys().map(|value| value.as_str()).collect::<Vec<_>>()
    }
}

#[cfg(feature = "tonic")]
impl api::Injector for tonic::metadata::MetadataMap {
    /// Set a key and value in the MetadataMap.  Does nothing if the key or value are not valid inputs
    fn set(&mut self, key: &str, value: String) {
        if let Ok(key) = tonic::metadata::MetadataKey::from_bytes(key.as_bytes()) {
            if let Ok(val) = tonic::metadata::MetadataValue::from_str(&value) {
                self.insert(key, val);
            }
        }
    }
}

#[cfg(feature = "tonic")]
impl api::Extractor for tonic::metadata::MetadataMap {
    /// Get a value for a key from the MetadataMap.  If the value can't be converted to &str, returns None
    fn get(&self, key: &str) -> Option<&str> {
        self.get(key).and_then(|metadata| metadata.to_str().ok())
    }

    /// Collect all the keys from the MetadataMap.
    fn keys(&self) -> Vec<&str> {
        self.keys().map(|key| match key {
            tonic::metadata::KeyRef::Ascii(v) => v.as_str(),
            tonic::metadata::KeyRef::Binary(v) => v.as_str(),
        }).collect::<Vec<_>>()
    }
}

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

    #[test]
    fn hash_map_get() {
        let mut carrier = HashMap::new();
        carrier.set("headerName", "value".to_string());

        assert_eq!(
            Extractor::get(&carrier, "HEADERNAME"),
            Some("value"),
            "case insensitive extraction"
        );
    }

    #[test]
    fn hash_map_keys() {
        let mut carrier = HashMap::new();
        carrier.set("headerName1", "value1".to_string());
        carrier.set("headerName2", "value2".to_string());

        let got = Extractor::keys(&carrier);
        assert_eq!(got.len(), 2);
        assert!(got.contains(&"headername1"));
        assert!(got.contains(&"headername2"));
    }

    #[test]
    #[cfg(feature = "http")]
    fn http_headers_get() {
        let mut carrier = http::HeaderMap::new();
        carrier.set("headerName", "value".to_string());

        assert_eq!(
            Extractor::get(&carrier, "HEADERNAME"),
            Some("value"),
            "case insensitive extraction"
        )
    }

    #[test]
    #[cfg(feature = "http")]
    fn http_headers_keys() {
        let mut carrier = http::HeaderMap::new();
        carrier.set("headerName1", "value1".to_string());
        carrier.set("headerName2", "value2".to_string());

        let got = Extractor::keys(&carrier);
        assert_eq!(got.len(), 2);
        assert!(got.contains(&"headername1"));
        assert!(got.contains(&"headername2"));
    }

    #[test]
    #[cfg(feature = "tonic")]
    fn tonic_headers_get() {
        let mut carrier = tonic::metadata::MetadataMap::new();
        carrier.set("headerName", "value".to_string());

        assert_eq!(
            Extractor::get(&carrier, "HEADERNAME"),
            Some("value"),
            "case insensitive extraction"
        )
    }

    #[test]
    #[cfg(feature = "tonic")]
    fn tonic_headers_keys() {
        let mut carrier = tonic::metadata::MetadataMap::new();
        carrier.set("headerName1", "value1".to_string());
        carrier.set("headerName2", "value2".to_string());

        let got = Extractor::keys(&carrier);
        assert_eq!(got.len(), 2);
        assert!(got.contains(&"headername1"));
        assert!(got.contains(&"headername2"));
    }
}
<|endoftext|>