<commit_before>//! FIXME: write short doc here

mod load_cargo;
mod analysis_stats;
mod analysis_bench;
mod progress_report;

use std::{error::Error, fmt::Write, io::Read};

use pico_args::Arguments;
use ra_ide::{file_structure, Analysis};
use ra_prof::profile;
use ra_syntax::{AstNode, SourceFile};

type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;

fn main() -> Result<()> {
    env_logger::try_init()?;

    let command = Command::from_args()?;
    command.run()?;

    Ok(())
}

#[derive(Clone, Copy)]
pub enum Verbosity {
    Spammy,
    Verbose,
    Normal,
    Quiet,
}

impl Verbosity {
    fn is_verbose(self) -> bool {
        match self {
            Verbosity::Verbose | Verbosity::Spammy => true,
            _ => false,
        }
    }
    fn is_spammy(self) -> bool {
        match self {
            Verbosity::Spammy => true,
            _ => false,
        }
    }
}

enum Command {
    Parse {
        no_dump: bool,
    },
    Symbols,
    Highlight {
        rainbow: bool,
    },
    Stats {
        verbosity: Verbosity,
        randomize: bool,
        memory_usage: bool,
        only: Option<String>,
        with_deps: bool,
        path: String,
    },
    Bench {
        verbose: bool,
        path: String,
        op: analysis_bench::Op,
    },
    HelpPrinted,
}

impl Command {
    fn from_args() -> Result<Command> {
        let mut matches = Arguments::from_env();
        let subcommand = matches.subcommand()?.unwrap_or_default();

        let command = match subcommand.as_str() {
            "parse" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-parse

USAGE:
    ra_cli parse [FLAGS]

FLAGS:
    -h, --help       Prints help inforamtion
        --no-dump"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let no_dump = matches.contains("--no-dump");
                matches.finish().or_else(handle_extra_flags)?;
                Command::Parse { no_dump }
            }
            "symbols" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-symbols

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help    Prints help inforamtion"
                    );
                    return Ok(Command::HelpPrinted);
                }

                matches.finish().or_else(handle_extra_flags)?;

                Command::Symbols
            }
            "highlight" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-highlight

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help       Prints help information
    -r, --rainbow"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let rainbow = matches.contains(["-r", "--rainbow"]);
                matches.finish().or_else(handle_extra_flags)?;
                Command::Highlight { rainbow }
            }
            "analysis-stats" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-analysis-stats

USAGE:
    ra_cli analysis-stats [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help            Prints help information
        --memory-usage
    -v, --verbose
    -q, --quiet

OPTIONS:
    -o <ONLY>

ARGS:
    <PATH>"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let verbosity = match (
                    matches.contains(["-vv", "--spammy"]),
                    matches.contains(["-v", "--verbose"]),
                    matches.contains(["-q", "--quiet"]),
                ) {
                    (true, _, true) => Err("Invalid flags: -q conflicts with -vv")?,
                    (true, _, false) => Verbosity::Spammy,
                    (false, false, false) => Verbosity::Normal,
                    (false, false, true) => Verbosity::Quiet,
                    (false, true, false) => Verbosity::Verbose,
                    (false, true, true) => Err("Invalid flags: -q conflicts with -v")?,
                };
                let randomize = matches.contains("--randomize");
                let memory_usage = matches.contains("--memory-usage");
                let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
                let with_deps: bool = matches.contains("--with-deps");
                let path = {
                    let mut trailing = matches.free()?;
                    if trailing.len() != 1 {
                        Err("Invalid flags")?;
                    }
                    trailing.pop().unwrap()
                };

                Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path }
            }
            "analysis-bench" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra_cli-analysis-bench

USAGE:
    ra_cli analysis-bench [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help        Prints help information
    -v, --verbose

OPTIONS:
    --complete <PATH:LINE:COLUMN>    Compute completions at this location
    --highlight <PATH>               Hightlight this file

ARGS:
    <PATH>    Project to analyse"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let verbose = matches.contains(["-v", "--verbose"]);
                let path: String = matches.opt_value_from_str("--path")?.unwrap_or_default();
                let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
                let complete_path: Option<String> = matches.opt_value_from_str("--complete")?;
                let goto_def_path: Option<String> = matches.opt_value_from_str("--goto-def")?;
                let op = match (highlight_path, complete_path, goto_def_path) {
                    (Some(path), None, None) => analysis_bench::Op::Highlight { path: path.into() },
                    (None, Some(position), None) => analysis_bench::Op::Complete(position.parse()?),
                    (None, None, Some(position)) => analysis_bench::Op::GotoDef(position.parse()?),
                    _ => panic!(
                        "exactly one of  `--highlight`, `--complete` or `--goto-def` must be set"
                    ),
                };
                Command::Bench { verbose, path, op }
            }
            _ => {
                eprintln!(
                    "\
ra-cli

USAGE:
    ra_cli <SUBCOMMAND>

FLAGS:
    -h, --help        Prints help information

SUBCOMMANDS:
    analysis-bench
    analysis-stats
    highlight
    parse
    symbols"
                );
                return Ok(Command::HelpPrinted);
            }
        };
        Ok(command)
    }

    fn run(self) -> Result<()> {
        match self {
            Command::Parse { no_dump } => {
                let _p = profile("parsing");
                let file = file()?;
                if !no_dump {
                    println!("{:#?}", file.syntax());
                }
                std::mem::forget(file);
            }
            Command::Symbols => {
                let file = file()?;
                for s in file_structure(&file) {
                    println!("{:?}", s);
                }
            }
            Command::Highlight { rainbow } => {
                let (analysis, file_id) = Analysis::from_single_file(read_stdin()?);
                let html = analysis.highlight_as_html(file_id, rainbow).unwrap();
                println!("{}", html);
            }
            Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path } => {
                analysis_stats::run(
                    verbosity,
                    memory_usage,
                    path.as_ref(),
                    only.as_ref().map(String::as_ref),
                    with_deps,
                    randomize,
                )?;
            }
            Command::Bench { verbose, path, op } => {
                analysis_bench::run(verbose, path.as_ref(), op)?;
            }
            Command::HelpPrinted => (),
        }
        Ok(())
    }
}

fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
    if let pico_args::Error::UnusedArgsLeft(flags) = e {
        let mut invalid_flags = String::new();
        for flag in flags {
            write!(&mut invalid_flags, "{}, ", flag)?;
        }
        let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
        Err(format!("Invalid flags: {}", invalid_flags).into())
    } else {
        Err(e.to_string().into())
    }
}

fn file() -> Result<SourceFile> {
    let text = read_stdin()?;
    Ok(SourceFile::parse(&text).tree())
}

fn read_stdin() -> Result<String> {
    let mut buff = String::new();
    std::io::stdin().read_to_string(&mut buff)?;
    Ok(buff)
}
<commit_msg>Stronger Types<commit_after>//! FIXME: write short doc here

mod load_cargo;
mod analysis_stats;
mod analysis_bench;
mod progress_report;

use std::{error::Error, fmt::Write, io::Read, path::PathBuf};

use pico_args::Arguments;
use ra_ide::{file_structure, Analysis};
use ra_prof::profile;
use ra_syntax::{AstNode, SourceFile};

type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;

fn main() -> Result<()> {
    env_logger::try_init()?;

    let command = Command::from_args()?;
    command.run()?;

    Ok(())
}

#[derive(Clone, Copy)]
pub enum Verbosity {
    Spammy,
    Verbose,
    Normal,
    Quiet,
}

impl Verbosity {
    fn is_verbose(self) -> bool {
        match self {
            Verbosity::Verbose | Verbosity::Spammy => true,
            _ => false,
        }
    }
    fn is_spammy(self) -> bool {
        match self {
            Verbosity::Spammy => true,
            _ => false,
        }
    }
}

enum Command {
    Parse {
        no_dump: bool,
    },
    Symbols,
    Highlight {
        rainbow: bool,
    },
    Stats {
        verbosity: Verbosity,
        randomize: bool,
        memory_usage: bool,
        only: Option<String>,
        with_deps: bool,
        path: PathBuf,
    },
    Bench {
        verbose: bool,
        path: PathBuf,
        op: analysis_bench::Op,
    },
    HelpPrinted,
}

impl Command {
    fn from_args() -> Result<Command> {
        let mut matches = Arguments::from_env();
        let subcommand = matches.subcommand()?.unwrap_or_default();

        let command = match subcommand.as_str() {
            "parse" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-parse

USAGE:
    ra_cli parse [FLAGS]

FLAGS:
    -h, --help       Prints help inforamtion
        --no-dump"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let no_dump = matches.contains("--no-dump");
                matches.finish().or_else(handle_extra_flags)?;
                Command::Parse { no_dump }
            }
            "symbols" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-symbols

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help    Prints help inforamtion"
                    );
                    return Ok(Command::HelpPrinted);
                }

                matches.finish().or_else(handle_extra_flags)?;

                Command::Symbols
            }
            "highlight" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-highlight

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help       Prints help information
    -r, --rainbow"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let rainbow = matches.contains(["-r", "--rainbow"]);
                matches.finish().or_else(handle_extra_flags)?;
                Command::Highlight { rainbow }
            }
            "analysis-stats" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-analysis-stats

USAGE:
    ra_cli analysis-stats [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help            Prints help information
        --memory-usage
    -v, --verbose
    -q, --quiet

OPTIONS:
    -o <ONLY>

ARGS:
    <PATH>"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let verbosity = match (
                    matches.contains(["-vv", "--spammy"]),
                    matches.contains(["-v", "--verbose"]),
                    matches.contains(["-q", "--quiet"]),
                ) {
                    (true, _, true) => Err("Invalid flags: -q conflicts with -vv")?,
                    (true, _, false) => Verbosity::Spammy,
                    (false, false, false) => Verbosity::Normal,
                    (false, false, true) => Verbosity::Quiet,
                    (false, true, false) => Verbosity::Verbose,
                    (false, true, true) => Err("Invalid flags: -q conflicts with -v")?,
                };
                let randomize = matches.contains("--randomize");
                let memory_usage = matches.contains("--memory-usage");
                let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
                let with_deps: bool = matches.contains("--with-deps");
                let path = {
                    let mut trailing = matches.free()?;
                    if trailing.len() != 1 {
                        Err("Invalid flags")?;
                    }
                    trailing.pop().unwrap().into()
                };

                Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path }
            }
            "analysis-bench" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra_cli-analysis-bench

USAGE:
    ra_cli analysis-bench [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help        Prints help information
    -v, --verbose

OPTIONS:
    --complete <PATH:LINE:COLUMN>    Compute completions at this location
    --highlight <PATH>               Hightlight this file

ARGS:
    <PATH>    Project to analyse"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let verbose = matches.contains(["-v", "--verbose"]);
                let path: PathBuf = matches.opt_value_from_str("--path")?.unwrap_or_default();
                let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
                let complete_path: Option<String> = matches.opt_value_from_str("--complete")?;
                let goto_def_path: Option<String> = matches.opt_value_from_str("--goto-def")?;
                let op = match (highlight_path, complete_path, goto_def_path) {
                    (Some(path), None, None) => analysis_bench::Op::Highlight { path: path.into() },
                    (None, Some(position), None) => analysis_bench::Op::Complete(position.parse()?),
                    (None, None, Some(position)) => analysis_bench::Op::GotoDef(position.parse()?),
                    _ => panic!(
                        "exactly one of  `--highlight`, `--complete` or `--goto-def` must be set"
                    ),
                };
                Command::Bench { verbose, path, op }
            }
            _ => {
                eprintln!(
                    "\
ra-cli

USAGE:
    ra_cli <SUBCOMMAND>

FLAGS:
    -h, --help        Prints help information

SUBCOMMANDS:
    analysis-bench
    analysis-stats
    highlight
    parse
    symbols"
                );
                return Ok(Command::HelpPrinted);
            }
        };
        Ok(command)
    }

    fn run(self) -> Result<()> {
        match self {
            Command::Parse { no_dump } => {
                let _p = profile("parsing");
                let file = file()?;
                if !no_dump {
                    println!("{:#?}", file.syntax());
                }
                std::mem::forget(file);
            }
            Command::Symbols => {
                let file = file()?;
                for s in file_structure(&file) {
                    println!("{:?}", s);
                }
            }
            Command::Highlight { rainbow } => {
                let (analysis, file_id) = Analysis::from_single_file(read_stdin()?);
                let html = analysis.highlight_as_html(file_id, rainbow).unwrap();
                println!("{}", html);
            }
            Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path } => {
                analysis_stats::run(
                    verbosity,
                    memory_usage,
                    path.as_ref(),
                    only.as_ref().map(String::as_ref),
                    with_deps,
                    randomize,
                )?;
            }
            Command::Bench { verbose, path, op } => {
                analysis_bench::run(verbose, path.as_ref(), op)?;
            }
            Command::HelpPrinted => (),
        }
        Ok(())
    }
}

fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
    if let pico_args::Error::UnusedArgsLeft(flags) = e {
        let mut invalid_flags = String::new();
        for flag in flags {
            write!(&mut invalid_flags, "{}, ", flag)?;
        }
        let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
        Err(format!("Invalid flags: {}", invalid_flags).into())
    } else {
        Err(e.to_string().into())
    }
}

fn file() -> Result<SourceFile> {
    let text = read_stdin()?;
    Ok(SourceFile::parse(&text).tree())
}

fn read_stdin() -> Result<String> {
    let mut buff = String::new();
    std::io::stdin().read_to_string(&mut buff)?;
    Ok(buff)
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

mod memory_usage;
#[cfg(feature = "cpu_profiler")]
mod google_cpu_profiler;

use std::{
    cell::RefCell,
    collections::HashSet,
    io::{stderr, Write},
    iter::repeat,
    mem,
    sync::{
        atomic::{AtomicBool, Ordering},
        RwLock,
    },
    time::{Duration, Instant},
};

use itertools::Itertools;
use once_cell::sync::Lazy;

pub use crate::memory_usage::{Bytes, MemoryUsage};

// We use jemalloc mainly to get heap usage statistics, actual performance
// difference is not measures.
#[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

/// Set profiling filter. It specifies descriptions allowed to profile.
/// This is helpful when call stack has too many nested profiling scopes.
/// Additionally filter can specify maximum depth of profiling scopes nesting.
///
/// #Example
/// ```
/// use ra_prof::{set_filter, Filter};
/// let f = Filter::from_spec("profile1|profile2@2");
/// set_filter(f);
/// ```
pub fn set_filter(f: Filter) {
    PROFILING_ENABLED.store(f.depth > 0, Ordering::SeqCst);
    let set: HashSet<_> = f.allowed.iter().cloned().collect();
    let mut old = FILTER.write().unwrap();
    let filter_data = FilterData {
        depth: f.depth,
        allowed: set,
        longer_than: f.longer_than,
        version: old.version + 1,
    };
    *old = filter_data;
}

/// This function starts a profiling scope in the current execution stack with a given description.
/// It returns a Profile structure and measure elapsed time between this method invocation and Profile structure drop.
/// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output.
/// Profiling information is being printed in the stderr.
///
/// # Example
/// ```
/// use ra_prof::{profile, set_filter, Filter};
///
/// let f = Filter::from_spec("profile1|profile2@2");
/// set_filter(f);
/// profiling_function1();
///
/// fn profiling_function1() {
///     let _p = profile("profile1");
///     profiling_function2();
/// }
///
/// fn profiling_function2() {
///     let _p = profile("profile2");
/// }
/// ```
/// This will print in the stderr the following:
/// ```text
///  0ms - profile
///      0ms - profile2
/// ```
pub fn profile(desc: &str) -> Profiler {
    assert!(!desc.is_empty());
    if !PROFILING_ENABLED.load(Ordering::Relaxed) {
        return Profiler { desc: None };
    }

    PROFILE_STACK.with(|stack| {
        let mut stack = stack.borrow_mut();
        if stack.starts.is_empty() {
            if let Ok(f) = FILTER.try_read() {
                if f.version > stack.filter_data.version {
                    stack.filter_data = f.clone();
                }
            };
        }
        if stack.starts.len() > stack.filter_data.depth {
            return Profiler { desc: None };
        }
        let allowed = &stack.filter_data.allowed;
        if stack.starts.is_empty() && !allowed.is_empty() && !allowed.contains(desc) {
            return Profiler { desc: None };
        }

        stack.starts.push(Instant::now());
        Profiler { desc: Some(desc.to_string()) }
    })
}

pub struct Profiler {
    desc: Option<String>,
}

pub struct Filter {
    depth: usize,
    allowed: Vec<String>,
    longer_than: Duration,
}

impl Filter {
    // Filtering syntax
    // env RA_PROFILE=*             // dump everything
    // env RA_PROFILE=foo|bar|baz   // enabled only selected entries
    // env RA_PROFILE=*@3>10        // dump everything, up to depth 3, if it takes more than 10 ms
    pub fn from_spec(mut spec: &str) -> Filter {
        let longer_than = if let Some(idx) = spec.rfind('>') {
            let longer_than = spec[idx + 1..].parse().expect("invalid profile longer_than");
            spec = &spec[..idx];
            Duration::from_millis(longer_than)
        } else {
            Duration::new(0, 0)
        };

        let depth = if let Some(idx) = spec.rfind('@') {
            let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
            spec = &spec[..idx];
            depth
        } else {
            999
        };
        let allowed =
            if spec == "*" { Vec::new() } else { spec.split('|').map(String::from).collect() };
        Filter::new(depth, allowed, longer_than)
    }

    pub fn disabled() -> Filter {
        Filter::new(0, Vec::new(), Duration::new(0, 0))
    }

    pub fn new(depth: usize, allowed: Vec<String>, longer_than: Duration) -> Filter {
        Filter { depth, allowed, longer_than }
    }
}

struct ProfileStack {
    starts: Vec<Instant>,
    messages: Vec<Message>,
    filter_data: FilterData,
}

struct Message {
    level: usize,
    duration: Duration,
    message: String,
}

impl ProfileStack {
    fn new() -> ProfileStack {
        ProfileStack { starts: Vec::new(), messages: Vec::new(), filter_data: Default::default() }
    }
}

#[derive(Default, Clone)]
struct FilterData {
    depth: usize,
    version: usize,
    allowed: HashSet<String>,
    longer_than: Duration,
}

static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);

static FILTER: Lazy<RwLock<FilterData>> = Lazy::new(Default::default);

thread_local!(static PROFILE_STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new()));

impl Drop for Profiler {
    fn drop(&mut self) {
        match self {
            Profiler { desc: Some(desc) } => {
                PROFILE_STACK.with(|stack| {
                    let mut stack = stack.borrow_mut();
                    let start = stack.starts.pop().unwrap();
                    let duration = start.elapsed();
                    let level = stack.starts.len();
                    let message = mem::replace(desc, String::new());
                    stack.messages.push(Message { level, duration, message });
                    if level == 0 {
                        let stdout = stderr();
                        let longer_than = stack.filter_data.longer_than;
                        // Convert to millis for comparison to avoid problems with rounding
                        // (otherwise we could print `0ms` despite user's `>0` filter when
                        // `duration` is just a few nanos).
                        if duration.as_millis() > longer_than.as_millis() {
                            print(0, &stack.messages, &mut stdout.lock(), longer_than, None);
                        }
                        stack.messages.clear();
                    }
                });
            }
            Profiler { desc: None } => (),
        }
    }
}

fn print(
    lvl: usize,
    msgs: &[Message],
    out: &mut impl Write,
    longer_than: Duration,
    total: Option<Duration>,
) {
    let mut last = 0;
    let indent = repeat("    ").take(lvl).collect::<String>();
    // We output hierarchy for long calls, but sum up all short calls
    let mut short = Vec::new();
    let mut accounted_for = Duration::default();
    for (i, &Message { level, duration, message: ref msg }) in msgs.iter().enumerate() {
        if level != lvl {
            continue;
        }
        accounted_for += duration;
        if duration.as_millis() > longer_than.as_millis() {
            writeln!(out, "{}{:5}ms - {}", indent, duration.as_millis(), msg)
                .expect("printing profiling info to stdout");

            print(lvl + 1, &msgs[last..i], out, longer_than, Some(duration));
        } else {
            short.push((msg, duration))
        }

        last = i;
    }
    short.sort_by_key(|(msg, _time)| *msg);
    for (msg, entires) in short.iter().group_by(|(msg, _time)| msg).into_iter() {
        let mut count = 0;
        let mut total_duration = Duration::default();
        entires.for_each(|(_msg, time)| {
            count += 1;
            total_duration += *time;
        });
        writeln!(out, "{}{:5}ms - {} ({} calls)", indent, total_duration.as_millis(), msg, count)
            .expect("printing profiling info to stdout");
    }

    if let Some(total) = total {
        if let Some(unaccounted) = total.checked_sub(accounted_for) {
            let unaccounted_millis = unaccounted.as_millis();
            if unaccounted_millis > longer_than.as_millis() && unaccounted_millis > 0 && last > 0 {
                writeln!(out, "{}{:5}ms - ???", indent, unaccounted_millis)
                    .expect("printing profiling info to stdout");
            }
        }
    }
}

/// Prints backtrace to stderr, useful for debugging.
pub fn print_backtrace() {
    let bt = backtrace::Backtrace::new();
    eprintln!("{:?}", bt);
}

thread_local!(static IN_SCOPE: RefCell<bool> = RefCell::new(false));

/// Allows to check if the current code is withing some dynamic scope, can be
/// useful during debugging to figure out why a function is called.
pub struct Scope {
    prev: bool,
}

impl Scope {
    pub fn enter() -> Scope {
        let prev = IN_SCOPE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), true));
        Scope { prev }
    }
    pub fn is_active() -> bool {
        IN_SCOPE.with(|slot| *slot.borrow())
    }
}

impl Drop for Scope {
    fn drop(&mut self) {
        IN_SCOPE.with(|slot| *slot.borrow_mut() = self.prev);
    }
}

/// A wrapper around google_cpu_profiler.
///
/// Usage:
/// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro.
/// 2. Build with `cpu_profiler` feature.
/// 3. Tun the code, the *raw* output would be in the `./out.profile` file.
/// 4. Install pprof for visualization (https://github.com/google/pprof).
/// 5. Use something like `pprof -svg target/release/ra_cli ./out.profile` to see the results.
///
/// For example, here's how I run profiling on NixOS:
///
/// ```bash
/// $ nix-shell -p gperftools --run \
///     'cargo run --release -p ra_cli -- parse < ~/projects/rustbench/parser.rs > /dev/null'
/// ```
#[derive(Debug)]
pub struct CpuProfiler {
    _private: (),
}

pub fn cpu_profiler() -> CpuProfiler {
    #[cfg(feature = "cpu_profiler")]
    {
        google_cpu_profiler::start("./out.profile".as_ref())
    }

    #[cfg(not(feature = "cpu_profiler"))]
    {
        eprintln!("cpu_profiler feature is disabled")
    }

    CpuProfiler { _private: () }
}

impl Drop for CpuProfiler {
    fn drop(&mut self) {
        #[cfg(feature = "cpu_profiler")]
        {
            google_cpu_profiler::stop()
        }
    }
}

pub fn memory_usage() -> MemoryUsage {
    MemoryUsage::current()
}

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

    #[test]
    fn test_basic_profile() {
        let s = vec!["profile1".to_string(), "profile2".to_string()];
        let f = Filter::new(2, s, Duration::new(0, 0));
        set_filter(f);
        profiling_function1();
    }

    fn profiling_function1() {
        let _p = profile("profile1");
        profiling_function2();
    }

    fn profiling_function2() {
        let _p = profile("profile2");
    }

    #[test]
    fn test_longer_than() {
        let mut result = vec![];
        let msgs = vec![
            Message { level: 1, duration: Duration::from_nanos(3), message: "bar".to_owned() },
            Message { level: 1, duration: Duration::from_nanos(2), message: "bar".to_owned() },
            Message { level: 0, duration: Duration::from_millis(1), message: "foo".to_owned() },
        ];
        print(0, &msgs, &mut result, Duration::from_millis(0), Some(Duration::from_millis(1)));
        // The calls to `bar` are so short that they'll be rounded to 0ms and should get collapsed
        // when printing.
        assert_eq!(
            std::str::from_utf8(&result).unwrap(),
            "    1ms - foo\n        0ms - bar (2 calls)\n"
        );
    }
}
<commit_msg>Fix a corner case when printing unaccounted time<commit_after>//! FIXME: write short doc here

mod memory_usage;
#[cfg(feature = "cpu_profiler")]
mod google_cpu_profiler;

use std::{
    cell::RefCell,
    collections::HashSet,
    io::{stderr, Write},
    iter::repeat,
    mem,
    sync::{
        atomic::{AtomicBool, Ordering},
        RwLock,
    },
    time::{Duration, Instant},
};

use itertools::Itertools;
use once_cell::sync::Lazy;

pub use crate::memory_usage::{Bytes, MemoryUsage};

// We use jemalloc mainly to get heap usage statistics, actual performance
// difference is not measures.
#[cfg(all(feature = "jemalloc", not(target_env = "msvc")))]
#[global_allocator]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

/// Set profiling filter. It specifies descriptions allowed to profile.
/// This is helpful when call stack has too many nested profiling scopes.
/// Additionally filter can specify maximum depth of profiling scopes nesting.
///
/// #Example
/// ```
/// use ra_prof::{set_filter, Filter};
/// let f = Filter::from_spec("profile1|profile2@2");
/// set_filter(f);
/// ```
pub fn set_filter(f: Filter) {
    PROFILING_ENABLED.store(f.depth > 0, Ordering::SeqCst);
    let set: HashSet<_> = f.allowed.iter().cloned().collect();
    let mut old = FILTER.write().unwrap();
    let filter_data = FilterData {
        depth: f.depth,
        allowed: set,
        longer_than: f.longer_than,
        version: old.version + 1,
    };
    *old = filter_data;
}

/// This function starts a profiling scope in the current execution stack with a given description.
/// It returns a Profile structure and measure elapsed time between this method invocation and Profile structure drop.
/// It supports nested profiling scopes in case when this function invoked multiple times at the execution stack. In this case the profiling information will be nested at the output.
/// Profiling information is being printed in the stderr.
///
/// # Example
/// ```
/// use ra_prof::{profile, set_filter, Filter};
///
/// let f = Filter::from_spec("profile1|profile2@2");
/// set_filter(f);
/// profiling_function1();
///
/// fn profiling_function1() {
///     let _p = profile("profile1");
///     profiling_function2();
/// }
///
/// fn profiling_function2() {
///     let _p = profile("profile2");
/// }
/// ```
/// This will print in the stderr the following:
/// ```text
///  0ms - profile
///      0ms - profile2
/// ```
pub fn profile(desc: &str) -> Profiler {
    assert!(!desc.is_empty());
    if !PROFILING_ENABLED.load(Ordering::Relaxed) {
        return Profiler { desc: None };
    }

    PROFILE_STACK.with(|stack| {
        let mut stack = stack.borrow_mut();
        if stack.starts.is_empty() {
            if let Ok(f) = FILTER.try_read() {
                if f.version > stack.filter_data.version {
                    stack.filter_data = f.clone();
                }
            };
        }
        if stack.starts.len() > stack.filter_data.depth {
            return Profiler { desc: None };
        }
        let allowed = &stack.filter_data.allowed;
        if stack.starts.is_empty() && !allowed.is_empty() && !allowed.contains(desc) {
            return Profiler { desc: None };
        }

        stack.starts.push(Instant::now());
        Profiler { desc: Some(desc.to_string()) }
    })
}

pub struct Profiler {
    desc: Option<String>,
}

pub struct Filter {
    depth: usize,
    allowed: Vec<String>,
    longer_than: Duration,
}

impl Filter {
    // Filtering syntax
    // env RA_PROFILE=*             // dump everything
    // env RA_PROFILE=foo|bar|baz   // enabled only selected entries
    // env RA_PROFILE=*@3>10        // dump everything, up to depth 3, if it takes more than 10 ms
    pub fn from_spec(mut spec: &str) -> Filter {
        let longer_than = if let Some(idx) = spec.rfind('>') {
            let longer_than = spec[idx + 1..].parse().expect("invalid profile longer_than");
            spec = &spec[..idx];
            Duration::from_millis(longer_than)
        } else {
            Duration::new(0, 0)
        };

        let depth = if let Some(idx) = spec.rfind('@') {
            let depth: usize = spec[idx + 1..].parse().expect("invalid profile depth");
            spec = &spec[..idx];
            depth
        } else {
            999
        };
        let allowed =
            if spec == "*" { Vec::new() } else { spec.split('|').map(String::from).collect() };
        Filter::new(depth, allowed, longer_than)
    }

    pub fn disabled() -> Filter {
        Filter::new(0, Vec::new(), Duration::new(0, 0))
    }

    pub fn new(depth: usize, allowed: Vec<String>, longer_than: Duration) -> Filter {
        Filter { depth, allowed, longer_than }
    }
}

struct ProfileStack {
    starts: Vec<Instant>,
    messages: Vec<Message>,
    filter_data: FilterData,
}

struct Message {
    level: usize,
    duration: Duration,
    message: String,
}

impl ProfileStack {
    fn new() -> ProfileStack {
        ProfileStack { starts: Vec::new(), messages: Vec::new(), filter_data: Default::default() }
    }
}

#[derive(Default, Clone)]
struct FilterData {
    depth: usize,
    version: usize,
    allowed: HashSet<String>,
    longer_than: Duration,
}

static PROFILING_ENABLED: AtomicBool = AtomicBool::new(false);

static FILTER: Lazy<RwLock<FilterData>> = Lazy::new(Default::default);

thread_local!(static PROFILE_STACK: RefCell<ProfileStack> = RefCell::new(ProfileStack::new()));

impl Drop for Profiler {
    fn drop(&mut self) {
        match self {
            Profiler { desc: Some(desc) } => {
                PROFILE_STACK.with(|stack| {
                    let mut stack = stack.borrow_mut();
                    let start = stack.starts.pop().unwrap();
                    let duration = start.elapsed();
                    let level = stack.starts.len();
                    let message = mem::replace(desc, String::new());
                    stack.messages.push(Message { level, duration, message });
                    if level == 0 {
                        let stdout = stderr();
                        let longer_than = stack.filter_data.longer_than;
                        // Convert to millis for comparison to avoid problems with rounding
                        // (otherwise we could print `0ms` despite user's `>0` filter when
                        // `duration` is just a few nanos).
                        if duration.as_millis() > longer_than.as_millis() {
                            print(0, &stack.messages, &mut stdout.lock(), longer_than, None);
                        }
                        stack.messages.clear();
                    }
                });
            }
            Profiler { desc: None } => (),
        }
    }
}

fn print(
    lvl: usize,
    msgs: &[Message],
    out: &mut impl Write,
    longer_than: Duration,
    total: Option<Duration>,
) {
    if msgs.is_empty() {
        return;
    }
    // The index of the first element that will be included in the slice when we recurse.
    let mut next_start = 0;
    let indent = repeat("    ").take(lvl).collect::<String>();
    // We output hierarchy for long calls, but sum up all short calls
    let mut short = Vec::new();
    let mut accounted_for = Duration::default();
    for (i, &Message { level, duration, message: ref msg }) in msgs.iter().enumerate() {
        if level != lvl {
            continue;
        }
        accounted_for += duration;
        if duration.as_millis() > longer_than.as_millis() {
            writeln!(out, "{}{:5}ms - {}", indent, duration.as_millis(), msg)
                .expect("printing profiling info to stdout");

            print(lvl + 1, &msgs[next_start..i], out, longer_than, Some(duration));
        } else {
            short.push((msg, duration))
        }

        next_start = i + 1;
    }
    short.sort_by_key(|(msg, _time)| *msg);
    for (msg, entires) in short.iter().group_by(|(msg, _time)| msg).into_iter() {
        let mut count = 0;
        let mut total_duration = Duration::default();
        entires.for_each(|(_msg, time)| {
            count += 1;
            total_duration += *time;
        });
        writeln!(out, "{}{:5}ms - {} ({} calls)", indent, total_duration.as_millis(), msg, count)
            .expect("printing profiling info to stdout");
    }

    if let Some(total) = total {
        if let Some(unaccounted) = total.checked_sub(accounted_for) {
            let unaccounted_millis = unaccounted.as_millis();
            if unaccounted_millis > longer_than.as_millis() && unaccounted_millis > 0 {
                writeln!(out, "{}{:5}ms - ???", indent, unaccounted_millis)
                    .expect("printing profiling info to stdout");
            }
        }
    }
}

/// Prints backtrace to stderr, useful for debugging.
pub fn print_backtrace() {
    let bt = backtrace::Backtrace::new();
    eprintln!("{:?}", bt);
}

thread_local!(static IN_SCOPE: RefCell<bool> = RefCell::new(false));

/// Allows to check if the current code is withing some dynamic scope, can be
/// useful during debugging to figure out why a function is called.
pub struct Scope {
    prev: bool,
}

impl Scope {
    pub fn enter() -> Scope {
        let prev = IN_SCOPE.with(|slot| std::mem::replace(&mut *slot.borrow_mut(), true));
        Scope { prev }
    }
    pub fn is_active() -> bool {
        IN_SCOPE.with(|slot| *slot.borrow())
    }
}

impl Drop for Scope {
    fn drop(&mut self) {
        IN_SCOPE.with(|slot| *slot.borrow_mut() = self.prev);
    }
}

/// A wrapper around google_cpu_profiler.
///
/// Usage:
/// 1. Install gpref_tools (https://github.com/gperftools/gperftools), probably packaged with your Linux distro.
/// 2. Build with `cpu_profiler` feature.
/// 3. Tun the code, the *raw* output would be in the `./out.profile` file.
/// 4. Install pprof for visualization (https://github.com/google/pprof).
/// 5. Use something like `pprof -svg target/release/ra_cli ./out.profile` to see the results.
///
/// For example, here's how I run profiling on NixOS:
///
/// ```bash
/// $ nix-shell -p gperftools --run \
///     'cargo run --release -p ra_cli -- parse < ~/projects/rustbench/parser.rs > /dev/null'
/// ```
#[derive(Debug)]
pub struct CpuProfiler {
    _private: (),
}

pub fn cpu_profiler() -> CpuProfiler {
    #[cfg(feature = "cpu_profiler")]
    {
        google_cpu_profiler::start("./out.profile".as_ref())
    }

    #[cfg(not(feature = "cpu_profiler"))]
    {
        eprintln!("cpu_profiler feature is disabled")
    }

    CpuProfiler { _private: () }
}

impl Drop for CpuProfiler {
    fn drop(&mut self) {
        #[cfg(feature = "cpu_profiler")]
        {
            google_cpu_profiler::stop()
        }
    }
}

pub fn memory_usage() -> MemoryUsage {
    MemoryUsage::current()
}

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

    #[test]
    fn test_basic_profile() {
        let s = vec!["profile1".to_string(), "profile2".to_string()];
        let f = Filter::new(2, s, Duration::new(0, 0));
        set_filter(f);
        profiling_function1();
    }

    fn profiling_function1() {
        let _p = profile("profile1");
        profiling_function2();
    }

    fn profiling_function2() {
        let _p = profile("profile2");
    }

    #[test]
    fn test_longer_than() {
        let mut result = vec![];
        let msgs = vec![
            Message { level: 1, duration: Duration::from_nanos(3), message: "bar".to_owned() },
            Message { level: 1, duration: Duration::from_nanos(2), message: "bar".to_owned() },
            Message { level: 0, duration: Duration::from_millis(1), message: "foo".to_owned() },
        ];
        print(0, &msgs, &mut result, Duration::from_millis(0), Some(Duration::from_millis(1)));
        // The calls to `bar` are so short that they'll be rounded to 0ms and should get collapsed
        // when printing.
        assert_eq!(
            std::str::from_utf8(&result).unwrap(),
            "    1ms - foo\n        0ms - bar (2 calls)\n"
        );
    }

    #[test]
    fn test_unaccounted_for_topmost() {
        let mut result = vec![];
        let msgs = vec![
            Message { level: 1, duration: Duration::from_millis(2), message: "bar".to_owned() },
            Message { level: 0, duration: Duration::from_millis(5), message: "foo".to_owned() },
        ];
        print(0, &msgs, &mut result, Duration::from_millis(0), Some(Duration::from_millis(1)));
        assert_eq!(
            std::str::from_utf8(&result).unwrap().lines().collect::<Vec<_>>(),
            vec![
                "    5ms - foo",
                "        2ms - bar",
                "        3ms - ???",
                // Dummy comment to improve formatting
            ]
        );
    }

    #[test]
    fn test_unaccounted_for_multiple_levels() {
        let mut result = vec![];
        let msgs = vec![
            Message { level: 2, duration: Duration::from_millis(3), message: "baz".to_owned() },
            Message { level: 1, duration: Duration::from_millis(5), message: "bar".to_owned() },
            Message { level: 2, duration: Duration::from_millis(2), message: "baz".to_owned() },
            Message { level: 1, duration: Duration::from_millis(4), message: "bar".to_owned() },
            Message { level: 0, duration: Duration::from_millis(9), message: "foo".to_owned() },
        ];
        print(0, &msgs, &mut result, Duration::from_millis(0), None);
        assert_eq!(
            std::str::from_utf8(&result).unwrap().lines().collect::<Vec<_>>(),
            vec![
                "    9ms - foo",
                "        5ms - bar",
                "            3ms - baz",
                "            2ms - ???",
                "        4ms - bar",
                "            2ms - baz",
                "            2ms - ???",
            ]
        );
    }
}
<|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 https://mozilla.org/MPL/2.0/. */

//! Tracking of pending loads in a document.
//!
//! <https://html.spec.whatwg.org/multipage/#the-end>

use crate::dom::bindings::root::Dom;
use crate::dom::document::Document;
use crate::fetch::FetchCanceller;
use ipc_channel::ipc::IpcSender;
use net_traits::request::RequestInit;
use net_traits::{CoreResourceMsg, FetchChannels, FetchResponseMsg};
use net_traits::{IpcSend, ResourceThreads};
use servo_url::ServoUrl;

#[derive(Clone, Debug, JSTraceable, MallocSizeOf, PartialEq)]
pub enum LoadType {
    Image(ServoUrl),
    Script(ServoUrl),
    Subframe(ServoUrl),
    Stylesheet(ServoUrl),
    PageSource(ServoUrl),
    Media,
}

impl LoadType {
    fn url(&self) -> Option<&ServoUrl> {
        match *self {
            LoadType::Image(ref url) |
            LoadType::Script(ref url) |
            LoadType::Subframe(ref url) |
            LoadType::Stylesheet(ref url) |
            LoadType::PageSource(ref url) => Some(url),
            LoadType::Media => None,
        }
    }
}

/// Canary value ensuring that manually added blocking loads (ie. ones that weren't
/// created via DocumentLoader::fetch_async) are always removed by the time
/// that the owner is destroyed.
#[derive(JSTraceable, MallocSizeOf)]
#[must_root]
pub struct LoadBlocker {
    /// The document whose load event is blocked by this object existing.
    doc: Dom<Document>,
    /// The load that is blocking the document's load event.
    load: Option<LoadType>,
}

impl LoadBlocker {
    /// Mark the document's load event as blocked on this new load.
    pub fn new(doc: &Document, load: LoadType) -> LoadBlocker {
        doc.loader_mut().add_blocking_load(load.clone());
        LoadBlocker {
            doc: Dom::from_ref(doc),
            load: Some(load),
        }
    }

    /// Remove this load from the associated document's list of blocking loads.
    pub fn terminate(blocker: &mut Option<LoadBlocker>) {
        if let Some(this) = blocker.as_mut() {
            this.doc.finish_load(this.load.take().unwrap());
        }
        *blocker = None;
    }

    /// Return the url associated with this load.
    pub fn url(&self) -> Option<&ServoUrl> {
        self.load.as_ref().and_then(LoadType::url)
    }
}

impl Drop for LoadBlocker {
    fn drop(&mut self) {
        if let Some(load) = self.load.take() {
            self.doc.finish_load(load);
        }
    }
}

#[derive(JSTraceable, MallocSizeOf)]
pub struct DocumentLoader {
    resource_threads: ResourceThreads,
    blocking_loads: Vec<LoadType>,
    events_inhibited: bool,
    cancellers: Vec<FetchCanceller>,
}

impl DocumentLoader {
    pub fn new(existing: &DocumentLoader) -> DocumentLoader {
        DocumentLoader::new_with_threads(existing.resource_threads.clone(), None)
    }

    pub fn new_with_threads(
        resource_threads: ResourceThreads,
        initial_load: Option<ServoUrl>,
    ) -> DocumentLoader {
        debug!("Initial blocking load {:?}.", initial_load);
        let initial_loads = initial_load.into_iter().map(LoadType::PageSource).collect();

        DocumentLoader {
            resource_threads: resource_threads,
            blocking_loads: initial_loads,
            events_inhibited: false,
            cancellers: Vec::new(),
        }
    }

    pub fn cancel_all_loads(&mut self) -> bool {
        let canceled_any = !self.cancellers.is_empty();
        // Associated fetches will be canceled when dropping the canceller.
        self.cancellers.clear();
        canceled_any
    }

    /// Add a load to the list of blocking loads.
    fn add_blocking_load(&mut self, load: LoadType) {
        debug!(
            "Adding blocking load {:?} ({}).",
            load,
            self.blocking_loads.len()
        );
        self.blocking_loads.push(load);
    }

    /// Initiate a new fetch.
    pub fn fetch_async(
        &mut self,
        load: LoadType,
        request: RequestInit,
        fetch_target: IpcSender<FetchResponseMsg>,
    ) {
        self.add_blocking_load(load);
        self.fetch_async_background(request, fetch_target);
    }

    /// Initiate a new fetch that does not block the document load event.
    pub fn fetch_async_background(
        &mut self,
        request: RequestInit,
        fetch_target: IpcSender<FetchResponseMsg>,
    ) {
        let mut canceller = FetchCanceller::new();
        let cancel_receiver = canceller.initialize();
        self.cancellers.push(canceller);
        self.resource_threads
            .sender()
            .send(CoreResourceMsg::Fetch(
                request,
                FetchChannels::ResponseMsg(fetch_target, Some(cancel_receiver)),
            ))
            .unwrap();
    }

    /// Mark an in-progress network request complete.
    pub fn finish_load(&mut self, load: &LoadType) {
        debug!(
            "Removing blocking load {:?} ({}).",
            load,
            self.blocking_loads.len()
        );
        let idx = self
            .blocking_loads
            .iter()
            .position(|unfinished| *unfinished == *load);
        self.blocking_loads
            .remove(idx.unwrap_or_else(|| panic!("unknown completed load {:?}", load)));
    }

    pub fn is_blocked(&self) -> bool {
        // TODO: Ensure that we report blocked if parsing is still ongoing.
        !self.blocking_loads.is_empty()
    }

    pub fn is_only_blocked_by_iframes(&self) -> bool {
        self.blocking_loads.iter().all(|load| match *load {
            LoadType::Subframe(_) => true,
            _ => false,
        })
    }

    pub fn inhibit_events(&mut self) {
        self.events_inhibited = true;
    }

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

    pub fn resource_threads(&self) -> &ResourceThreads {
        &self.resource_threads
    }
}
<commit_msg>Replace panic with warn in DocumentLoader.finish_load.<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 https://mozilla.org/MPL/2.0/. */

//! Tracking of pending loads in a document.
//!
//! <https://html.spec.whatwg.org/multipage/#the-end>

use crate::dom::bindings::root::Dom;
use crate::dom::document::Document;
use crate::fetch::FetchCanceller;
use ipc_channel::ipc::IpcSender;
use net_traits::request::RequestInit;
use net_traits::{CoreResourceMsg, FetchChannels, FetchResponseMsg};
use net_traits::{IpcSend, ResourceThreads};
use servo_url::ServoUrl;

#[derive(Clone, Debug, JSTraceable, MallocSizeOf, PartialEq)]
pub enum LoadType {
    Image(ServoUrl),
    Script(ServoUrl),
    Subframe(ServoUrl),
    Stylesheet(ServoUrl),
    PageSource(ServoUrl),
    Media,
}

impl LoadType {
    fn url(&self) -> Option<&ServoUrl> {
        match *self {
            LoadType::Image(ref url) |
            LoadType::Script(ref url) |
            LoadType::Subframe(ref url) |
            LoadType::Stylesheet(ref url) |
            LoadType::PageSource(ref url) => Some(url),
            LoadType::Media => None,
        }
    }
}

/// Canary value ensuring that manually added blocking loads (ie. ones that weren't
/// created via DocumentLoader::fetch_async) are always removed by the time
/// that the owner is destroyed.
#[derive(JSTraceable, MallocSizeOf)]
#[must_root]
pub struct LoadBlocker {
    /// The document whose load event is blocked by this object existing.
    doc: Dom<Document>,
    /// The load that is blocking the document's load event.
    load: Option<LoadType>,
}

impl LoadBlocker {
    /// Mark the document's load event as blocked on this new load.
    pub fn new(doc: &Document, load: LoadType) -> LoadBlocker {
        doc.loader_mut().add_blocking_load(load.clone());
        LoadBlocker {
            doc: Dom::from_ref(doc),
            load: Some(load),
        }
    }

    /// Remove this load from the associated document's list of blocking loads.
    pub fn terminate(blocker: &mut Option<LoadBlocker>) {
        if let Some(this) = blocker.as_mut() {
            this.doc.finish_load(this.load.take().unwrap());
        }
        *blocker = None;
    }

    /// Return the url associated with this load.
    pub fn url(&self) -> Option<&ServoUrl> {
        self.load.as_ref().and_then(LoadType::url)
    }
}

impl Drop for LoadBlocker {
    fn drop(&mut self) {
        if let Some(load) = self.load.take() {
            self.doc.finish_load(load);
        }
    }
}

#[derive(JSTraceable, MallocSizeOf)]
pub struct DocumentLoader {
    resource_threads: ResourceThreads,
    blocking_loads: Vec<LoadType>,
    events_inhibited: bool,
    cancellers: Vec<FetchCanceller>,
}

impl DocumentLoader {
    pub fn new(existing: &DocumentLoader) -> DocumentLoader {
        DocumentLoader::new_with_threads(existing.resource_threads.clone(), None)
    }

    pub fn new_with_threads(
        resource_threads: ResourceThreads,
        initial_load: Option<ServoUrl>,
    ) -> DocumentLoader {
        debug!("Initial blocking load {:?}.", initial_load);
        let initial_loads = initial_load.into_iter().map(LoadType::PageSource).collect();

        DocumentLoader {
            resource_threads: resource_threads,
            blocking_loads: initial_loads,
            events_inhibited: false,
            cancellers: Vec::new(),
        }
    }

    pub fn cancel_all_loads(&mut self) -> bool {
        let canceled_any = !self.cancellers.is_empty();
        // Associated fetches will be canceled when dropping the canceller.
        self.cancellers.clear();
        canceled_any
    }

    /// Add a load to the list of blocking loads.
    fn add_blocking_load(&mut self, load: LoadType) {
        debug!(
            "Adding blocking load {:?} ({}).",
            load,
            self.blocking_loads.len()
        );
        self.blocking_loads.push(load);
    }

    /// Initiate a new fetch.
    pub fn fetch_async(
        &mut self,
        load: LoadType,
        request: RequestInit,
        fetch_target: IpcSender<FetchResponseMsg>,
    ) {
        self.add_blocking_load(load);
        self.fetch_async_background(request, fetch_target);
    }

    /// Initiate a new fetch that does not block the document load event.
    pub fn fetch_async_background(
        &mut self,
        request: RequestInit,
        fetch_target: IpcSender<FetchResponseMsg>,
    ) {
        let mut canceller = FetchCanceller::new();
        let cancel_receiver = canceller.initialize();
        self.cancellers.push(canceller);
        self.resource_threads
            .sender()
            .send(CoreResourceMsg::Fetch(
                request,
                FetchChannels::ResponseMsg(fetch_target, Some(cancel_receiver)),
            ))
            .unwrap();
    }

    /// Mark an in-progress network request complete.
    pub fn finish_load(&mut self, load: &LoadType) {
        debug!(
            "Removing blocking load {:?} ({}).",
            load,
            self.blocking_loads.len()
        );
        let idx = self
            .blocking_loads
            .iter()
            .position(|unfinished| *unfinished == *load);
        match idx {
            Some(i) => {
                self.blocking_loads.remove(i);
            },
            None => warn!("unknown completed load {:?}", load),
        }
    }

    pub fn is_blocked(&self) -> bool {
        // TODO: Ensure that we report blocked if parsing is still ongoing.
        !self.blocking_loads.is_empty()
    }

    pub fn is_only_blocked_by_iframes(&self) -> bool {
        self.blocking_loads.iter().all(|load| match *load {
            LoadType::Subframe(_) => true,
            _ => false,
        })
    }

    pub fn inhibit_events(&mut self) {
        self.events_inhibited = true;
    }

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

    pub fn resource_threads(&self) -> &ResourceThreads {
        &self.resource_threads
    }
}
<|endoftext|>


<commit_before>use std::io::{TcpListener, TcpStream};
use std::io::{Acceptor, Listener};
use std::io::BufferedStream;
use std::string::String;

pub const CR: u8 = b'\r';
pub const LF: u8 = b'\n';
pub const SP: u8 = b' ';
pub const CRLF: [u8, ..2] = [CR,LF];

fn parse_request_line(header: &str) {
    let mut pathname = String::new();
    let mut method = String::new();
    let mut starting = true;
    let mut found_method = false;

    for ch in header.graphemes(true) {
        let cbyte = ch.as_bytes()[0];

        if starting && cbyte != SP {
            method.push_str(ch);
        }
        if starting && cbyte == SP {
            starting = false;
            found_method = true;
            continue;
        }
        if found_method && cbyte != SP {
            pathname.push_str(ch);
        }
        if found_method && cbyte == SP {
            break;
        }

    }

    println!("METHOD: {}", method);
    println!("PATHNAME: {}", pathname);
}

fn main () {

    let listener = TcpListener::bind("127.0.0.1:8099").unwrap();
    let mut acceptor = listener.listen().unwrap();

    fn handle_client(mut stream: BufferedStream<TcpStream>) {

        let mut body: String = "
You sent it!
".to_string();

        let mut cur_line: String;
        let mut ii = 0u;

        loop {
            match stream.read_line() { // XXX: strange unwrap like thing
                Ok(line) => cur_line = line,
                Err(_) => break
            }
            if ii == 0u { // the Request-Line is always the first line
                parse_request_line(cur_line.as_slice());
                ii += 1;
            }

            body = body + cur_line + "
";
            if cur_line.as_bytes() == CRLF {
                break;
            }
        }

        let body_length = format!("Content-length: {}", body.len());

        stream.write(b"HTTP/1.1 200 OK\r\n").unwrap(); // byte literal
        stream.write(b"Content-type: text/html\r\n").unwrap();
        stream.write(b"X-header: from bytes\r\n").unwrap();
        stream.write(body_length.into_bytes().as_slice()).unwrap();
        stream.write(b"\r\n\r\n").unwrap();
        stream.write(body.into_bytes().as_slice()).unwrap();
        stream.flush().unwrap();

        println!("Handling acceptor");
    }

    for stream in acceptor.incoming() {
        match stream {
            Err(e) => { println!("connection failed: {}", e) }
            Ok(stream) => spawn(proc() {
                let bs = BufferedStream::new(stream);
                handle_client(bs)
            })
        }
    }

    drop(acceptor);
}
<commit_msg>use const CRLF, us struct to return method and pathname<commit_after>use std::io::{TcpListener, TcpStream};
use std::io::{Acceptor, Listener};
use std::io::BufferedStream;
use std::string::String;

pub const CR: u8 = b'\r';
pub const LF: u8 = b'\n';
pub const SP: u8 = b' ';
pub const CRLF: [u8, ..2] = [CR,LF];

struct RequestedRoute {
    method: String,
    pathname: String
}

fn parse_request_line(header: &str) -> RequestedRoute {
    let mut pathname = String::new();
    let mut method = String::new();
    let mut starting = true;
    let mut found_method = false;

    for ch in header.graphemes(true) {
        let cbyte = ch.as_bytes()[0];

        if starting && cbyte != SP {
            method.push_str(ch);
        }
        if starting && cbyte == SP {
            starting = false;
            found_method = true;
            continue;
        }
        if found_method && cbyte != SP {
            pathname.push_str(ch);
        }
        if found_method && cbyte == SP {
            break;
        }

    }

    return RequestedRoute {method: method, pathname: pathname};
}

fn main () {

    let listener = TcpListener::bind("127.0.0.1:8099").unwrap();
    let mut acceptor = listener.listen().unwrap();

    fn handle_client(mut stream: BufferedStream<TcpStream>) {

        let mut body: String = "
You sent it!
".to_string();

        let mut cur_line: String;
        let mut ii = 0u;
        let mut req: RequestedRoute = RequestedRoute{
            pathname: String::new(),
            method: String::new()
        };

        loop {
            match stream.read_line() { // XXX: strange unwrap like thing
                Ok(line) => cur_line = line,
                Err(_) => break
            }
            if ii == 0u { // the Request-Line is always the first line
                req = parse_request_line(cur_line.as_slice());
                ii += 1;
            }

            body = body + cur_line + "
";
            if cur_line.as_bytes() == &CRLF {
                break;
            }
        }
        println!("pathname {} method {}", req.pathname, req.method);

        let body_length = format!("Content-length: {}", body.len());

        stream.write(b"HTTP/1.1 200 OK\r\n").unwrap(); // byte literal
        stream.write(b"Content-type: text/html\r\n").unwrap();
        stream.write(b"X-header: from bytes\r\n").unwrap();
        stream.write(body_length.into_bytes().as_slice()).unwrap();
        stream.write(b"\r\n\r\n").unwrap();
        stream.write(body.into_bytes().as_slice()).unwrap();
        stream.flush().unwrap();

        println!("Handling acceptor");
    }

    for stream in acceptor.incoming() {
        match stream {
            Err(e) => { println!("connection failed: {}", e) }
            Ok(stream) => spawn(proc() {
                let bs = BufferedStream::new(stream);
                handle_client(bs)
            })
        }
    }

    drop(acceptor);
}
<|endoftext|>


<commit_before>//! The integration suite for inspecting sessions.

extern crate libc;
extern crate rand;
extern crate regex;

mod helpers;

mod open {
    use rand::{random};
    use std::fs::File;
    use std::fs;
    use std::path::PathBuf;
    use std::io::prelude::*;
    use std::env::home_dir;
    use helpers::*;

    fn setup(contents: &'static [u8]) -> (String, PathBuf) {
        let project_name = format!("muxed_int_test_{}", random::<u16>());
        let project_file = format!("/tmp/muxed_{}/{}.yml", random::<u16>(), project_name);
        let project_path = PathBuf::from(&project_file);

        let muxed_path = project_path.parent().unwrap();
        if !muxed_path.exists() { println!("{:?}", fs::create_dir(muxed_path)) };

        let mut buffer = File::create(&project_path).unwrap();
        let _ = buffer.write(contents);

        (project_name, project_path.clone())
    }

    fn cleanup(project_name: &String, config_path: &PathBuf) -> () {
        let _ = fs::remove_file(config_path);
        let _ = fs::remove_dir(config_path.parent().unwrap());
        kill_session(&project_name);
    }

    fn test_with_contents(contents: &'static [u8]) -> TmuxSession {
        let (project_name, config_path) = setup(contents);
        open_muxed(&project_name, config_path.parent().unwrap());
        let completed = PathBuf::from(format!("/tmp/{}.complete", project_name));
        let exec = format!("touch {}", completed.display());
        send_keys(&project_name, &exec);
        wait_on(&completed);
        let session = TmuxSession::from_string(&list_windows(&project_name.to_string()));
        cleanup(&project_name, &config_path);
        session
    }

    #[test]
    fn opens_3_windows_from_array() {
        let contents = b"---
windows: ['ls', 'vi', 'git']
";
        let session = test_with_contents(contents);
        assert_eq!(session.num_of_windows, 3)
    }

    #[test]
    fn opens_2_windows() {
        let contents = b"---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['ls', 'vi']
  - stuff: ''
";
        let session = test_with_contents(contents);
        assert_eq!(session.num_of_windows, 2)
    }

    #[test]
    fn opens_3_windows_with_integer_names() {
        let contents = b"---
windows: [1, 'ls', 3]
";
        let session = test_with_contents(contents);
        assert_eq!(session.num_of_windows, 3)
    }

    #[test]
    fn single_window_has_2_panes() {
        let contents = b"---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['ls', 'vi']
";
        let session = test_with_contents(contents);
        let num = session.windows.get("editor").unwrap().panes.as_usize().unwrap();
        assert_eq!(num, 2)
    }

    #[test]
    fn multiple_windows_with_panes() {
        let contents = b"---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['ls', 'vi']
  - tests:
      layout: 'main-vertical'
      panes: ['ls', 'vi', 'ls']
";
        let session = test_with_contents(contents);
        let num = session.windows.get("editor").unwrap().panes.as_usize().unwrap();
        let num1 = session.windows.get("tests").unwrap().panes.as_usize().unwrap();
        assert_eq!(num, 2);
        assert_eq!(num1, 3)
    }

    #[test]
    fn window_with_empty_command_is_valid() {
        let contents = b"---
windows:
  - editor:
";
        let session = test_with_contents(contents);
        assert_eq!(session.num_of_windows, 1)
    }

    #[test]
    fn panes_with_empty_commands_are_valid() {
        let contents = b"---
windows:
  - editor:
      layout: 'main-vertical'
      panes:
        -
        -
";
        let session = test_with_contents(contents);
        let num = session.windows.get("editor").unwrap().panes.as_usize().unwrap();
        assert_eq!(num, 2)
    }

    #[test]
    fn expect_to_open_in_directory_containing_spaces() {
        let dir = PathBuf::from("/tmp/Directory With Spaces/");
        if !dir.exists() { println!("{:?}", fs::create_dir(&dir)) };
        let contents = b"---
root: /tmp/Directory With Spaces/
windows:
  - editor: ''
";
        let session = test_with_contents(contents);
        let pane_current_path = session.windows.get("editor").unwrap().pane_current_path.as_str().unwrap();
        let _ = fs::remove_dir(dir);
        // Use contains because OSX on travis ci symlinks /tmp/ to /private/tmp/
        // resulting in `pane_current_path` being `/private/tmp/Direct…`
        assert!(pane_current_path.contains("/tmp/Directory With Spaces"));
    }

    #[test]
    fn expect_home_var_to_open_in_home_dir() {
        let contents = b"---
root: '$HOME'
windows:
  - editor: ''
";
        let session = test_with_contents(contents);
        let pane_current_path = session.windows.get("editor").unwrap().pane_current_path.as_str().unwrap();
        assert_eq!(pane_current_path, home_dir().unwrap().to_str().unwrap());
    }

    #[test]
    fn expect_tilde_slash_to_open_in_home_dir() {
        let contents = b"---
root: ~/
windows:
  - editor: ''
";
        let session = test_with_contents(contents);
        let pane_current_path = session.windows.get("editor").unwrap().pane_current_path.as_str().unwrap();
        assert_eq!(pane_current_path, home_dir().unwrap().to_str().unwrap());
    }

    #[test]
    fn expect_focus_on_the_first_window() {
        let contents = b"---
windows: ['ssh', 'git']
";
        let session = test_with_contents(contents);
        let window_active = session.window_active.as_str().unwrap();
        assert_eq!(window_active, "ssh")
    }

// This test should exist but we currently don't do anything to list panes.
//    #[test]
//    fn expect_focus_on_the_top_pane() {
//        let contents = b"---
//windows:
//  - ssh:
//    layout: main-horizontal
//    panes:
//      - ''
//      - ''
//  - git: ''
//";
//        let session = test_with_contents(contents);
//        assert_eq!(session.pane_active, "ssh.0")
//    }
}
<commit_msg>test(pre & pre_window): Verify expectations of pre<commit_after>//! The integration suite for inspecting sessions.

extern crate libc;
extern crate rand;
extern crate regex;

mod helpers;

mod open {
    use rand::{random};
    use std::fs::File;
    use std::fs;
    use std::path::PathBuf;
    use std::io::prelude::*;
    use std::env::home_dir;
    use helpers::*;

    fn setup(contents: &[u8]) -> (String, PathBuf) {
        let project_name = format!("muxed_int_test_{}", random::<u16>());
        let project_file = format!("/tmp/muxed_{}/{}.yml", random::<u16>(), project_name);
        let project_path = PathBuf::from(&project_file);

        let muxed_path = project_path.parent().unwrap();
        if !muxed_path.exists() { println!("{:?}", fs::create_dir(muxed_path)) };

        let mut buffer = File::create(&project_path).unwrap();
        let _ = buffer.write(contents);

        (project_name, project_path.clone())
    }

    fn cleanup(project_name: &String, config_path: &PathBuf) -> () {
        let _ = fs::remove_file(config_path);
        let _ = fs::remove_dir(config_path.parent().unwrap());
        kill_session(&project_name);
    }

    fn test_with_contents(contents: &[u8]) -> TmuxSession {
        let (project_name, config_path) = setup(contents);
        open_muxed(&project_name, config_path.parent().unwrap());
        let completed = PathBuf::from(format!("/tmp/{}.complete", project_name));
        let exec = format!("touch {}", completed.display());
        send_keys(&project_name, &exec);
        wait_on(&completed);
        let session = TmuxSession::from_string(&list_windows(&project_name.to_string()));
        cleanup(&project_name, &config_path);
        session
    }

    #[test]
    fn opens_3_windows_from_array() {
        let contents = b"---
windows: ['ls', 'vi', 'git']
";
        let session = test_with_contents(contents);
        assert_eq!(session.num_of_windows, 3)
    }

    #[test]
    fn opens_2_windows() {
        let contents = b"---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['ls', 'vi']
  - stuff: ''
";
        let session = test_with_contents(contents);
        assert_eq!(session.num_of_windows, 2)
    }

    #[test]
    fn opens_3_windows_with_integer_names() {
        let contents = b"---
windows: [1, 'ls', 3]
";
        let session = test_with_contents(contents);
        assert_eq!(session.num_of_windows, 3)
    }

    #[test]
    fn single_window_has_2_panes() {
        let contents = b"---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['ls', 'vi']
";
        let session = test_with_contents(contents);
        let num = session.windows.get("editor").unwrap().panes.as_usize().unwrap();
        assert_eq!(num, 2)
    }

    #[test]
    fn multiple_windows_with_panes() {
        let contents = b"---
windows:
  - editor:
      layout: 'main-vertical'
      panes: ['ls', 'vi']
  - tests:
      layout: 'main-vertical'
      panes: ['ls', 'vi', 'ls']
";
        let session = test_with_contents(contents);
        let num = session.windows.get("editor").unwrap().panes.as_usize().unwrap();
        let num1 = session.windows.get("tests").unwrap().panes.as_usize().unwrap();
        assert_eq!(num, 2);
        assert_eq!(num1, 3)
    }

    #[test]
    fn window_with_empty_command_is_valid() {
        let contents = b"---
windows:
  - editor:
";
        let session = test_with_contents(contents);
        assert_eq!(session.num_of_windows, 1)
    }

    #[test]
    fn panes_with_empty_commands_are_valid() {
        let contents = b"---
windows:
  - editor:
      layout: 'main-vertical'
      panes:
        -
        -
";
        let session = test_with_contents(contents);
        let num = session.windows.get("editor").unwrap().panes.as_usize().unwrap();
        assert_eq!(num, 2)
    }

    #[test]
    fn expect_to_open_in_directory_containing_spaces() {
        let dir = PathBuf::from("/tmp/Directory With Spaces/");
        if !dir.exists() { println!("{:?}", fs::create_dir(&dir)) };
        let contents = b"---
root: /tmp/Directory With Spaces/
windows:
  - editor: ''
";
        let session = test_with_contents(contents);
        let pane_current_path = session.windows.get("editor").unwrap().pane_current_path.as_str().unwrap();
        let _ = fs::remove_dir(dir);
        // Use contains because OSX on travis ci symlinks /tmp/ to /private/tmp/
        // resulting in `pane_current_path` being `/private/tmp/Direct…`
        assert!(pane_current_path.contains("/tmp/Directory With Spaces"));
    }

    #[test]
    fn expect_home_var_to_open_in_home_dir() {
        let contents = b"---
root: '$HOME'
windows:
  - editor: ''
";
        let session = test_with_contents(contents);
        let pane_current_path = session.windows.get("editor").unwrap().pane_current_path.as_str().unwrap();
        assert_eq!(pane_current_path, home_dir().unwrap().to_str().unwrap());
    }

    #[test]
    fn expect_tilde_slash_to_open_in_home_dir() {
        let contents = b"---
root: ~/
windows:
  - editor: ''
";
        let session = test_with_contents(contents);
        let pane_current_path = session.windows.get("editor").unwrap().pane_current_path.as_str().unwrap();
        assert_eq!(pane_current_path, home_dir().unwrap().to_str().unwrap());
    }

    #[test]
    fn expect_focus_on_the_first_window() {
        let contents = b"---
windows: ['ssh', 'git']
";
        let session = test_with_contents(contents);
        let window_active = session.window_active.as_str().unwrap();
        assert_eq!(window_active, "ssh")
    }

    #[test]
    fn expect_pre_to_create_file() {
        let file = PathBuf::from(format!("/tmp/{}", random::<u16>()));
        let contents = format!("---
pre: touch {}
windows: ['ssh', 'git']
", file.display());
        let _ = test_with_contents(contents.as_bytes());
        assert!(file.exists());
        let _ = fs::remove_file(file);
    }

    #[test]
    fn expect_pre_to_create_two_files() {
        let file1 = PathBuf::from(format!("/tmp/{}", random::<u16>()));
        let file2 = PathBuf::from(format!("/tmp/{}", random::<u16>()));
        let contents = format!("---
pre:
  - touch {}
  - touch {}
windows: ['ssh', 'git']
", file1.display(), file2.display());
        let _ = test_with_contents(contents.as_bytes());
        assert!(file1.exists());
        assert!(file2.exists());
        let _ = fs::remove_file(file1);
        let _ = fs::remove_file(file2);
    }

    #[test]
    fn expect_pre_window_to_be_called_for_each_window() {
        let file = PathBuf::from(format!("/tmp/{}", random::<u16>()));
        let contents = format!("---
pre_window: echo 'pre_window' >> {}
windows: ['ssh', 'git']
", file.display());
        let _ = test_with_contents(contents.as_bytes());
        let mut f = File::open(&file).unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s);
        assert_eq!(s.lines().count(), 2);
        let _ = fs::remove_file(&file);
    }

    #[test]
    fn expect_pre_window_to_be_called_twice_for_each_window() {
        let file = PathBuf::from(format!("/tmp/{}", random::<u16>()));
        let contents = format!("---
pre_window:
 - echo 'pre_window' >> {}
 - echo 'pre_window' >> {}
windows: ['ssh', 'git']
", file.display(), file.display());
        let _ = test_with_contents(contents.as_bytes());
        let mut f = File::open(&file).unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s);
        assert_eq!(s.lines().count(), 4);
        let _ = fs::remove_file(&file);
    }

// This test should exist but we currently don't do anything to list panes.
//    #[test]
//    fn expect_focus_on_the_top_pane() {
//        let contents = b"---
//windows:
//  - ssh:
//    layout: main-horizontal
//    panes:
//      - ''
//      - ''
//  - git: ''
//";
//        let session = test_with_contents(contents);
//        assert_eq!(session.pane_active, "ssh.0")
//    }
}
<|endoftext|>


<commit_before>use cmp::Ord;
use num::*;

use ncast::*;

// TODO: move to a more appropriate module
pub trait ToPtr<T> {
    pure fn to_ptr() -> *T;
}

pub trait ExactEq {
    pure fn exact_eq(other: &self) -> bool;
}

/**
 * TODO: This trait will be implemented for <T:Num Ord> when generic trait
 * support is improved. At the moment this would cause a conflict between the
 * implementations in lmath::vector.
 */
trait MinMax {
    pure fn min(other: &self) -> self;
    pure fn max(other: &self) -> self;
}

#[inline(always)] pure fn min<T:MinMax>(a: &T, b: &T) -> T { a.min(b) }
#[inline(always)] pure fn max<T:MinMax>(a: &T, b: &T) -> T { a.max(b) }

impl u8: MinMax {
    #[inline(always)] pure fn min(other: &u8) -> u8 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &u8) -> u8 { if self > *other { self } else { *other } }
}

impl u16: MinMax {
    #[inline(always)] pure fn min(other: &u16) -> u16 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &u16) -> u16 { if self > *other { self } else { *other } }
}

impl u32: MinMax {
    #[inline(always)] pure fn min(other: &u32) -> u32 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &u32) -> u32 { if self > *other { self } else { *other } }
}

impl u64: MinMax {
    #[inline(always)] pure fn min(other: &u64) -> u64 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &u64) -> u64 { if self > *other { self } else { *other } }
}

impl uint: MinMax {
    #[inline(always)] pure fn min(other: &uint) -> uint { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &uint) -> uint { if self > *other { self } else { *other } }
}

impl i8: MinMax {
    #[inline(always)] pure fn min(other: &i8) -> i8 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &i8) -> i8 { if self > *other { self } else { *other } }
}

impl i16: MinMax {
    #[inline(always)] pure fn min(other: &i16) -> i16 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &i16) -> i16 { if self > *other { self } else { *other } }
}

impl i32: MinMax {
    #[inline(always)] pure fn min(other: &i32) -> i32 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &i32) -> i32 { if self > *other { self } else { *other } }
}

impl i64: MinMax {
    #[inline(always)] pure fn min(other: &i64) -> i64 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &i64) -> i64 { if self > *other { self } else { *other } }
}

impl int: MinMax {
    #[inline(always)] pure fn min(other: &int) -> int { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &int) -> int { if self > *other { self } else { *other } }
}

impl f32: MinMax {
    #[inline(always)] pure fn min(other: &f32) -> f32 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &f32) -> f32 { if self > *other { self } else { *other } }
}

impl f64: MinMax {
    #[inline(always)] pure fn min(other: &f64) -> f64 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &f64) -> f64 { if self > *other { self } else { *other } }
}

impl float: MinMax {
    #[inline(always)] pure fn min(other: &float) -> float { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &float) -> float { if self > *other { self } else { *other } }
}

/**
 * TODO: This trait will be implemented for <T:NumCast Ord> when generic trait
 * support is improved. At the moment this would cause a conflict between the
 * implementations in lmath::vector.
 */
trait Abs {
    pure fn abs() -> self;
}

#[inline(always)]
pure fn abs<T: Abs>(x: &T) -> T {
    x.abs()
}

impl i8: Abs {
    #[inline(always)]
    pure fn abs() -> i8 {
        if self >= 0 { self }
        else         {-self }
    }
}

impl i16: Abs {
    #[inline(always)]
    pure fn abs() -> i16 {
        if self >= 0 { self }
        else         {-self }
    }
}

impl i32: Abs {
    #[inline(always)]
    pure fn abs() -> i32 {
        if self >= 0 { self }
        else         {-self }
    }
}

impl i64: Abs {
    #[inline(always)]
    pure fn abs() -> i64 {
        if self >= 0 { self }
        else         {-self }
    }
}

impl int: Abs {
    #[inline(always)]
    pure fn abs() -> int {
        if self >= 0 { self }
        else         {-self }
    }
}

impl f32: Abs {
    #[inline(always)]
    pure fn abs() -> f32 {
        if self >= 0f32 { self }
        else            {-self }
    }
}

impl f64: Abs {
    #[inline(always)]
    pure fn abs() -> f64 {
        if self >= 0f64 { self }
        else            {-self }
    }
}

impl float: Abs {
    #[inline(always)]
    pure fn abs() -> float {
        if self >= 0f { self }
        else          {-self }
    }
}

pub trait Sqrt {
    pure fn sqrt() -> self;
}

#[inline(always)]
pure fn sqrt<T:Sqrt>(n: T) -> T {
    n.sqrt()
}

pub impl<T: NumCast> T: Sqrt {
    #[inline(always)]
    pure fn sqrt() -> T {
        f64::sqrt(self.cast()).cast()
    }
}

pub trait Trig {
    pure fn sin() -> self;
    pure fn cos() -> self;
    pure fn tan() -> self;
    pure fn asin() -> self;
    pure fn acos() -> self;
    pure fn atan() -> self;
    pure fn sinh() -> self;
    pure fn cosh() -> self;
    pure fn tanh() -> self;
    pure fn atan2(n: self) -> self;
}

#[inline(always)] pub pure fn sin<T:Trig>(n: T) -> T { n.sin() }
#[inline(always)] pub pure fn cos<T:Trig>(n: T) -> T { n.cos() }
#[inline(always)] pub pure fn tan<T:Trig>(n: T) -> T { n.tan() }
#[inline(always)] pub pure fn asin<T:Trig>(n: T) -> T { n.asin() }
#[inline(always)] pub pure fn acos<T:Trig>(n: T) -> T { n.acos() }
#[inline(always)] pub pure fn atan<T:Trig>(n: T) -> T { n.atan() }
#[inline(always)] pub pure fn sinh<T:Trig>(n: T) -> T { n.sinh() }
#[inline(always)] pub pure fn cosh<T:Trig>(n: T) -> T { n.cosh() }
#[inline(always)] pub pure fn tanh<T:Trig>(n: T) -> T { n.tanh() }
#[inline(always)] pub pure fn atan2<T:Trig>(a: T, b: T) -> T { a.atan2(move b) }

pub impl<T:NumCast> T: Trig {
    #[inline(always)] pure fn sin() -> T { f64::sin(self.cast()).cast() }
    #[inline(always)] pure fn cos() -> T { f64::cos(self.cast()).cast() }
    #[inline(always)] pure fn tan() -> T { f64::tan(self.cast()).cast() }
    #[inline(always)] pure fn asin() -> T { f64::asin(self.cast()).cast() }
    #[inline(always)] pure fn acos() -> T { f64::acos(self.cast()).cast() }
    #[inline(always)] pure fn atan() -> T { f64::atan(self.cast()).cast() }
    #[inline(always)] pure fn sinh() -> T { f64::sinh(self.cast()).cast() }
    #[inline(always)] pure fn cosh() -> T { f64::cosh(self.cast()).cast() }
    #[inline(always)] pure fn tanh() -> T { f64::tanh(self.cast()).cast() }
    #[inline(always)] pure fn atan2(n: T) -> T { f64::atan2(self.cast(), move n.cast()).cast() }
}

pub trait AngleUnits {
    pure fn to_radians() -> self;
    pure fn to_degrees() -> self;
}

#[inline(always)] pub pure fn radians<T:AngleUnits>(degrees: T) -> T { degrees.to_radians() }
#[inline(always)] pub pure fn degrees<T:AngleUnits>(radians: T) -> T { radians.to_degrees() }

pub impl<T:Num NumCast> T: AngleUnits {
    #[inline(always)] pure fn to_radians() -> T { self * cast(f64::consts::pi / 180f64) }
    #[inline(always)] pure fn to_degrees() -> T { self * cast(180f64 / f64::consts::pi) }
}<commit_msg>Formatting<commit_after>use cmp::Ord;
use num::*;

use ncast::*;

// TODO: move to a more appropriate module
pub trait ToPtr<T> {
    pure fn to_ptr() -> *T;
}

pub trait ExactEq {
    pure fn exact_eq(other: &self) -> bool;
}

/**
 * TODO: This trait will be implemented for <T:Num Ord> when generic trait
 * support is improved. At the moment this would cause a conflict between the
 * implementations in lmath::vector.
 */
trait MinMax {
    pure fn min(other: &self) -> self;
    pure fn max(other: &self) -> self;
}

#[inline(always)] pure fn min<T:MinMax>(a: &T, b: &T) -> T { a.min(b) }
#[inline(always)] pure fn max<T:MinMax>(a: &T, b: &T) -> T { a.max(b) }

impl u8: MinMax {
    #[inline(always)] pure fn min(other: &u8) -> u8 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &u8) -> u8 { if self > *other { self } else { *other } }
}

impl u16: MinMax {
    #[inline(always)] pure fn min(other: &u16) -> u16 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &u16) -> u16 { if self > *other { self } else { *other } }
}

impl u32: MinMax {
    #[inline(always)] pure fn min(other: &u32) -> u32 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &u32) -> u32 { if self > *other { self } else { *other } }
}

impl u64: MinMax {
    #[inline(always)] pure fn min(other: &u64) -> u64 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &u64) -> u64 { if self > *other { self } else { *other } }
}

impl uint: MinMax {
    #[inline(always)] pure fn min(other: &uint) -> uint { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &uint) -> uint { if self > *other { self } else { *other } }
}

impl i8: MinMax {
    #[inline(always)] pure fn min(other: &i8) -> i8 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &i8) -> i8 { if self > *other { self } else { *other } }
}

impl i16: MinMax {
    #[inline(always)] pure fn min(other: &i16) -> i16 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &i16) -> i16 { if self > *other { self } else { *other } }
}

impl i32: MinMax {
    #[inline(always)] pure fn min(other: &i32) -> i32 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &i32) -> i32 { if self > *other { self } else { *other } }
}

impl i64: MinMax {
    #[inline(always)] pure fn min(other: &i64) -> i64 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &i64) -> i64 { if self > *other { self } else { *other } }
}

impl int: MinMax {
    #[inline(always)] pure fn min(other: &int) -> int { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &int) -> int { if self > *other { self } else { *other } }
}

impl f32: MinMax {
    #[inline(always)] pure fn min(other: &f32) -> f32 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &f32) -> f32 { if self > *other { self } else { *other } }
}

impl f64: MinMax {
    #[inline(always)] pure fn min(other: &f64) -> f64 { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &f64) -> f64 { if self > *other { self } else { *other } }
}

impl float: MinMax {
    #[inline(always)] pure fn min(other: &float) -> float { if self < *other { self } else { *other } }
    #[inline(always)] pure fn max(other: &float) -> float { if self > *other { self } else { *other } }
}

/**
 * TODO: This trait will be implemented for <T:NumCast Ord> when generic trait
 * support is improved. At the moment this would cause a conflict between the
 * implementations in lmath::vector.
 */
trait Abs {
    pure fn abs() -> self;
}

#[inline(always)]
pure fn abs<T:Abs>(x: &T) -> T {
    x.abs()
}

impl i8:    Abs { #[inline(always)] pure fn abs() -> i8    { if self >= 0    { self } else {-self } } }
impl i16:   Abs { #[inline(always)] pure fn abs() -> i16   { if self >= 0    { self } else {-self } } }
impl i32:   Abs { #[inline(always)] pure fn abs() -> i32   { if self >= 0    { self } else {-self } } }
impl i64:   Abs { #[inline(always)] pure fn abs() -> i64   { if self >= 0    { self } else {-self } } }
impl int:   Abs { #[inline(always)] pure fn abs() -> int   { if self >= 0    { self } else {-self } } }
impl f32:   Abs { #[inline(always)] pure fn abs() -> f32   { if self >= 0f32 { self } else {-self } } }
impl f64:   Abs { #[inline(always)] pure fn abs() -> f64   { if self >= 0f64 { self } else {-self } } }
impl float: Abs { #[inline(always)] pure fn abs() -> float { if self >= 0f   { self } else {-self } } }

pub trait Sqrt {
    pure fn sqrt() -> self;
}

#[inline(always)]
pure fn sqrt<T:Sqrt>(n: T) -> T {
    n.sqrt()
}

pub impl<T: NumCast> T: Sqrt {
    #[inline(always)]
    pure fn sqrt() -> T {
        f64::sqrt(self.cast()).cast()
    }
}

pub trait Trig {
    pure fn sin() -> self;
    pure fn cos() -> self;
    pure fn tan() -> self;
    pure fn asin() -> self;
    pure fn acos() -> self;
    pure fn atan() -> self;
    pure fn sinh() -> self;
    pure fn cosh() -> self;
    pure fn tanh() -> self;
    pure fn atan2(n: self) -> self;
}

#[inline(always)] pub pure fn sin<T:Trig>(n: T) -> T { n.sin() }
#[inline(always)] pub pure fn cos<T:Trig>(n: T) -> T { n.cos() }
#[inline(always)] pub pure fn tan<T:Trig>(n: T) -> T { n.tan() }
#[inline(always)] pub pure fn asin<T:Trig>(n: T) -> T { n.asin() }
#[inline(always)] pub pure fn acos<T:Trig>(n: T) -> T { n.acos() }
#[inline(always)] pub pure fn atan<T:Trig>(n: T) -> T { n.atan() }
#[inline(always)] pub pure fn sinh<T:Trig>(n: T) -> T { n.sinh() }
#[inline(always)] pub pure fn cosh<T:Trig>(n: T) -> T { n.cosh() }
#[inline(always)] pub pure fn tanh<T:Trig>(n: T) -> T { n.tanh() }
#[inline(always)] pub pure fn atan2<T:Trig>(a: T, b: T) -> T { a.atan2(move b) }

pub impl<T:NumCast> T: Trig {
    #[inline(always)] pure fn sin() -> T { f64::sin(self.cast()).cast() }
    #[inline(always)] pure fn cos() -> T { f64::cos(self.cast()).cast() }
    #[inline(always)] pure fn tan() -> T { f64::tan(self.cast()).cast() }
    #[inline(always)] pure fn asin() -> T { f64::asin(self.cast()).cast() }
    #[inline(always)] pure fn acos() -> T { f64::acos(self.cast()).cast() }
    #[inline(always)] pure fn atan() -> T { f64::atan(self.cast()).cast() }
    #[inline(always)] pure fn sinh() -> T { f64::sinh(self.cast()).cast() }
    #[inline(always)] pure fn cosh() -> T { f64::cosh(self.cast()).cast() }
    #[inline(always)] pure fn tanh() -> T { f64::tanh(self.cast()).cast() }
    #[inline(always)] pure fn atan2(n: T) -> T { f64::atan2(self.cast(), move n.cast()).cast() }
}

pub trait AngleUnits {
    pure fn to_radians() -> self;
    pure fn to_degrees() -> self;
}

#[inline(always)] pub pure fn radians<T:AngleUnits>(degrees: T) -> T { degrees.to_radians() }
#[inline(always)] pub pure fn degrees<T:AngleUnits>(radians: T) -> T { radians.to_degrees() }

pub impl<T:Num NumCast> T: AngleUnits {
    #[inline(always)] pure fn to_radians() -> T { self * cast(f64::consts::pi / 180f64) }
    #[inline(always)] pure fn to_degrees() -> T { self * cast(180f64 / f64::consts::pi) }
}<|endoftext|>