<commit_before>use clap::{crate_version, value_t};

use std::env;
use std::ffi::OsStr;
use std::io;
use std::process::{self, Command, ExitStatus};
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::{Duration, Instant};

#[path = "../common.rs"]
mod common;

fn main() {
    if !common::archive_file_path().exists() {
        let version = common::afl_rustc_version();
        eprintln!(
            "AFL LLVM runtime is not built with Rust {}, run `cargo \
             install --force afl` to build it.",
            version
        );
        process::exit(1);
    }

    let app_matches = clap_app().get_matches();
    // This unwrap is okay because we set SubcommandRequiredElseHelp at the top level, and afl is
    // the only subcommand
    let afl_matches = app_matches.subcommand_matches("afl").unwrap();

    match afl_matches.subcommand() {
        ("analyze", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-analyze args")
                .unwrap_or_default();
            run_afl(args, "afl-analyze", None);
        }
        ("cmin", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-cmin args")
                .unwrap_or_default();
            run_afl(args, "afl-cmin", None);
        }
        ("fuzz", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-fuzz args")
                .unwrap_or_default();
            let timeout = sub_matches
                .value_of("max_total_time")
                .map(|_| value_t!(sub_matches, "max_total_time", u64).unwrap_or_else(|e| e.exit()));
            run_afl(args, "afl-fuzz", timeout);
        }
        ("gotcpu", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-gotcpu args")
                .unwrap_or_default();
            run_afl(args, "afl-gotcpu", None);
        }
        ("plot", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-plot args")
                .unwrap_or_default();
            run_afl(args, "afl-plot", None);
        }
        ("showmap", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-showmap args")
                .unwrap_or_default();
            run_afl(args, "afl-showmap", None);
        }
        ("tmin", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-tmin args")
                .unwrap_or_default();
            run_afl(args, "afl-tmin", None);
        }
        ("whatsup", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-whatsup args")
                .unwrap_or_default();
            run_afl(args, "afl-whatsup", None);
        }
        (subcommand, Some(sub_matches)) => {
            let args = sub_matches.values_of_os("").unwrap_or_default();
            run_cargo(subcommand, args);
        }
        // unreachable due to SubcommandRequiredElseHelp on "afl" subcommand
        (_, None) => unreachable!(),
    }
}

fn clap_app() -> clap::App<'static, 'static> {
    use clap::{
        App,
        AppSettings::{
            AllowExternalSubcommands, AllowLeadingHyphen, DisableHelpFlags, DisableHelpSubcommand,
            DisableVersion, SubcommandRequiredElseHelp,
        },
        Arg, SubCommand,
    };

    App::new("cargo afl")
        .bin_name("cargo")
        .setting(SubcommandRequiredElseHelp)
        .subcommand(
            SubCommand::with_name("afl")
                .version(crate_version!())
                .setting(SubcommandRequiredElseHelp)
                .setting(AllowExternalSubcommands)
                .usage("cargo afl [SUBCOMMAND or Cargo SUBCOMMAND]")
                .after_help(
                    "In addition to the subcommands above, Cargo subcommands are also \
                 supported (see `cargo help` for a list of all Cargo subcommands).",
                )
                .subcommand(
                    SubCommand::with_name("analyze")
                        .about("Invoke afl-analyze")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-analyze args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("cmin")
                        .about("Invoke afl-cmin")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-cmin args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("fuzz")
                        .about("Invoke afl-fuzz")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(
                            Arg::with_name("max_total_time")
                                .long("max_total_time")
                                .takes_value(true)
                                .help("Maximum amount of time to run the fuzzer"),
                        )
                        .arg(Arg::with_name("afl-fuzz args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("gotcpu")
                        .about("Invoke afl-gotcpu")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-gotcpu args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("plot")
                        .about("Invoke afl-plot")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-plot args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("showmap")
                        .about("Invoke afl-showmap")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-showmap args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("tmin")
                        .about("Invoke afl-tmin")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-tmin args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("whatsup")
                        .about("Invoke afl-whatsup")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-whatsup args").multiple(true)),
                ),
        )
}

fn run_timeout_terminate(mut cmd: Command, timeout: Option<u64>) -> Result<ExitStatus, io::Error> {
    let timeout = match timeout {
        Some(timeout) => Duration::from_secs(timeout),
        None => return cmd.status(),
    };
    let start_time = Instant::now();

    let mut child = cmd.spawn()?;
    let pid = child.id();

    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let (stop_mutex, condvar) = &*pair;
    let thread_handle = {
        let pair = pair.clone();
        thread::spawn(move || -> Result<(), io::Error> {
            // This thread will wait until the child process has exited, or the
            // timeout has elapsed, whichever comes first. If the timeout
            // elapses, and the process is still running, it will send SIGTERM
            // to the child process.

            let (stop_mutex, condvar) = &*pair;
            let mut stop = stop_mutex.lock().unwrap();
            loop {
                let elapsed = Instant::now() - start_time;
                if elapsed >= timeout {
                    break;
                }

                let dur = timeout - elapsed;
                let results = condvar.wait_timeout(stop, dur).unwrap();
                stop = results.0;
                if *stop {
                    // Blocking waitid call on the main thread has returned,
                    // thus the child process has terminated
                    return Ok(());
                }
                if results.1.timed_out() {
                    break;
                }
            }

            // Since the waitid call on the main thread is using WNOWAIT, the
            // child process won't be cleaned up (until after this thread
            // exits and the main thread calls wait) and thus its PID won't be
            // reused by another, unrelated process.
            unsafe {
                let ret = libc::kill(pid as i32, libc::SIGTERM);
                if ret == -1 {
                    Err(io::Error::last_os_error())?
                }
            }

            Ok(())
        })
    };

    unsafe {
        // Block until the child process terminates, but leave it in a waitable
        // state still
        let ret = libc::waitid(
            libc::P_PID,
            pid,
            std::ptr::null_mut(),
            libc::WEXITED | libc::WNOWAIT,
        );
        if ret == -1 {
            Err(io::Error::last_os_error())?
        }
    }
    {
        let mut stop = stop_mutex.lock().unwrap();
        *stop = true;
    }
    // Tell the timeout thread to stop, wake it, and wait for it to exit
    condvar.notify_one();
    thread_handle.join().unwrap()?;

    // Clean up zombie and get exit status (this won't block, because the child
    // process has terminated and is still waitable)
    child.wait()
}

fn run_afl<I, S>(args: I, cmd: &str, timeout: Option<u64>)
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let cmd_path = common::afl_dir().join("bin").join(cmd);
    let mut cmd = Command::new(cmd_path);
    cmd.args(args);
    let status = run_timeout_terminate(cmd, timeout).unwrap();
    process::exit(status.code().unwrap_or(1));
}

fn run_cargo<I, S>(subcommand: &str, args: I)
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let cargo_path = env::var("CARGO").expect("Could not determine `cargo` path");

    // add some flags to sanitizers to make them work with Rust code
    let asan_options = env::var("ASAN_OPTIONS").unwrap_or_default();
    let asan_options = format!(
        "detect_odr_violation=0:abort_on_error=1:symbolize=0:{}",
        asan_options
    );

    let tsan_options = env::var("TSAN_OPTIONS").unwrap_or_default();
    let tsan_options = format!("report_signal_unsafe=0:{}", tsan_options);

    // The new LLVM pass manager was not enabled in rustc 1.57 as expected:
    // https://github.com/rust-lang/rust/pull/91263
    // The fix for now is to only pass `-C passes=sancov-module` to nightly
    // compilers for which the LLVM version is >= 13.

    let version_meta = rustc_version::version_meta().unwrap();
    let passes = if is_nightly() && version_meta.llvm_version.map_or(true, |v| v.major >= 13) {
        "sancov-module"
    } else {
        "sancov"
    };

    // `-C codegen-units=1` is needed to work around link errors
    // https://github.com/rust-fuzz/afl.rs/pull/193#issuecomment-933550430
    let mut rustflags = format!(
        "--cfg fuzzing \
         -C debug-assertions \
         -C overflow_checks \
         -C passes={} \
         -C codegen-units=1 \
         -C llvm-args=-sanitizer-coverage-level=3 \
         -C llvm-args=-sanitizer-coverage-trace-pc-guard \
         -C llvm-args=-sanitizer-coverage-prune-blocks=0 \
         -C opt-level=3 \
         -C target-cpu=native \
         -C debuginfo=0 \
         -l afl-llvm-rt \
         -L {} ",
        passes,
        common::afl_llvm_rt_dir().display()
    );

    // RUSTFLAGS are not used by rustdoc, instead RUSTDOCFLAGS are used. Since
    // doctests will try to link against afl-llvm-rt, set up RUSTDOCFLAGS to
    // have doctests built the same as other code to avoid issues with doctests.
    let mut rustdocflags = format!(
        "--cfg fuzzing \
         -C debug-assertions \
         -C overflow_checks \
         -C passes={} \
         -C codegen-units=1 \
         -C llvm-args=-sanitizer-coverage-level=3 \
         -C llvm-args=-sanitizer-coverage-trace-pc-guard \
         -C llvm-args=-sanitizer-coverage-prune-blocks=0 \
         -C opt-level=3 \
         -C target-cpu=native \
         -C debuginfo=0 \
         -L {} ",
        passes,
        common::afl_llvm_rt_dir().display()
    );

    if cfg!(target_os = "linux") {
        // work around https://github.com/rust-fuzz/afl.rs/issues/141 /
        // https://github.com/rust-lang/rust/issues/53945, can be removed once
        // those are fixed.
        rustflags.push_str("-Clink-arg=-fuse-ld=gold ");
        rustdocflags.push_str("-Clink-arg=-fuse-ld=gold ");
    }

    // add user provided flags
    rustflags.push_str(&env::var("RUSTFLAGS").unwrap_or_default());
    rustdocflags.push_str(&env::var("RUSTDOCFLAGS").unwrap_or_default());

    let status = Command::new(cargo_path)
        .arg(subcommand)
        .args(args)
        .env("RUSTFLAGS", &rustflags)
        .env("RUSTDOCFLAGS", &rustdocflags)
        .env("ASAN_OPTIONS", asan_options)
        .env("TSAN_OPTIONS", tsan_options)
        .status()
        .unwrap();
    process::exit(status.code().unwrap_or(1));
}

fn is_nightly() -> bool {
    Command::new("rustc")
        .args(&["-Z", "help"])
        .status()
        .unwrap()
        .success()
}
<commit_msg>Grammar<commit_after>use clap::{crate_version, value_t};

use std::env;
use std::ffi::OsStr;
use std::io;
use std::process::{self, Command, ExitStatus};
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::{Duration, Instant};

#[path = "../common.rs"]
mod common;

fn main() {
    if !common::archive_file_path().exists() {
        let version = common::afl_rustc_version();
        eprintln!(
            "AFL LLVM runtime is not built with Rust {}, run `cargo \
             install --force afl` to build it.",
            version
        );
        process::exit(1);
    }

    let app_matches = clap_app().get_matches();
    // This unwrap is okay because we set SubcommandRequiredElseHelp at the top level, and afl is
    // the only subcommand
    let afl_matches = app_matches.subcommand_matches("afl").unwrap();

    match afl_matches.subcommand() {
        ("analyze", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-analyze args")
                .unwrap_or_default();
            run_afl(args, "afl-analyze", None);
        }
        ("cmin", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-cmin args")
                .unwrap_or_default();
            run_afl(args, "afl-cmin", None);
        }
        ("fuzz", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-fuzz args")
                .unwrap_or_default();
            let timeout = sub_matches
                .value_of("max_total_time")
                .map(|_| value_t!(sub_matches, "max_total_time", u64).unwrap_or_else(|e| e.exit()));
            run_afl(args, "afl-fuzz", timeout);
        }
        ("gotcpu", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-gotcpu args")
                .unwrap_or_default();
            run_afl(args, "afl-gotcpu", None);
        }
        ("plot", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-plot args")
                .unwrap_or_default();
            run_afl(args, "afl-plot", None);
        }
        ("showmap", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-showmap args")
                .unwrap_or_default();
            run_afl(args, "afl-showmap", None);
        }
        ("tmin", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-tmin args")
                .unwrap_or_default();
            run_afl(args, "afl-tmin", None);
        }
        ("whatsup", Some(sub_matches)) => {
            let args = sub_matches
                .values_of_os("afl-whatsup args")
                .unwrap_or_default();
            run_afl(args, "afl-whatsup", None);
        }
        (subcommand, Some(sub_matches)) => {
            let args = sub_matches.values_of_os("").unwrap_or_default();
            run_cargo(subcommand, args);
        }
        // unreachable due to SubcommandRequiredElseHelp on "afl" subcommand
        (_, None) => unreachable!(),
    }
}

fn clap_app() -> clap::App<'static, 'static> {
    use clap::{
        App,
        AppSettings::{
            AllowExternalSubcommands, AllowLeadingHyphen, DisableHelpFlags, DisableHelpSubcommand,
            DisableVersion, SubcommandRequiredElseHelp,
        },
        Arg, SubCommand,
    };

    App::new("cargo afl")
        .bin_name("cargo")
        .setting(SubcommandRequiredElseHelp)
        .subcommand(
            SubCommand::with_name("afl")
                .version(crate_version!())
                .setting(SubcommandRequiredElseHelp)
                .setting(AllowExternalSubcommands)
                .usage("cargo afl [SUBCOMMAND or Cargo SUBCOMMAND]")
                .after_help(
                    "In addition to the subcommands above, Cargo subcommands are also \
                 supported (see `cargo help` for a list of all Cargo subcommands).",
                )
                .subcommand(
                    SubCommand::with_name("analyze")
                        .about("Invoke afl-analyze")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-analyze args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("cmin")
                        .about("Invoke afl-cmin")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-cmin args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("fuzz")
                        .about("Invoke afl-fuzz")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(
                            Arg::with_name("max_total_time")
                                .long("max_total_time")
                                .takes_value(true)
                                .help("Maximum amount of time to run the fuzzer"),
                        )
                        .arg(Arg::with_name("afl-fuzz args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("gotcpu")
                        .about("Invoke afl-gotcpu")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-gotcpu args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("plot")
                        .about("Invoke afl-plot")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-plot args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("showmap")
                        .about("Invoke afl-showmap")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-showmap args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("tmin")
                        .about("Invoke afl-tmin")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-tmin args").multiple(true)),
                )
                .subcommand(
                    SubCommand::with_name("whatsup")
                        .about("Invoke afl-whatsup")
                        .setting(AllowLeadingHyphen)
                        .setting(DisableHelpSubcommand)
                        .setting(DisableHelpFlags)
                        .setting(DisableVersion)
                        .arg(Arg::with_name("afl-whatsup args").multiple(true)),
                ),
        )
}

fn run_timeout_terminate(mut cmd: Command, timeout: Option<u64>) -> Result<ExitStatus, io::Error> {
    let timeout = match timeout {
        Some(timeout) => Duration::from_secs(timeout),
        None => return cmd.status(),
    };
    let start_time = Instant::now();

    let mut child = cmd.spawn()?;
    let pid = child.id();

    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let (stop_mutex, condvar) = &*pair;
    let thread_handle = {
        let pair = pair.clone();
        thread::spawn(move || -> Result<(), io::Error> {
            // This thread will wait until the child process has exited, or the
            // timeout has elapsed, whichever comes first. If the timeout
            // elapses, and the process is still running, it will send SIGTERM
            // to the child process.

            let (stop_mutex, condvar) = &*pair;
            let mut stop = stop_mutex.lock().unwrap();
            loop {
                let elapsed = Instant::now() - start_time;
                if elapsed >= timeout {
                    break;
                }

                let dur = timeout - elapsed;
                let results = condvar.wait_timeout(stop, dur).unwrap();
                stop = results.0;
                if *stop {
                    // Blocking waitid call on the main thread has returned,
                    // thus the child process has terminated
                    return Ok(());
                }
                if results.1.timed_out() {
                    break;
                }
            }

            // Since the waitid call on the main thread is using WNOWAIT, the
            // child process won't be cleaned up (until after this thread
            // exits and the main thread calls wait) and thus its PID won't be
            // reused by another, unrelated process.
            unsafe {
                let ret = libc::kill(pid as i32, libc::SIGTERM);
                if ret == -1 {
                    Err(io::Error::last_os_error())?
                }
            }

            Ok(())
        })
    };

    unsafe {
        // Block until the child process terminates, but leave it in a waitable
        // state still
        let ret = libc::waitid(
            libc::P_PID,
            pid,
            std::ptr::null_mut(),
            libc::WEXITED | libc::WNOWAIT,
        );
        if ret == -1 {
            Err(io::Error::last_os_error())?
        }
    }
    {
        let mut stop = stop_mutex.lock().unwrap();
        *stop = true;
    }
    // Tell the timeout thread to stop, wake it, and wait for it to exit
    condvar.notify_one();
    thread_handle.join().unwrap()?;

    // Clean up zombie and get exit status (this won't block, because the child
    // process has terminated and is still waitable)
    child.wait()
}

fn run_afl<I, S>(args: I, cmd: &str, timeout: Option<u64>)
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let cmd_path = common::afl_dir().join("bin").join(cmd);
    let mut cmd = Command::new(cmd_path);
    cmd.args(args);
    let status = run_timeout_terminate(cmd, timeout).unwrap();
    process::exit(status.code().unwrap_or(1));
}

fn run_cargo<I, S>(subcommand: &str, args: I)
where
    I: IntoIterator<Item = S>,
    S: AsRef<OsStr>,
{
    let cargo_path = env::var("CARGO").expect("Could not determine `cargo` path");

    // add some flags to sanitizers to make them work with Rust code
    let asan_options = env::var("ASAN_OPTIONS").unwrap_or_default();
    let asan_options = format!(
        "detect_odr_violation=0:abort_on_error=1:symbolize=0:{}",
        asan_options
    );

    let tsan_options = env::var("TSAN_OPTIONS").unwrap_or_default();
    let tsan_options = format!("report_signal_unsafe=0:{}", tsan_options);

    // The new LLVM pass manager was not enabled in rustc 1.57 as expected:
    // https://github.com/rust-lang/rust/pull/91263
    // The fix for now is to pass `-C passes=sancov-module` only to nightly
    // compilers for which the LLVM version is >= 13.

    let version_meta = rustc_version::version_meta().unwrap();
    let passes = if is_nightly() && version_meta.llvm_version.map_or(true, |v| v.major >= 13) {
        "sancov-module"
    } else {
        "sancov"
    };

    // `-C codegen-units=1` is needed to work around link errors
    // https://github.com/rust-fuzz/afl.rs/pull/193#issuecomment-933550430
    let mut rustflags = format!(
        "--cfg fuzzing \
         -C debug-assertions \
         -C overflow_checks \
         -C passes={} \
         -C codegen-units=1 \
         -C llvm-args=-sanitizer-coverage-level=3 \
         -C llvm-args=-sanitizer-coverage-trace-pc-guard \
         -C llvm-args=-sanitizer-coverage-prune-blocks=0 \
         -C opt-level=3 \
         -C target-cpu=native \
         -C debuginfo=0 \
         -l afl-llvm-rt \
         -L {} ",
        passes,
        common::afl_llvm_rt_dir().display()
    );

    // RUSTFLAGS are not used by rustdoc, instead RUSTDOCFLAGS are used. Since
    // doctests will try to link against afl-llvm-rt, set up RUSTDOCFLAGS to
    // have doctests built the same as other code to avoid issues with doctests.
    let mut rustdocflags = format!(
        "--cfg fuzzing \
         -C debug-assertions \
         -C overflow_checks \
         -C passes={} \
         -C codegen-units=1 \
         -C llvm-args=-sanitizer-coverage-level=3 \
         -C llvm-args=-sanitizer-coverage-trace-pc-guard \
         -C llvm-args=-sanitizer-coverage-prune-blocks=0 \
         -C opt-level=3 \
         -C target-cpu=native \
         -C debuginfo=0 \
         -L {} ",
        passes,
        common::afl_llvm_rt_dir().display()
    );

    if cfg!(target_os = "linux") {
        // work around https://github.com/rust-fuzz/afl.rs/issues/141 /
        // https://github.com/rust-lang/rust/issues/53945, can be removed once
        // those are fixed.
        rustflags.push_str("-Clink-arg=-fuse-ld=gold ");
        rustdocflags.push_str("-Clink-arg=-fuse-ld=gold ");
    }

    // add user provided flags
    rustflags.push_str(&env::var("RUSTFLAGS").unwrap_or_default());
    rustdocflags.push_str(&env::var("RUSTDOCFLAGS").unwrap_or_default());

    let status = Command::new(cargo_path)
        .arg(subcommand)
        .args(args)
        .env("RUSTFLAGS", &rustflags)
        .env("RUSTDOCFLAGS", &rustdocflags)
        .env("ASAN_OPTIONS", asan_options)
        .env("TSAN_OPTIONS", tsan_options)
        .status()
        .unwrap();
    process::exit(status.code().unwrap_or(1));
}

fn is_nightly() -> bool {
    Command::new("rustc")
        .args(&["-Z", "help"])
        .status()
        .unwrap()
        .success()
}
<|endoftext|>


<commit_before>extern crate bincode;
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
use flate2::{read, write, Compression};
use log_domain::LogDomain;
use std::{fs::File,
          io::{stdin, stdout, Read}};

use rustomata::grammars::{lcfrs::{cs_representation::{automata::{FilterStrategy,
                                                                 GeneratorStrategy},
                                                      CSRepresentation},
                                  Lcfrs},
                          pmcfg::negra::{to_negra, DumpMode, noparse}};
use rustomata::util::{agenda::Capacity, reverse::Reverse};

pub fn get_sub_command(name: &str) -> App {
    SubCommand::with_name(name)
        .about("Chomsky-Schützenberger representation of LCFRS")
        .subcommand(
            SubCommand::with_name("extract")
                .about(
                    "Reads a grammar from stdin and prints an object
                                       that Chomsky-Schützenberger-represents the grammar.",
                )
                .group(ArgGroup::with_name("strategy"))
                .arg(
                    Arg::with_name("naive")
                        .help("Use a naive Generator automaton.")
                        .long("naive")
                        .group("strategy"),
                )
                .arg(
                    Arg::with_name("approx")
                        .help("Use a regular approximation the push-down generator.")
                        .long("approx")
                        .group("strategy")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name("pd")
                        .help("Use a push-down automaton to generate candidates.")
                        .long("pd")
                        .group("strategy"),
                )
                .arg(
                    Arg::with_name("cyk")
                        .help("Evaluate an fsa cyk-style to generate candidates.")
                        .long("cyk")
                        .group("strategy"),
                )
                .group(ArgGroup::with_name("filter"))
                .arg(
                    Arg::with_name("naive-filter")
                        .help("Use a naive filter automaton.")
                        .long("naive-filter")
                        .group("filter"),
                )
                .arg(
                    Arg::with_name("inside-filter")
                        .help("Use the inside filter automaton.")
                        .long("inside-filter")
                        .group("filter"),
                ),
        )
        .subcommand(
            SubCommand::with_name("parse")
                .about(
                    "Reads a list of words from stdin and parses them
                                       using the CS representation of a grammar.",
                )
                .arg(
                    Arg::with_name("csfile")
                        .required(true)
                        .index(1)
                        .help("The file that contains the CS representation of a grammar."),
                )
                .arg(
                    Arg::with_name("k")
                        .short("k")
                        .takes_value(true)
                        .help("Puts the `k` in k-best parsing."),
                )
                .arg(
                    Arg::with_name("beam")
                        .short("b")
                        .long("beam")
                        .takes_value(true)
                        .help("Beam with during search."),
                )
                .arg(
                    Arg::with_name("candidates")
                        .short("c")
                        .long("candidates")
                        .takes_value(true)
                        .help("Maximum number of candidates to enumerate."),
                )
                .arg(
                    Arg::with_name("with-lines")
                        .short("l")
                        .long("with-lines")
                        .help("The first symbol denotes the id of the parse tree."),
                )
                .arg(
                    Arg::with_name("with-pos")
                        .short("p")
                        .long("with-pos")
                        .help("Parses the sentence with already annotated POS tags."),
                )
                .arg(
                    Arg::with_name("debugmode")
                        .short("d")
                        .long("debug")
                        .help("Prints debug information instead of parse trees."),
                )
                .arg(
                    Arg::with_name("fallback")
                        .short("f")
                        .long("with-fallback")
                        .help("Will output an incorrect parse tree if the parse does not find a correct one."),
                ),
        )
}

pub fn handle_sub_matches(submatches: &ArgMatches) {
    match submatches.subcommand() {
        ("extract", Some(susbsubmatches)) => {
            let strategy = {
                if susbsubmatches.is_present("naive") {
                    GeneratorStrategy::Finite
                } else if susbsubmatches.is_present("pd") {
                    GeneratorStrategy::PushDown
                } else if let Some(depth) = susbsubmatches.value_of("approx") {
                    GeneratorStrategy::Approx(depth.parse().unwrap())
                } else {
                    GeneratorStrategy::CykLike
                }
            };
            let filter = {
                if susbsubmatches.is_present("naive-filter") {
                    FilterStrategy::Naive
                } else {
                    FilterStrategy::Inside
                }
            };

            let mut grammar_string = String::new();
            stdin()
                .read_to_string(&mut grammar_string)
                .expect("Could not read from stdin. Be sure to provide the gramar file as input.");
            let grammar: Lcfrs<String, String, Reverse<LogDomain<f64>>> = grammar_string
                .parse()
                .expect("Could not decode the grammar provided via stdin.");

            bincode::serialize_into(
                &mut write::GzEncoder::new(stdout(), Compression::best()),
                &CSRepresentation::new(grammar, filter, strategy),
                bincode::Infinite,
            ).unwrap()
        }

        ("parse", Some(params)) => {
            let mut word_strings = String::new();
            stdin()
                .read_to_string(&mut word_strings)
                .expect("Please pass a string to parse using stdin or using the `--word` option.");
            let k: usize = match params.value_of("k") {
                Some(n) => n.parse::<usize>().unwrap(),
                None => 1usize,
            };

            let beam: Capacity = match params.value_of("beam") {
                Some(b) => b.parse().unwrap(),
                None => Capacity::Infinite,
            };
            let candidates: Capacity = match params.value_of("candidates") {
                Some(c) => c.parse().unwrap(),
                None => Capacity::Infinite,
            };

            let csfile = File::open(params.value_of("csfile").unwrap()).unwrap();

            let csrep: CSRepresentation<String, String, Reverse<LogDomain<f64>>> =
                bincode::deserialize_from(&mut read::GzDecoder::new(csfile), bincode::Infinite)
                    .unwrap();

            for (i, sentence) in word_strings.lines().enumerate() {
                let (i, words) = split_line(sentence, params.is_present("with-lines"), i);
                let (words, negra_mode) = split_pos(words, params.is_present("with-pos"));

                if params.is_present("debugmode") {
                    let tuple = csrep.debug(words.as_slice(), beam, candidates);
                    eprint!("{} {} {} {} {} {} ", tuple.0, tuple.1, tuple.2, tuple.3, tuple.4, tuple.5);
                    if let Some((t, c)) = tuple.6 {
                        eprintln!("{}", c);
                        println!("{}", to_negra(&t, i, negra_mode));
                    } else {
                        eprintln!("{}", -1);
                        println!("{}", noparse(&words, i, negra_mode));
                    }
                } else {
                    let mut found_trees = false;
                    let (iterator, fallback) = csrep.generate(words.as_slice(), beam, candidates);
                    for derivation in iterator.take(k) {
                        found_trees = true;
                        println!(
                            "{}",
                            to_negra(
                                &derivation
                                    .into_iter()
                                    .map(|(k, v)| (k, v.clone()))
                                    .collect(),
                                i,
                                negra_mode.clone()
                            )
                        );
                    }
                    if !found_trees && params.is_present("fallback") {
                        if let Some(tree) = fallback {
                            println!("{}", to_negra(&tree, i, negra_mode));
                        } else {
                            println!("{}", noparse(&words, i, negra_mode));
                        }
                    } else if !found_trees {
                        println!("{}", noparse(&words, i, negra_mode));
                    }
                }
            }
        }
        _ => (),
    }
}

fn split_line<'a>(line: &'a str, with_line_number: bool, default_line_number: usize) -> (usize, impl Iterator<Item=&'a str> + 'a) {
    let mut word_iterator = line.split_whitespace();

    let line_number = if with_line_number {
        word_iterator.next()
                     .expect("missing line number")
                     .parse()
                     .expect("invalid line numer")
    } else {
        default_line_number
    };

    (line_number, word_iterator)
}

fn split_pos<'a>(words: impl Iterator<Item=&'a str> + 'a, with_pos: bool) -> (Vec<String>, DumpMode<String>) {
    if with_pos {
        let (ws, pos) = words.map(
            |wp| {
                let mut it = wp.split('/');
                ( it.next().unwrap().to_string(),
                  it.next().expect("missing pos annotation").to_string()
                )
            }
        ).unzip();
        (pos, DumpMode::FromPos(ws))
    } else {
        (words.map(|s| s.to_string()).collect(), DumpMode::Default)
    }
}<commit_msg>fix word/pos split<commit_after>extern crate bincode;
use clap::{App, Arg, ArgGroup, ArgMatches, SubCommand};
use flate2::{read, write, Compression};
use log_domain::LogDomain;
use std::{fs::File,
          io::{stdin, stdout, Read}};

use rustomata::grammars::{lcfrs::{cs_representation::{automata::{FilterStrategy,
                                                                 GeneratorStrategy},
                                                      CSRepresentation},
                                  Lcfrs},
                          pmcfg::negra::{to_negra, DumpMode, noparse}};
use rustomata::util::{agenda::Capacity, reverse::Reverse};

pub fn get_sub_command(name: &str) -> App {
    SubCommand::with_name(name)
        .about("Chomsky-Schützenberger representation of LCFRS")
        .subcommand(
            SubCommand::with_name("extract")
                .about(
                    "Reads a grammar from stdin and prints an object
                                       that Chomsky-Schützenberger-represents the grammar.",
                )
                .group(ArgGroup::with_name("strategy"))
                .arg(
                    Arg::with_name("naive")
                        .help("Use a naive Generator automaton.")
                        .long("naive")
                        .group("strategy"),
                )
                .arg(
                    Arg::with_name("approx")
                        .help("Use a regular approximation the push-down generator.")
                        .long("approx")
                        .group("strategy")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name("pd")
                        .help("Use a push-down automaton to generate candidates.")
                        .long("pd")
                        .group("strategy"),
                )
                .arg(
                    Arg::with_name("cyk")
                        .help("Evaluate an fsa cyk-style to generate candidates.")
                        .long("cyk")
                        .group("strategy"),
                )
                .group(ArgGroup::with_name("filter"))
                .arg(
                    Arg::with_name("naive-filter")
                        .help("Use a naive filter automaton.")
                        .long("naive-filter")
                        .group("filter"),
                )
                .arg(
                    Arg::with_name("inside-filter")
                        .help("Use the inside filter automaton.")
                        .long("inside-filter")
                        .group("filter"),
                ),
        )
        .subcommand(
            SubCommand::with_name("parse")
                .about(
                    "Reads a list of words from stdin and parses them
                                       using the CS representation of a grammar.",
                )
                .arg(
                    Arg::with_name("csfile")
                        .required(true)
                        .index(1)
                        .help("The file that contains the CS representation of a grammar."),
                )
                .arg(
                    Arg::with_name("k")
                        .short("k")
                        .takes_value(true)
                        .help("Puts the `k` in k-best parsing."),
                )
                .arg(
                    Arg::with_name("beam")
                        .short("b")
                        .long("beam")
                        .takes_value(true)
                        .help("Beam with during search."),
                )
                .arg(
                    Arg::with_name("candidates")
                        .short("c")
                        .long("candidates")
                        .takes_value(true)
                        .help("Maximum number of candidates to enumerate."),
                )
                .arg(
                    Arg::with_name("with-lines")
                        .short("l")
                        .long("with-lines")
                        .help("The first symbol denotes the id of the parse tree."),
                )
                .arg(
                    Arg::with_name("with-pos")
                        .short("p")
                        .long("with-pos")
                        .help("Parses the sentence with already annotated POS tags."),
                )
                .arg(
                    Arg::with_name("debugmode")
                        .short("d")
                        .long("debug")
                        .help("Prints debug information instead of parse trees."),
                )
                .arg(
                    Arg::with_name("fallback")
                        .short("f")
                        .long("with-fallback")
                        .help("Will output an incorrect parse tree if the parse does not find a correct one."),
                ),
        )
}

pub fn handle_sub_matches(submatches: &ArgMatches) {
    match submatches.subcommand() {
        ("extract", Some(susbsubmatches)) => {
            let strategy = {
                if susbsubmatches.is_present("naive") {
                    GeneratorStrategy::Finite
                } else if susbsubmatches.is_present("pd") {
                    GeneratorStrategy::PushDown
                } else if let Some(depth) = susbsubmatches.value_of("approx") {
                    GeneratorStrategy::Approx(depth.parse().unwrap())
                } else {
                    GeneratorStrategy::CykLike
                }
            };
            let filter = {
                if susbsubmatches.is_present("naive-filter") {
                    FilterStrategy::Naive
                } else {
                    FilterStrategy::Inside
                }
            };

            let mut grammar_string = String::new();
            stdin()
                .read_to_string(&mut grammar_string)
                .expect("Could not read from stdin. Be sure to provide the gramar file as input.");
            let grammar: Lcfrs<String, String, Reverse<LogDomain<f64>>> = grammar_string
                .parse()
                .expect("Could not decode the grammar provided via stdin.");

            bincode::serialize_into(
                &mut write::GzEncoder::new(stdout(), Compression::best()),
                &CSRepresentation::new(grammar, filter, strategy),
                bincode::Infinite,
            ).unwrap()
        }

        ("parse", Some(params)) => {
            let mut word_strings = String::new();
            stdin()
                .read_to_string(&mut word_strings)
                .expect("Please pass a string to parse using stdin or using the `--word` option.");
            let k: usize = match params.value_of("k") {
                Some(n) => n.parse::<usize>().unwrap(),
                None => 1usize,
            };

            let beam: Capacity = match params.value_of("beam") {
                Some(b) => b.parse().unwrap(),
                None => Capacity::Infinite,
            };
            let candidates: Capacity = match params.value_of("candidates") {
                Some(c) => c.parse().unwrap(),
                None => Capacity::Infinite,
            };

            let csfile = File::open(params.value_of("csfile").unwrap()).unwrap();

            let csrep: CSRepresentation<String, String, Reverse<LogDomain<f64>>> =
                bincode::deserialize_from(&mut read::GzDecoder::new(csfile), bincode::Infinite)
                    .unwrap();

            for (i, sentence) in word_strings.lines().enumerate() {
                let (i, words) = split_line(sentence, params.is_present("with-lines"), i);
                let (words, negra_mode) = split_pos(words, params.is_present("with-pos"));

                if params.is_present("debugmode") {
                    let tuple = csrep.debug(words.as_slice(), beam, candidates);
                    eprint!("{} {} {} {} {} {} ", tuple.0, tuple.1, tuple.2, tuple.3, tuple.4, tuple.5);
                    if let Some((t, c)) = tuple.6 {
                        eprintln!("{}", c);
                        println!("{}", to_negra(&t, i, negra_mode));
                    } else {
                        eprintln!("{}", -1);
                        println!("{}", noparse(&words, i, negra_mode));
                    }
                } else {
                    let mut found_trees = false;
                    let (iterator, fallback) = csrep.generate(words.as_slice(), beam, candidates);
                    for derivation in iterator.take(k) {
                        found_trees = true;
                        println!(
                            "{}",
                            to_negra(
                                &derivation
                                    .into_iter()
                                    .map(|(k, v)| (k, v.clone()))
                                    .collect(),
                                i,
                                negra_mode.clone()
                            )
                        );
                    }
                    if !found_trees && params.is_present("fallback") {
                        if let Some(tree) = fallback {
                            println!("{}", to_negra(&tree, i, negra_mode));
                        } else {
                            println!("{}", noparse(&words, i, negra_mode));
                        }
                    } else if !found_trees {
                        println!("{}", noparse(&words, i, negra_mode));
                    }
                }
            }
        }
        _ => (),
    }
}

fn split_line<'a>(line: &'a str, with_line_number: bool, default_line_number: usize) -> (usize, impl Iterator<Item=&'a str> + 'a) {
    let mut word_iterator = line.split_whitespace();

    let line_number = if with_line_number {
        word_iterator.next()
                     .expect("missing line number")
                     .parse()
                     .expect("invalid line numer")
    } else {
        default_line_number
    };

    (line_number, word_iterator)
}

fn split_pos<'a>(words: impl Iterator<Item=&'a str> + 'a, with_pos: bool) -> (Vec<String>, DumpMode<String>) {
    if with_pos {
        let (pos, ws) = words.map(
            |wp| {
                let mut it = wp.rsplitn(2, '/');
                ( it.next().unwrap().to_string(),
                  it.next().expect("missing pos annotation").to_string()
                )
            }
        ).unzip();
        (pos, DumpMode::FromPos(ws))
    } else {
        (words.map(|s| s.to_string()).collect(), DumpMode::Default)
    }
}<|endoftext|>


<commit_before>#![crate_type = "bin"]
#![crate_name="demo"]

extern crate sdl2;
extern crate sdl2_mixer;

use std::os;
use std::borrow::ToOwned;

fn main() {
    let args = os::args();
    if args.len() < 2 {
        println!("Usage: ./demo audio.[mp3|wav|ogg]")
    } else {
        match dump_info(&Path::new(args[1].to_owned())) {
            _ => ()
        }
    }
}

fn dump_info(filename: &Path) -> Result<(), String> {
    println!("linked version: {:?}", sdl2_mixer::get_linked_version());
    sdl2::init(sdl2::INIT_AUDIO | sdl2::INIT_TIMER);
    println!("inited => {}", sdl2_mixer::init(sdl2_mixer::INIT_MP3 | sdl2_mixer::INIT_FLAC |
                                              sdl2_mixer::INIT_MOD | sdl2_mixer::INIT_FLUIDSYNTH |
                                              sdl2_mixer::INIT_MODPLUG | sdl2_mixer::INIT_OGG).bits());
    // TODO: 0x8010 is SDL_audio flag
    try!(sdl2_mixer::open_audio(sdl2_mixer::DEFAULT_FREQUENCY, 0x8010u16, 2, 1024));
    sdl2_mixer::allocate_channels(0);

    {
        let n = sdl2_mixer::get_chunk_decoders_number();
        println!("available chunk(sample) decoders: {}", n);
        for i in (0..n) {
            println!("| decoder {} => {:}", i, sdl2_mixer::get_chunk_decoder(i));
        }
    }

    {
        let n = sdl2_mixer::get_music_decoders_number();
        println!("available music decoders: {}", n);
        for i in (0..n) {
            println!("| decoder {} => {:}", i, sdl2_mixer::get_music_decoder(i));
        }
    }

    println!("query spec => {:?}", sdl2_mixer::query_spec());

    let music = sdl2_mixer::Music::from_file(filename).unwrap();

    fn hook_finished() {
        println!("play ends! from rust cb");
    }

    sdl2_mixer::Music::hook_finished(hook_finished);

    println!("music => {:?}", music);
    println!("music type => {:?}", music.get_type());

    println!("music volume => {:?}", sdl2_mixer::Music::get_volume());

    println!("play => {:?}", music.play(1));

    sdl2::timer::delay(10000);

    println!("fading out ... {:?}", sdl2_mixer::Music::fade_out(4000));

    sdl2::timer::delay(5000);

    println!("fading in from pos ... {:?}", music.fade_in_from_pos(1, 10000, 100.0));

    sdl2::timer::delay(5000);

    sdl2_mixer::Music::halt();

    sdl2::timer::delay(1000);
    // here will print hook_finished

    sdl2_mixer::quit();
    sdl2::quit();

    Ok(())
}
<commit_msg>Make demo work with newest Rust and sdl2_mixer.<commit_after>#![crate_type = "bin"]

extern crate sdl2;
extern crate sdl2_mixer;

use std::env;
use std::path::Path;
use sdl2::{INIT_AUDIO, INIT_TIMER};
use sdl2_mixer::{INIT_MP3, INIT_FLAC, INIT_MOD, INIT_FLUIDSYNTH, INIT_MODPLUG,
                 INIT_OGG, DEFAULT_FREQUENCY};

fn main() {

    let args: Vec<_> = env::args().collect();

    if args.len() < 2 {
        println!("Usage: ./demo audio.[mp3|wav|ogg]")
    } else {
        demo(Path::new(&args[1]));
    }
}

fn demo(filename: &Path) {

    println!("linked version: {}", sdl2_mixer::get_linked_version());

    let _ = sdl2::init(INIT_AUDIO | INIT_TIMER);

    println!("mixer initialized: {}", sdl2_mixer::init(
        INIT_MP3 | INIT_FLAC | INIT_MOD | INIT_FLUIDSYNTH |
        INIT_MODPLUG | INIT_OGG).bits());

    // TODO: 0x8010 is SDL_audio flag
    let _ = sdl2_mixer::open_audio(DEFAULT_FREQUENCY, 0x8010u16, 2, 1024);
    sdl2_mixer::allocate_channels(0);

    {
        let n = sdl2_mixer::get_chunk_decoders_number();
        println!("available chunk(sample) decoders: {}", n);
        for i in (0..n) {
            println!("  decoder {} => {}", i, sdl2_mixer::get_chunk_decoder(i));
        }
    }

    {
        let n = sdl2_mixer::get_music_decoders_number();
        println!("available music decoders: {}", n);
        for i in (0..n) {
            println!("  decoder {} => {}", i, sdl2_mixer::get_music_decoder(i));
        }
    }

    println!("query spec => {:?}", sdl2_mixer::query_spec());

    let music = sdl2_mixer::Music::from_file(filename).unwrap();

    fn hook_finished() {
        println!("play ends! from rust cb");
    }

    sdl2_mixer::Music::hook_finished(hook_finished);

    println!("music => {:?}", music);
    println!("music type => {:?}", music.get_type());
    println!("music volume => {:?}", sdl2_mixer::Music::get_volume());
    println!("play => {:?}", music.play(1));

    sdl2::timer::delay(10000);

    println!("fading out ... {:?}", sdl2_mixer::Music::fade_out(4000));

    sdl2::timer::delay(5000);

    println!("fading in from pos ... {:?}", music.fade_in_from_pos(1, 10000, 100.0));

    sdl2::timer::delay(5000);
    sdl2_mixer::Music::halt();
    sdl2::timer::delay(1000);

    // here will print hook_finished

    println!("qutting mixer");
    sdl2_mixer::quit();
    println!("quitting sdl");
}
<|endoftext|>


<commit_before>//! Direction-related structures.
//!
//! This module defines two main concepts: [Orientation] and [Direction].
//!
//! ### Orientation
//!
//! `Orientation` is a simple enum that can take two values:
//! `Horizontal` or `Vertical`.
//!
//! ### Direction
//!
//! `Direction` is a bit more complex, and can be of two kinds:
//! * Absolute direction: left, up, right, or down
//! * Relative direction: front or back.
//!   Its actual direction depends on the orientation.

use vec::Vec2;

/// Describes a vertical or horizontal orientation for a view.
#[derive(Clone,Copy,Debug,PartialEq)]
pub enum Orientation {
    /// Horizontal orientation
    Horizontal,
    /// Vertical orientation
    Vertical,
}

impl Orientation {
    /// Returns the component of `v` corresponding to this orientation.
    ///
    /// (`Horizontal` will return the x value,
    /// and `Vertical` will return the y value.)
    pub fn get(&self, v: &Vec2) -> usize {
        *v.get(*self)
    }

    /// Returns the other orientation.
    pub fn swap(&self) -> Self {
        match *self {
            Orientation::Horizontal => Orientation::Vertical,
            Orientation::Vertical => Orientation::Horizontal,
        }
    }

    /// Returns a mutable reference to the component of the given vector
    /// corresponding to this orientation.
    pub fn get_ref<'a, 'b>(&'a self, v: &'b mut Vec2) -> &'b mut usize {
        match *self {
            Orientation::Horizontal => &mut v.x,
            Orientation::Vertical => &mut v.y,
        }
    }

    /// Takes an iterator on sizes, and stack them in the current orientation,
    /// returning the size of the required bounding box.
    ///
    /// For an horizontal view, returns (Sum(x), Max(y)).
    /// For a vertical view, returns (Max(x),Sum(y)).
    pub fn stack<'a, T: Iterator<Item = &'a Vec2>>(&self, iter: T) -> Vec2 {
        match *self {
            Orientation::Horizontal => {
                iter.fold(Vec2::zero(), |a, b| a.stack_horizontal(b))
            }
            Orientation::Vertical => {
                iter.fold(Vec2::zero(), |a, b| a.stack_vertical(b))
            }
        }
    }

    /// Creates a new `Vec2` with `value` in `self`'s axis.
    pub fn make_vec(&self, value: usize) -> Vec2 {
        let mut result = Vec2::zero();
        *self.get_ref(&mut result) = value;
        result
    }
}

/// Represents a direction, either absolute or orientation-dependent.
///
/// * Absolute directions are Up, Down, Left, and Right.
/// * Relative directions are Front and Back.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Direction {
    /// An absolute direction.
    Abs(Absolute),
    /// A direction relative to the current orientation.
    Rel(Relative),
}

impl Direction {
    /// Returns the relative direction for the given orientation.
    pub fn relative(self, orientation: Orientation) -> Option<Relative> {
        match self {
            Direction::Abs(abs) => abs.relative(orientation),
            Direction::Rel(rel) => Some(rel),
        }
    }

    /// Returns the absolute direction in the given `orientation`.
    pub fn absolute(self, orientation: Orientation) -> Absolute {
        match self {
            Direction::Abs(abs) => abs,
            Direction::Rel(rel) => rel.absolute(orientation),
        }
    }

    /// Shortcut to create `Direction::Rel(Relative::Back)`
    pub fn back() -> Self {
        Direction::Rel(Relative::Back)
    }

    /// Shortcut to create `Direction::Rel(Relative::Front)`
    pub fn front() -> Self {
        Direction::Rel(Relative::Front)
    }

    /// Shortcut to create `Direction::Abs(Absolute::Left)`
    pub fn left() -> Self {
        Direction::Abs(Absolute::Left)
    }

    /// Shortcut to create `Direction::Abs(Absolute::Right)`
    pub fn right() -> Self {
        Direction::Abs(Absolute::Right)
    }

    /// Shortcut to create `Direction::Abs(Absolute::Up)`
    pub fn up() -> Self {
        Direction::Abs(Absolute::Up)
    }

    /// Shortcut to create `Direction::Abs(Absolute::Down)`
    pub fn down() -> Self {
        Direction::Abs(Absolute::Down)
    }

    /// Shortcut to create `Direction::Abs(Absolute::None)`
    pub fn none() -> Self {
        Direction::Abs(Absolute::None)
    }
}

/// Direction relative to an orientation.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Relative {
    /// Front relative direction.
    ///
    /// * Horizontally, this means `Left`
    /// * Vertically, this means `Up`
    ///
    /// TODO: handle right-to-left?
    Front,

    /// Back relative direction.
    ///
    /// * Horizontally, this means `Right`
    /// * Vertically, this means `Down`.
    Back,
}

impl Relative {
    /// Returns the absolute direction in the given `orientation`.
    pub fn absolute(self, orientation: Orientation) -> Absolute {
        match (orientation, self) {
            (Orientation::Horizontal, Relative::Front) => Absolute::Left,
            (Orientation::Horizontal, Relative::Back) => Absolute::Right,
            (Orientation::Vertical, Relative::Front) => Absolute::Up,
            (Orientation::Vertical, Relative::Back) => Absolute::Down,
        }
    }
}

/// Absolute direction (up, down, left, right).
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Absolute {
    /// Left
    Left,
    /// Up
    Up,
    /// Right
    Right,
    /// Down
    Down,

    /// No real direction
    None,
}

impl Absolute {
    /// Returns the relative direction for the given orientation.
    ///
    /// Returns `None` when the direction does not apply to the given
    /// orientation (ex: `Left` and `Vertical`).
    pub fn relative(self, orientation: Orientation) -> Option<Relative> {
        match (orientation, self) {
            (Orientation::Horizontal, Absolute::Left) |
            (Orientation::Vertical, Absolute::Up) => Some(Relative::Front),
            (Orientation::Horizontal, Absolute::Right) |
            (Orientation::Vertical, Absolute::Down) => Some(Relative::Back),
            _ => None,
        }
    }
}
<commit_msg>Fix some documentation<commit_after>//! Direction-related structures.
//!
//! This module defines two main concepts: [Orientation] and [Direction].
//!
//! ### Orientation
//!
//! `Orientation` is a simple enum that can take two values:
//! `Horizontal` or `Vertical`.
//!
//! ### Direction
//!
//! `Direction` is a bit more complex, and can be of two kinds:
//!
//! * Absolute direction: left, up, right, or down
//! * Relative direction: front or back.
//!   Its actual direction depends on the orientation.

use vec::Vec2;

/// Describes a vertical or horizontal orientation for a view.
#[derive(Clone,Copy,Debug,PartialEq)]
pub enum Orientation {
    /// Horizontal orientation
    Horizontal,
    /// Vertical orientation
    Vertical,
}

impl Orientation {
    /// Returns the component of `v` corresponding to this orientation.
    ///
    /// (`Horizontal` will return the x value,
    /// and `Vertical` will return the y value.)
    pub fn get(&self, v: &Vec2) -> usize {
        *v.get(*self)
    }

    /// Returns the other orientation.
    pub fn swap(&self) -> Self {
        match *self {
            Orientation::Horizontal => Orientation::Vertical,
            Orientation::Vertical => Orientation::Horizontal,
        }
    }

    /// Returns a mutable reference to the component of the given vector
    /// corresponding to this orientation.
    pub fn get_ref<'a, 'b>(&'a self, v: &'b mut Vec2) -> &'b mut usize {
        match *self {
            Orientation::Horizontal => &mut v.x,
            Orientation::Vertical => &mut v.y,
        }
    }

    /// Takes an iterator on sizes, and stack them in the current orientation,
    /// returning the size of the required bounding box.
    ///
    /// For an horizontal view, returns (Sum(x), Max(y)).
    /// For a vertical view, returns (Max(x),Sum(y)).
    pub fn stack<'a, T: Iterator<Item = &'a Vec2>>(&self, iter: T) -> Vec2 {
        match *self {
            Orientation::Horizontal => {
                iter.fold(Vec2::zero(), |a, b| a.stack_horizontal(b))
            }
            Orientation::Vertical => {
                iter.fold(Vec2::zero(), |a, b| a.stack_vertical(b))
            }
        }
    }

    /// Creates a new `Vec2` with `value` in `self`'s axis.
    pub fn make_vec(&self, value: usize) -> Vec2 {
        let mut result = Vec2::zero();
        *self.get_ref(&mut result) = value;
        result
    }
}

/// Represents a direction, either absolute or orientation-dependent.
///
/// * Absolute directions are Up, Down, Left, and Right.
/// * Relative directions are Front and Back.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Direction {
    /// An absolute direction.
    Abs(Absolute),
    /// A direction relative to the current orientation.
    Rel(Relative),
}

impl Direction {
    /// Returns the relative direction for the given orientation.
    pub fn relative(self, orientation: Orientation) -> Option<Relative> {
        match self {
            Direction::Abs(abs) => abs.relative(orientation),
            Direction::Rel(rel) => Some(rel),
        }
    }

    /// Returns the absolute direction in the given `orientation`.
    pub fn absolute(self, orientation: Orientation) -> Absolute {
        match self {
            Direction::Abs(abs) => abs,
            Direction::Rel(rel) => rel.absolute(orientation),
        }
    }

    /// Shortcut to create `Direction::Rel(Relative::Back)`
    pub fn back() -> Self {
        Direction::Rel(Relative::Back)
    }

    /// Shortcut to create `Direction::Rel(Relative::Front)`
    pub fn front() -> Self {
        Direction::Rel(Relative::Front)
    }

    /// Shortcut to create `Direction::Abs(Absolute::Left)`
    pub fn left() -> Self {
        Direction::Abs(Absolute::Left)
    }

    /// Shortcut to create `Direction::Abs(Absolute::Right)`
    pub fn right() -> Self {
        Direction::Abs(Absolute::Right)
    }

    /// Shortcut to create `Direction::Abs(Absolute::Up)`
    pub fn up() -> Self {
        Direction::Abs(Absolute::Up)
    }

    /// Shortcut to create `Direction::Abs(Absolute::Down)`
    pub fn down() -> Self {
        Direction::Abs(Absolute::Down)
    }

    /// Shortcut to create `Direction::Abs(Absolute::None)`
    pub fn none() -> Self {
        Direction::Abs(Absolute::None)
    }
}

/// Direction relative to an orientation.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Relative {
    /// Front relative direction.
    ///
    /// * Horizontally, this means `Left`
    /// * Vertically, this means `Up`
    ///
    /// TODO: handle right-to-left?
    Front,

    /// Back relative direction.
    ///
    /// * Horizontally, this means `Right`
    /// * Vertically, this means `Down`.
    Back,
}

impl Relative {
    /// Returns the absolute direction in the given `orientation`.
    pub fn absolute(self, orientation: Orientation) -> Absolute {
        match (orientation, self) {
            (Orientation::Horizontal, Relative::Front) => Absolute::Left,
            (Orientation::Horizontal, Relative::Back) => Absolute::Right,
            (Orientation::Vertical, Relative::Front) => Absolute::Up,
            (Orientation::Vertical, Relative::Back) => Absolute::Down,
        }
    }
}

/// Absolute direction (up, down, left, right).
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Absolute {
    /// Left
    Left,
    /// Up
    Up,
    /// Right
    Right,
    /// Down
    Down,

    /// No real direction.
    ///
    /// Used when the "direction" is accross layers for instance.
    None,
}

impl Absolute {
    /// Returns the relative direction for the given orientation.
    ///
    /// Returns `None` when the direction does not apply to the given
    /// orientation (ex: `Left` and `Vertical`).
    pub fn relative(self, orientation: Orientation) -> Option<Relative> {
        match (orientation, self) {
            (Orientation::Horizontal, Absolute::Left) |
            (Orientation::Vertical, Absolute::Up) => Some(Relative::Front),
            (Orientation::Horizontal, Absolute::Right) |
            (Orientation::Vertical, Absolute::Down) => Some(Relative::Back),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>extern crate ws;

use ws::listen;

fn main() {
    listen("127.0.0.1:3012", |out| {
        move |msg| {
            out.send(msg)
        }
    }).unwrap()
}
<commit_msg>Deleted not used code<commit_after>extern crate ws;

use ws::listen;

fn main() {
    listen("127.0.0.1:3012", |out| {
        move |msg| {
            println!(msg);
            out.send(msg)
        }
    }).unwrap()
}
<|endoftext|>


<commit_before>// Spans are encoded using 1-bit tag and 2 different encoding formats (one for each tag value).
// One format is used for keeping span data inline,
// another contains index into an out-of-line span interner.
// The encoding format for inline spans were obtained by optimizing over crates in rustc/libstd.
// See https://internals.rust-lang.org/t/rfc-compiler-refactoring-spans/1357/28

use crate::hygiene::SyntaxContext;
use crate::SESSION_GLOBALS;
use crate::{BytePos, SpanData};

use rustc_data_structures::fx::FxIndexSet;

/// A compressed span.
///
/// Whereas [`SpanData`] is 12 bytes, which is a bit too big to stick everywhere, `Span`
/// is a form that only takes up 8 bytes, with less space for the length and
/// context. The vast majority (99.9%+) of `SpanData` instances will fit within
/// those 8 bytes; any `SpanData` whose fields don't fit into a `Span` are
/// stored in a separate interner table, and the `Span` will index into that
/// table. Interning is rare enough that the cost is low, but common enough
/// that the code is exercised regularly.
///
/// An earlier version of this code used only 4 bytes for `Span`, but that was
/// slower because only 80--90% of spans could be stored inline (even less in
/// very large crates) and so the interner was used a lot more.
///
/// Inline (compressed) format:
/// - `span.base_or_index == span_data.lo`
/// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
/// - `span.ctxt == span_data.ctxt` (must be `<= MAX_CTXT`)
///
/// Interned format:
/// - `span.base_or_index == index` (indexes into the interner table)
/// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero)
/// - `span.ctxt == 0`
///
/// The inline form uses 0 for the tag value (rather than 1) so that we don't
/// need to mask out the tag bit when getting the length, and so that the
/// dummy span can be all zeroes.
///
/// Notes about the choice of field sizes:
/// - `base` is 32 bits in both `Span` and `SpanData`, which means that `base`
///   values never cause interning. The number of bits needed for `base`
///   depends on the crate size. 32 bits allows up to 4 GiB of code in a crate.
/// - `len` is 15 bits in `Span` (a u16, minus 1 bit for the tag) and 32 bits
///   in `SpanData`, which means that large `len` values will cause interning.
///   The number of bits needed for `len` does not depend on the crate size.
///   The most common numbers of bits for `len` are from 0 to 7, with a peak usually
///   at 3 or 4, and then it drops off quickly from 8 onwards. 15 bits is enough
///   for 99.99%+ of cases, but larger values (sometimes 20+ bits) might occur
///   dozens of times in a typical crate.
/// - `ctxt` is 16 bits in `Span` and 32 bits in `SpanData`, which means that
///   large `ctxt` values will cause interning. The number of bits needed for
///   `ctxt` values depend partly on the crate size and partly on the form of
///   the code. No crates in `rustc-perf` need more than 15 bits for `ctxt`,
///   but larger crates might need more than 16 bits.
///
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
pub struct Span {
    base_or_index: u32,
    len_or_tag: u16,
    ctxt_or_zero: u16,
}

const LEN_TAG: u16 = 0b1000_0000_0000_0000;
const MAX_LEN: u32 = 0b0111_1111_1111_1111;
const MAX_CTXT: u32 = 0b1111_1111_1111_1111;

/// Dummy span, both position and length are zero, syntax context is zero as well.
pub const DUMMY_SP: Span = Span { base_or_index: 0, len_or_tag: 0, ctxt_or_zero: 0 };

impl Span {
    #[inline]
    pub fn new(mut lo: BytePos, mut hi: BytePos, ctxt: SyntaxContext) -> Self {
        if lo > hi {
            std::mem::swap(&mut lo, &mut hi);
        }

        let (base, len, ctxt2) = (lo.0, hi.0 - lo.0, ctxt.as_u32());

        if len <= MAX_LEN && ctxt2 <= MAX_CTXT {
            // Inline format.
            Span { base_or_index: base, len_or_tag: len as u16, ctxt_or_zero: ctxt2 as u16 }
        } else {
            // Interned format.
            let index = with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt }));
            Span { base_or_index: index, len_or_tag: LEN_TAG, ctxt_or_zero: 0 }
        }
    }

    #[inline]
    pub fn data(self) -> SpanData {
        if self.len_or_tag != LEN_TAG {
            // Inline format.
            debug_assert!(self.len_or_tag as u32 <= MAX_LEN);
            SpanData {
                lo: BytePos(self.base_or_index),
                hi: BytePos(self.base_or_index + self.len_or_tag as u32),
                ctxt: SyntaxContext::from_u32(self.ctxt_or_zero as u32),
            }
        } else {
            // Interned format.
            debug_assert!(self.ctxt_or_zero == 0);
            let index = self.base_or_index;
            with_span_interner(|interner| *interner.get(index))
        }
    }
}

#[derive(Default)]
pub struct SpanInterner {
    spans: FxIndexSet<SpanData>,
}

impl SpanInterner {
    fn intern(&mut self, span_data: &SpanData) -> u32 {
        let (index, _) = self.spans.insert_full(*span_data);
        index as u32
    }

    #[inline]
    fn get(&self, index: u32) -> &SpanData {
        &self.spans[index as usize]
    }
}

// If an interner exists, return it. Otherwise, prepare a fresh one.
#[inline]
fn with_span_interner<T, F: FnOnce(&mut SpanInterner) -> T>(f: F) -> T {
    SESSION_GLOBALS.with(|session_globals| f(&mut *session_globals.span_interner.lock()))
}
<commit_msg>Rollup merge of #84887 - jyn514:index-span, r=Xanewok<commit_after>// Spans are encoded using 1-bit tag and 2 different encoding formats (one for each tag value).
// One format is used for keeping span data inline,
// another contains index into an out-of-line span interner.
// The encoding format for inline spans were obtained by optimizing over crates in rustc/libstd.
// See https://internals.rust-lang.org/t/rfc-compiler-refactoring-spans/1357/28

use crate::hygiene::SyntaxContext;
use crate::SESSION_GLOBALS;
use crate::{BytePos, SpanData};

use rustc_data_structures::fx::FxIndexSet;

/// A compressed span.
///
/// Whereas [`SpanData`] is 12 bytes, which is a bit too big to stick everywhere, `Span`
/// is a form that only takes up 8 bytes, with less space for the length and
/// context. The vast majority (99.9%+) of `SpanData` instances will fit within
/// those 8 bytes; any `SpanData` whose fields don't fit into a `Span` are
/// stored in a separate interner table, and the `Span` will index into that
/// table. Interning is rare enough that the cost is low, but common enough
/// that the code is exercised regularly.
///
/// An earlier version of this code used only 4 bytes for `Span`, but that was
/// slower because only 80--90% of spans could be stored inline (even less in
/// very large crates) and so the interner was used a lot more.
///
/// Inline (compressed) format:
/// - `span.base_or_index == span_data.lo`
/// - `span.len_or_tag == len == span_data.hi - span_data.lo` (must be `<= MAX_LEN`)
/// - `span.ctxt == span_data.ctxt` (must be `<= MAX_CTXT`)
///
/// Interned format:
/// - `span.base_or_index == index` (indexes into the interner table)
/// - `span.len_or_tag == LEN_TAG` (high bit set, all other bits are zero)
/// - `span.ctxt == 0`
///
/// The inline form uses 0 for the tag value (rather than 1) so that we don't
/// need to mask out the tag bit when getting the length, and so that the
/// dummy span can be all zeroes.
///
/// Notes about the choice of field sizes:
/// - `base` is 32 bits in both `Span` and `SpanData`, which means that `base`
///   values never cause interning. The number of bits needed for `base`
///   depends on the crate size. 32 bits allows up to 4 GiB of code in a crate.
/// - `len` is 15 bits in `Span` (a u16, minus 1 bit for the tag) and 32 bits
///   in `SpanData`, which means that large `len` values will cause interning.
///   The number of bits needed for `len` does not depend on the crate size.
///   The most common numbers of bits for `len` are from 0 to 7, with a peak usually
///   at 3 or 4, and then it drops off quickly from 8 onwards. 15 bits is enough
///   for 99.99%+ of cases, but larger values (sometimes 20+ bits) might occur
///   dozens of times in a typical crate.
/// - `ctxt` is 16 bits in `Span` and 32 bits in `SpanData`, which means that
///   large `ctxt` values will cause interning. The number of bits needed for
///   `ctxt` values depend partly on the crate size and partly on the form of
///   the code. No crates in `rustc-perf` need more than 15 bits for `ctxt`,
///   but larger crates might need more than 16 bits.
///
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
pub struct Span {
    base_or_index: u32,
    len_or_tag: u16,
    ctxt_or_zero: u16,
}

const LEN_TAG: u16 = 0b1000_0000_0000_0000;
const MAX_LEN: u32 = 0b0111_1111_1111_1111;
const MAX_CTXT: u32 = 0b1111_1111_1111_1111;

/// Dummy span, both position and length are zero, syntax context is zero as well.
pub const DUMMY_SP: Span = Span { base_or_index: 0, len_or_tag: 0, ctxt_or_zero: 0 };

impl Span {
    #[inline]
    pub fn new(mut lo: BytePos, mut hi: BytePos, ctxt: SyntaxContext) -> Self {
        if lo > hi {
            std::mem::swap(&mut lo, &mut hi);
        }

        let (base, len, ctxt2) = (lo.0, hi.0 - lo.0, ctxt.as_u32());

        if len <= MAX_LEN && ctxt2 <= MAX_CTXT {
            // Inline format.
            Span { base_or_index: base, len_or_tag: len as u16, ctxt_or_zero: ctxt2 as u16 }
        } else {
            // Interned format.
            let index = with_span_interner(|interner| interner.intern(&SpanData { lo, hi, ctxt }));
            Span { base_or_index: index, len_or_tag: LEN_TAG, ctxt_or_zero: 0 }
        }
    }

    #[inline]
    pub fn data(self) -> SpanData {
        if self.len_or_tag != LEN_TAG {
            // Inline format.
            debug_assert!(self.len_or_tag as u32 <= MAX_LEN);
            SpanData {
                lo: BytePos(self.base_or_index),
                hi: BytePos(self.base_or_index + self.len_or_tag as u32),
                ctxt: SyntaxContext::from_u32(self.ctxt_or_zero as u32),
            }
        } else {
            // Interned format.
            debug_assert!(self.ctxt_or_zero == 0);
            let index = self.base_or_index;
            with_span_interner(|interner| interner.spans[index as usize])
        }
    }
}

#[derive(Default)]
pub struct SpanInterner {
    spans: FxIndexSet<SpanData>,
}

impl SpanInterner {
    fn intern(&mut self, span_data: &SpanData) -> u32 {
        let (index, _) = self.spans.insert_full(*span_data);
        index as u32
    }
}

// If an interner exists, return it. Otherwise, prepare a fresh one.
#[inline]
fn with_span_interner<T, F: FnOnce(&mut SpanInterner) -> T>(f: F) -> T {
    SESSION_GLOBALS.with(|session_globals| f(&mut *session_globals.span_interner.lock()))
}
<|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/. */

//! The bulk of the HTML parser integration is in `script::parse::html`.
//! This module is mostly about its interaction with DOM memory management.

use dom::bindings::codegen::Bindings::ServoHTMLParserBinding;
use dom::bindings::global;
use dom::bindings::trace::JSTraceable;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::node::TrustedNodeAddress;
use dom::document::Document;
use parse::html::JSMessage;

use std::default::Default;
use std::cell::RefCell;
use url::Url;
use js::jsapi::JSTracer;
use html5ever::tokenizer;
use html5ever::tree_builder;
use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts};

#[must_root]
#[jstraceable]
pub struct Sink {
    pub js_chan: Sender<JSMessage>,
    pub base_url: Option<Url>,
    pub document: JS<Document>,
}

pub type Tokenizer = tokenizer::Tokenizer<TreeBuilder<TrustedNodeAddress, Sink>>;

// NB: JSTraceable is *not* auto-derived.
// You must edit the impl below if you add fields!
#[must_root]
#[privatize]
pub struct ServoHTMLParser {
    reflector_: Reflector,
    tokenizer: RefCell<Tokenizer>,
}

impl ServoHTMLParser {
    #[allow(unrooted_must_root)]
    pub fn new(js_chan: Sender<JSMessage>, base_url: Option<Url>, document: JSRef<Document>)
            -> Temporary<ServoHTMLParser> {
        let window = document.window().root();
        let sink = Sink {
            js_chan: js_chan,
            base_url: base_url,
            document: JS::from_rooted(document),
        };

        let tb = TreeBuilder::new(sink, TreeBuilderOpts {
            ignore_missing_rules: true,
            .. Default::default()
        });

        let tok = tokenizer::Tokenizer::new(tb, Default::default());

        let parser = ServoHTMLParser {
            reflector_: Reflector::new(),
            tokenizer: RefCell::new(tok),
        };

        reflect_dom_object(box parser, &global::Window(*window), ServoHTMLParserBinding::Wrap)
    }

    #[inline]
    pub fn tokenizer<'a>(&'a self) -> &'a RefCell<Tokenizer> {
        &self.tokenizer
    }
}

impl Reflectable for ServoHTMLParser {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

struct Tracer {
    trc: *mut JSTracer,
}

impl tree_builder::Tracer<TrustedNodeAddress> for Tracer {
    fn trace_handle(&self, node: TrustedNodeAddress) {
        node.trace(self.trc);
    }
}

impl JSTraceable for ServoHTMLParser {
    fn trace(&self, trc: *mut JSTracer) {
        let tracer = Tracer {
            trc: trc,
        };
        let tracer = &tracer as &tree_builder::Tracer<TrustedNodeAddress>;

        self.reflector_.trace(trc);
        let tokenizer = self.tokenizer.borrow();
        let tree_builder = tokenizer.sink();
        tree_builder.trace_handles(tracer);
        tree_builder.sink().trace(trc);
    }
}
<commit_msg>Use DOMRefCell for ServoHTMLParser.<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/. */

//! The bulk of the HTML parser integration is in `script::parse::html`.
//! This module is mostly about its interaction with DOM memory management.

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::ServoHTMLParserBinding;
use dom::bindings::global;
use dom::bindings::trace::JSTraceable;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::node::TrustedNodeAddress;
use dom::document::Document;
use parse::html::JSMessage;

use std::default::Default;
use url::Url;
use js::jsapi::JSTracer;
use html5ever::tokenizer;
use html5ever::tree_builder;
use html5ever::tree_builder::{TreeBuilder, TreeBuilderOpts};

#[must_root]
#[jstraceable]
pub struct Sink {
    pub js_chan: Sender<JSMessage>,
    pub base_url: Option<Url>,
    pub document: JS<Document>,
}

pub type Tokenizer = tokenizer::Tokenizer<TreeBuilder<TrustedNodeAddress, Sink>>;

// NB: JSTraceable is *not* auto-derived.
// You must edit the impl below if you add fields!
#[must_root]
#[privatize]
pub struct ServoHTMLParser {
    reflector_: Reflector,
    tokenizer: DOMRefCell<Tokenizer>,
}

impl ServoHTMLParser {
    #[allow(unrooted_must_root)]
    pub fn new(js_chan: Sender<JSMessage>, base_url: Option<Url>, document: JSRef<Document>)
            -> Temporary<ServoHTMLParser> {
        let window = document.window().root();
        let sink = Sink {
            js_chan: js_chan,
            base_url: base_url,
            document: JS::from_rooted(document),
        };

        let tb = TreeBuilder::new(sink, TreeBuilderOpts {
            ignore_missing_rules: true,
            .. Default::default()
        });

        let tok = tokenizer::Tokenizer::new(tb, Default::default());

        let parser = ServoHTMLParser {
            reflector_: Reflector::new(),
            tokenizer: DOMRefCell::new(tok),
        };

        reflect_dom_object(box parser, &global::Window(*window), ServoHTMLParserBinding::Wrap)
    }

    #[inline]
    pub fn tokenizer<'a>(&'a self) -> &'a DOMRefCell<Tokenizer> {
        &self.tokenizer
    }
}

impl Reflectable for ServoHTMLParser {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        &self.reflector_
    }
}

struct Tracer {
    trc: *mut JSTracer,
}

impl tree_builder::Tracer<TrustedNodeAddress> for Tracer {
    fn trace_handle(&self, node: TrustedNodeAddress) {
        node.trace(self.trc);
    }
}

impl JSTraceable for ServoHTMLParser {
    fn trace(&self, trc: *mut JSTracer) {
        let tracer = Tracer {
            trc: trc,
        };
        let tracer = &tracer as &tree_builder::Tracer<TrustedNodeAddress>;

        self.reflector_.trace(trc);
        let tokenizer = self.tokenizer.borrow();
        let tree_builder = tokenizer.sink();
        tree_builder.trace_handles(tracer);
        tree_builder.sink().trace(trc);
    }
}
<|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/. */

//! Generic types for font stuff.

use app_units::Au;
use byteorder::{ReadBytesExt, BigEndian};
use cssparser::Parser;
use num_traits::One;
use parser::{Parse, ParserContext};
use std::fmt::{self, Write};
use std::io::Cursor;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use values::distance::{ComputeSquaredDistance, SquaredDistance};

/// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
pub struct FeatureTagValue<Integer> {
    /// A four-character tag, packed into a u32 (one byte per character).
    pub tag: FontTag,
    /// The actual value.
    pub value: Integer,
}

impl<Integer> ToCss for FeatureTagValue<Integer>
where
    Integer: One + ToCss + PartialEq,
{
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        self.tag.to_css(dest)?;
        // Don't serialize the default value.
        if self.value != Integer::one() {
            dest.write_char(' ')?;
            self.value.to_css(dest)?;
        }

        Ok(())
    }
}

/// Variation setting for a single feature, see:
///
/// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
#[derive(Animate, Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct VariationValue<Number> {
    /// A four-character tag, packed into a u32 (one byte per character).
    #[animation(constant)]
    pub tag: FontTag,
    /// The actual value.
    pub value: Number,
}

impl<Number> ComputeSquaredDistance for VariationValue<Number>
where
    Number: ComputeSquaredDistance,
{
    #[inline]
    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
        if self.tag != other.tag {
            return Err(());
        }
        self.value.compute_squared_distance(&other.value)
    }
}

/// A value both for font-variation-settings and font-feature-settings.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
pub struct FontSettings<T>(pub Box<[T]>);

impl<T> FontSettings<T> {
    /// Default value of font settings as `normal`.
    #[inline]
    pub fn normal() -> Self {
        FontSettings(vec![].into_boxed_slice())
    }
}

impl<T: Parse> Parse for FontSettings<T> {
    /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
    /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if input.try(|i| i.expect_ident_matching("normal")).is_ok() {
            return Ok(Self::normal());
        }

        Ok(FontSettings(
            input.parse_comma_separated(|i| T::parse(context, i))?.into_boxed_slice()
        ))
    }
}

impl<T: ToCss> ToCss for FontSettings<T> {
    /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
    /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        if self.0.is_empty() {
            return dest.write_str("normal");
        }

        let mut first = true;
        for item in self.0.iter() {
            if !first {
                dest.write_str(", ")?;
            }
            first = false;
            item.to_css(dest)?;
        }

        Ok(())
    }
}

/// A font four-character tag, represented as a u32 for convenience.
///
/// See:
///   https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
///   https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
///
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
pub struct FontTag(pub u32);

impl ToCss for FontTag {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        use byteorder::{BigEndian, ByteOrder};
        use std::str;

        let mut raw = [0u8; 4];
        BigEndian::write_u32(&mut raw, self.0);
        str::from_utf8(&raw).unwrap_or_default().to_css(dest)
    }
}

impl Parse for FontTag {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let location = input.current_source_location();
        let tag = input.expect_string()?;

        // allowed strings of length 4 containing chars: <U+20, U+7E>
        if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {
            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
        }

        let mut raw = Cursor::new(tag.as_bytes());
        Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))
    }
}

#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
#[derive(PartialEq, ToAnimatedValue, ToAnimatedZero, ToCss)]
/// Additional information for keyword-derived font sizes.
pub struct KeywordInfo<Length> {
    /// The keyword used
    pub kw: KeywordSize,
    /// A factor to be multiplied by the computed size of the keyword
    #[css(skip)]
    pub factor: f32,
    /// An additional Au offset to add to the kw*factor in the case of calcs
    #[css(skip)]
    pub offset: Length,
}

impl<L> KeywordInfo<L>
where
    Au: Into<L>,
{
    /// KeywordInfo value for font-size: medium
    pub fn medium() -> Self {
        KeywordSize::Medium.into()
    }
}

impl<L> From<KeywordSize> for KeywordInfo<L>
where
    Au: Into<L>,
{
    fn from(x: KeywordSize) -> Self {
        KeywordInfo {
            kw: x,
            factor: 1.,
            offset: Au(0).into(),
        }
    }
}

/// CSS font keywords
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
#[derive(PartialEq, ToAnimatedValue, ToAnimatedZero)]
#[allow(missing_docs)]
pub enum KeywordSize {
    XXSmall,
    XSmall,
    Small,
    Medium,
    Large,
    XLarge,
    XXLarge,
    // This is not a real font keyword and will not parse
    // HTML font-size 7 corresponds to this value
    XXXLarge,
}

impl KeywordSize {
    /// Convert to an HTML <font size> value
    #[inline]
    pub fn html_size(self) -> u8 {
        self as u8
    }
}

impl Default for KeywordSize {
    fn default() -> Self {
        KeywordSize::Medium
    }
}

impl ToCss for KeywordSize {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        dest.write_str(match *self {
            KeywordSize::XXSmall => "xx-small",
            KeywordSize::XSmall => "x-small",
            KeywordSize::Small => "small",
            KeywordSize::Medium => "medium",
            KeywordSize::Large => "large",
            KeywordSize::XLarge => "x-large",
            KeywordSize::XXLarge => "xx-large",
            KeywordSize::XXXLarge => {
                debug_assert!(
                    false,
                    "We should never serialize specified values set via HTML presentation attributes"
                );
                "-servo-xxx-large"
            },
        })
    }
}
<commit_msg>Derive ToCss for FontSettings<T><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/. */

//! Generic types for font stuff.

use app_units::Au;
use byteorder::{ReadBytesExt, BigEndian};
use cssparser::Parser;
use num_traits::One;
use parser::{Parse, ParserContext};
use std::fmt::{self, Write};
use std::io::Cursor;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use values::distance::{ComputeSquaredDistance, SquaredDistance};

/// https://drafts.csswg.org/css-fonts-4/#feature-tag-value
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
pub struct FeatureTagValue<Integer> {
    /// A four-character tag, packed into a u32 (one byte per character).
    pub tag: FontTag,
    /// The actual value.
    pub value: Integer,
}

impl<Integer> ToCss for FeatureTagValue<Integer>
where
    Integer: One + ToCss + PartialEq,
{
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        self.tag.to_css(dest)?;
        // Don't serialize the default value.
        if self.value != Integer::one() {
            dest.write_char(' ')?;
            self.value.to_css(dest)?;
        }

        Ok(())
    }
}

/// Variation setting for a single feature, see:
///
/// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
#[derive(Animate, Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct VariationValue<Number> {
    /// A four-character tag, packed into a u32 (one byte per character).
    #[animation(constant)]
    pub tag: FontTag,
    /// The actual value.
    pub value: Number,
}

impl<Number> ComputeSquaredDistance for VariationValue<Number>
where
    Number: ComputeSquaredDistance,
{
    #[inline]
    fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
        if self.tag != other.tag {
            return Err(());
        }
        self.value.compute_squared_distance(&other.value)
    }
}

/// A value both for font-variation-settings and font-feature-settings.
#[css(comma)]
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue, ToCss)]
pub struct FontSettings<T>(#[css(if_empty = "normal", iterable)] pub Box<[T]>);

impl<T> FontSettings<T> {
    /// Default value of font settings as `normal`.
    #[inline]
    pub fn normal() -> Self {
        FontSettings(vec![].into_boxed_slice())
    }
}

impl<T: Parse> Parse for FontSettings<T> {
    /// https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
    /// https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if input.try(|i| i.expect_ident_matching("normal")).is_ok() {
            return Ok(Self::normal());
        }

        Ok(FontSettings(
            input.parse_comma_separated(|i| T::parse(context, i))?.into_boxed_slice()
        ))
    }
}

/// A font four-character tag, represented as a u32 for convenience.
///
/// See:
///   https://drafts.csswg.org/css-fonts-4/#font-variation-settings-def
///   https://drafts.csswg.org/css-fonts-4/#descdef-font-face-font-feature-settings
///
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToComputedValue)]
pub struct FontTag(pub u32);

impl ToCss for FontTag {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        use byteorder::{BigEndian, ByteOrder};
        use std::str;

        let mut raw = [0u8; 4];
        BigEndian::write_u32(&mut raw, self.0);
        str::from_utf8(&raw).unwrap_or_default().to_css(dest)
    }
}

impl Parse for FontTag {
    fn parse<'i, 't>(
        _context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let location = input.current_source_location();
        let tag = input.expect_string()?;

        // allowed strings of length 4 containing chars: <U+20, U+7E>
        if tag.len() != 4 || tag.as_bytes().iter().any(|c| *c < b' ' || *c > b'~') {
            return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError))
        }

        let mut raw = Cursor::new(tag.as_bytes());
        Ok(FontTag(raw.read_u32::<BigEndian>().unwrap()))
    }
}

#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
#[derive(PartialEq, ToAnimatedValue, ToAnimatedZero, ToCss)]
/// Additional information for keyword-derived font sizes.
pub struct KeywordInfo<Length> {
    /// The keyword used
    pub kw: KeywordSize,
    /// A factor to be multiplied by the computed size of the keyword
    #[css(skip)]
    pub factor: f32,
    /// An additional Au offset to add to the kw*factor in the case of calcs
    #[css(skip)]
    pub offset: Length,
}

impl<L> KeywordInfo<L>
where
    Au: Into<L>,
{
    /// KeywordInfo value for font-size: medium
    pub fn medium() -> Self {
        KeywordSize::Medium.into()
    }
}

impl<L> From<KeywordSize> for KeywordInfo<L>
where
    Au: Into<L>,
{
    fn from(x: KeywordSize) -> Self {
        KeywordInfo {
            kw: x,
            factor: 1.,
            offset: Au(0).into(),
        }
    }
}

/// CSS font keywords
#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf)]
#[derive(PartialEq, ToAnimatedValue, ToAnimatedZero)]
#[allow(missing_docs)]
pub enum KeywordSize {
    XXSmall,
    XSmall,
    Small,
    Medium,
    Large,
    XLarge,
    XXLarge,
    // This is not a real font keyword and will not parse
    // HTML font-size 7 corresponds to this value
    XXXLarge,
}

impl KeywordSize {
    /// Convert to an HTML <font size> value
    #[inline]
    pub fn html_size(self) -> u8 {
        self as u8
    }
}

impl Default for KeywordSize {
    fn default() -> Self {
        KeywordSize::Medium
    }
}

impl ToCss for KeywordSize {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        dest.write_str(match *self {
            KeywordSize::XXSmall => "xx-small",
            KeywordSize::XSmall => "x-small",
            KeywordSize::Small => "small",
            KeywordSize::Medium => "medium",
            KeywordSize::Large => "large",
            KeywordSize::XLarge => "x-large",
            KeywordSize::XXLarge => "xx-large",
            KeywordSize::XXXLarge => {
                debug_assert!(
                    false,
                    "We should never serialize specified values set via HTML presentation attributes"
                );
                "-servo-xxx-large"
            },
        })
    }
}
<|endoftext|>