<commit_before>#[macro_use]
extern crate criterion;
use serde_json;

use boxcars::crc::calc_crc;
use boxcars::*;
use criterion::{black_box, Criterion};
use std::io;

fn bench_crc(c: &mut Criterion) {
    c.bench_function("bench_crc", |b| {
        let data = include_bytes!("../assets/rumble.replay");
        b.iter(|| {
            black_box(calc_crc(&data[..]));
        })
    });
}

fn bench_parse_crc_body(c: &mut Criterion) {
    c.bench_function("bench_parse_crc_body", |b| {
        let data = include_bytes!("../assets/3381.replay");
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .always_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
}

fn bench_parse_no_crc_body(c: &mut Criterion) {
    c.bench_function("bench_parse_no_crc_body", |b| {
        let data = include_bytes!("../assets/3381.replay");
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
}

fn bench_parse_no_crc_no_body(c: &mut Criterion) {
    c.bench_function("bench_parse_no_crc_no_body", |b| {
        let data = include_bytes!("../assets/3381.replay");
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .never_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
}

fn bench_parse_crc_json(c: &mut Criterion) {
    c.bench_function("bench_parse_crc_json", |b| {
        let data = include_bytes!("../assets/3381.replay");
        b.iter(|| {
            let data = ParserBuilder::new(data).always_check_crc().parse().unwrap();
            black_box(serde_json::to_writer(&mut io::sink(), &data).is_ok());
        });
    });
}

fn bench_parse_no_crc_json(c: &mut Criterion) {
    c.bench_function("bench_parse_no_crc_json", |b| {
        let data = include_bytes!("../assets/3381.replay");
        b.iter(|| {
            let replay = ParserBuilder::new(data)
                .on_error_check_crc()
                .parse()
                .unwrap();
            black_box(serde_json::to_writer(&mut io::sink(), &replay).is_ok());
        });
    });
}

criterion_group!(
    benches,
    bench_crc,
    bench_parse_crc_body,
    bench_parse_no_crc_body,
    bench_parse_no_crc_no_body,
    bench_parse_crc_json,
    bench_parse_no_crc_json
);

criterion_main!(benches);
<commit_msg>Fix benchmark compilation<commit_after>#[macro_use]
extern crate criterion;
use serde_json;

use boxcars::crc::calc_crc;
use boxcars::*;
use criterion::{black_box, Criterion};
use std::io;

fn bench_crc(c: &mut Criterion) {
    c.bench_function("bench_crc", |b| {
        let data = include_bytes!("../assets/replays/good/rumble.replay");
        b.iter(|| {
            black_box(calc_crc(&data[..]));
        })
    });
}

fn bench_parse_crc_body(c: &mut Criterion) {
    c.bench_function("bench_parse_crc_body", |b| {
        let data = include_bytes!("../assets/replays/good/3381.replay");
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .always_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
}

fn bench_parse_no_crc_body(c: &mut Criterion) {
    c.bench_function("bench_parse_no_crc_body", |b| {
        let data = include_bytes!("../assets/replays/good/3381.replay");
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .must_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
}

fn bench_parse_no_crc_no_body(c: &mut Criterion) {
    c.bench_function("bench_parse_no_crc_no_body", |b| {
        let data = include_bytes!("../assets/replays/good/3381.replay");
        b.iter(|| {
            black_box(
                ParserBuilder::new(data)
                    .on_error_check_crc()
                    .never_parse_network_data()
                    .parse()
                    .is_ok(),
            )
        });
    });
}

fn bench_parse_crc_json(c: &mut Criterion) {
    c.bench_function("bench_parse_crc_json", |b| {
        let data = include_bytes!("../assets/replays/good/3381.replay");
        b.iter(|| {
            let data = ParserBuilder::new(data).always_check_crc().parse().unwrap();
            black_box(serde_json::to_writer(&mut io::sink(), &data).is_ok());
        });
    });
}

fn bench_parse_no_crc_json(c: &mut Criterion) {
    c.bench_function("bench_parse_no_crc_json", |b| {
        let data = include_bytes!("../assets/replays/good/3381.replay");
        b.iter(|| {
            let replay = ParserBuilder::new(data)
                .on_error_check_crc()
                .parse()
                .unwrap();
            black_box(serde_json::to_writer(&mut io::sink(), &replay).is_ok());
        });
    });
}

criterion_group!(
    benches,
    bench_crc,
    bench_parse_crc_body,
    bench_parse_no_crc_body,
    bench_parse_no_crc_no_body,
    bench_parse_crc_json,
    bench_parse_no_crc_json
);

criterion_main!(benches);
<|endoftext|>


<commit_before>#![crate_name = "uu_sort"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Yin <mikeyin@mikeyin.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![allow(dead_code)]

extern crate getopts;
extern crate libc;

#[macro_use]
extern crate uucore;

use std::cmp::Ordering;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Read, stdin, stdout, Write};
use std::path::Path;
use uucore::fs::is_stdin_interactive;

static NAME: &'static str = "sort";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

static DECIMAL_PT: char = '.';
static THOUSANDS_SEP: char = ',';

enum SortMode {
    Numeric,
    HumanNumeric,
    Default,
}

struct Settings {
    mode: SortMode,
    reverse: bool,
    outfile: Option<String>,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            mode: SortMode::Default,
            reverse: false,
            outfile: None,
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut settings: Settings = Default::default();
    let mut opts = getopts::Options::new();

    opts.optflag("n", "numeric-sort", "compare according to string numerical value");
    opts.optflag("h", "human-numeric-sort", "compare according to human readable sizes, eg 1M > 100k");
    opts.optflag("r", "reverse", "reverse the output");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("", "version", "output version information and exit");
    opts.optopt("o", "output", "write output to FILENAME instead of stdout", "FILENAME");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };
    if matches.opt_present("help") {
        let msg = format!("{0} {1}

Usage:
 {0} [OPTION]... [FILE]...

Write the sorted concatenation of all FILE(s) to standard output.

Mandatory arguments for long options are mandatory for short options too.

With no FILE, or when FILE is -, read standard input.", NAME, VERSION);
        print!("{}", opts.usage(&msg));
        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    settings.mode = if matches.opt_present("numeric-sort") {
        SortMode::Numeric
    } else if matches.opt_present("human-numeric-sort") {
        SortMode::HumanNumeric
    } else {
        SortMode::Default
    };

    settings.reverse = matches.opt_present("reverse");
    settings.outfile = matches.opt_str("output");

    let mut files = matches.free;
    if files.is_empty() {
        /* if no file, default to stdin */
        files.push("-".to_owned());
    }

    exec(files, &settings);

    0
}

fn exec(files: Vec<String>, settings: &Settings) {
    for path in &files {
        let (reader, _) = match open(path) {
            Some(x) => x,
            None => continue,
        };

        let buf_reader = BufReader::new(reader);
        let mut lines = Vec::new();

        for line in buf_reader.lines() {
            match line {
                Ok(n) => {
                    lines.push(n);
                },
                _ => break
            }
        }

        match settings.mode {
            SortMode::Numeric => lines.sort_by(numeric_compare),
            SortMode::HumanNumeric => lines.sort_by(human_numeric_size_compare),
            SortMode::Default => lines.sort()
        }

        let iter = lines.iter();
        if settings.reverse {
            print_sorted(iter.rev(), &settings.outfile);
        } else {
            print_sorted(iter, &settings.outfile)
        };
    }
}

/// Parse the beginning string into an f64, returning -inf instead of NaN on errors.
fn permissive_f64_parse(a: &str) -> f64{
    //Maybe should be split on non-digit, but then 10e100 won't parse properly.
    //On the flip side, this will give NEG_INFINITY for "1,234", which might be OK
    //because there's no way to handle both CSV and thousands separators without a new flag.
    //GNU sort treats "1,234" as "1" in numeric, so maybe it's fine.
    let sa: &str = a.split_whitespace().next().unwrap();
    match sa.parse::<f64>() {
        Ok(a) => a,
        Err(_) => std::f64::NEG_INFINITY
    }
}

/// Compares two floating point numbers, with errors being assumned to be -inf.
/// Stops coercing at the first whitespace char, so 1e2 will parse as 100 but
/// 1,000 will parse as -inf.
fn numeric_compare(a: &String, b: &String) -> Ordering {
    let fa = permissive_f64_parse(a);
    let fb = permissive_f64_parse(b);
    //f64::cmp isn't implemented because NaN messes with it
    //but we sidestep that with permissive_f64_parse so just fake it
    if fa > fb {
        Ordering::Greater
    }
    else if fa < fb {
        Ordering::Less
    }
    else {
        Ordering::Equal
    }
}

fn human_numeric_convert(a: &String) -> f64 {
    let int_iter = a.chars();
    let suffix_iter = a.chars();
    let int_str: String = int_iter.take_while(|c| c.is_numeric()).collect();
    let suffix = suffix_iter.skip_while(|c| c.is_numeric()).next();
    let int_part = match int_str.parse::<f64>() {
        Ok(i) => i,
        Err(_) => -1f64
    } as f64;
    let suffix: f64 = match suffix.unwrap_or('\0') {
        'K' => 1000f64,
        'M' => 1E6,
        'G' => 1E9,
        'T' => 1E12,
        'P' => 1E15,
        _ => 1f64
    };
    int_part * suffix
}

/// Compare two strings as if they are human readable sizes.
/// AKA 1M > 100k
fn human_numeric_size_compare(a: &String, b: &String) -> Ordering {
    let fa = human_numeric_convert(a);
    let fb = human_numeric_convert(b);
    if fa > fb {
        Ordering::Greater
    }
    else if fa < fb {
        Ordering::Less
    }
    else {
        Ordering::Equal
    }

}

fn print_sorted<S, T: Iterator<Item=S>>(iter: T, outfile: &Option<String>) where S: std::fmt::Display {
    let mut file: Box<Write> = match *outfile {
        Some(ref filename) => {
            match File::create(Path::new(&filename)) {
                Ok(f) => Box::new(BufWriter::new(f)) as Box<Write>,
                Err(e) => {
                    show_error!("sort: {0}: {1}", filename, e.to_string());
                    panic!("Could not open output file");
                },
            }
        },
        None => Box::new(stdout()) as Box<Write>,
    };

    for line in iter {
        let str = format!("{}\n", line);
        match file.write_all(str.as_bytes()) {
            Err(e) => {
                show_error!("sort: {0}", e.to_string());
                panic!("Write failed");
            },
            Ok(_) => (),
        }
    }
}

// from cat.rs
fn open(path: &str) -> Option<(Box<Read>, bool)> {
    if path == "-" {
        let stdin = stdin();
        return Some((Box::new(stdin) as Box<Read>, is_stdin_interactive()));
    }

    match File::open(Path::new(path)) {
        Ok(f) => Some((Box::new(f) as Box<Read>, false)),
        Err(e) => {
            show_error!("sort: {0}: {1}", path, e.to_string());
            None
        },
    }
}
<commit_msg>sort: add month sort<commit_after>#![crate_name = "uu_sort"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Michael Yin <mikeyin@mikeyin.org>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![allow(dead_code)]

extern crate getopts;
extern crate libc;

#[macro_use]
extern crate uucore;

use std::cmp::Ordering;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Read, stdin, stdout, Write};
use std::path::Path;
use uucore::fs::is_stdin_interactive;

static NAME: &'static str = "sort";
static VERSION: &'static str = env!("CARGO_PKG_VERSION");

static DECIMAL_PT: char = '.';
static THOUSANDS_SEP: char = ',';

enum SortMode {
    Numeric,
    HumanNumeric,
    Month,
    Default,
}

struct Settings {
    mode: SortMode,
    reverse: bool,
    outfile: Option<String>,
}

impl Default for Settings {
    fn default() -> Settings {
        Settings {
            mode: SortMode::Default,
            reverse: false,
            outfile: None,
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut settings: Settings = Default::default();
    let mut opts = getopts::Options::new();

    opts.optflag("n", "numeric-sort", "compare according to string numerical value");
    opts.optflag("h", "human-numeric-sort", "compare according to human readable sizes, eg 1M > 100k");
    opts.optflag("M", "month-sort", "compare according to month name abbreviation");
    opts.optflag("r", "reverse", "reverse the output");
    opts.optflag("h", "help", "display this help and exit");
    opts.optflag("", "version", "output version information and exit");
    opts.optopt("o", "output", "write output to FILENAME instead of stdout", "FILENAME");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };
    if matches.opt_present("help") {
        let msg = format!("{0} {1}

Usage:
 {0} [OPTION]... [FILE]...

Write the sorted concatenation of all FILE(s) to standard output.

Mandatory arguments for long options are mandatory for short options too.

With no FILE, or when FILE is -, read standard input.", NAME, VERSION);
        print!("{}", opts.usage(&msg));
        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    settings.mode = if matches.opt_present("numeric-sort") {
        SortMode::Numeric
    } else if matches.opt_present("human-numeric-sort") {
        SortMode::HumanNumeric
    } else if matches.opt_present("month-sort") {
        SortMode::Month
    } else {
        SortMode::Default
    };

    settings.reverse = matches.opt_present("reverse");
    settings.outfile = matches.opt_str("output");

    let mut files = matches.free;
    if files.is_empty() {
        /* if no file, default to stdin */
        files.push("-".to_owned());
    }

    exec(files, &settings);

    0
}

fn exec(files: Vec<String>, settings: &Settings) {
    for path in &files {
        let (reader, _) = match open(path) {
            Some(x) => x,
            None => continue,
        };

        let buf_reader = BufReader::new(reader);
        let mut lines = Vec::new();

        for line in buf_reader.lines() {
            match line {
                Ok(n) => {
                    lines.push(n);
                },
                _ => break
            }
        }

        match settings.mode {
            SortMode::Numeric => lines.sort_by(numeric_compare),
            SortMode::HumanNumeric => lines.sort_by(human_numeric_size_compare),
            SortMode::Month => lines.sort_by(month_compare),
            SortMode::Default => lines.sort()
        }

        let iter = lines.iter();
        if settings.reverse {
            print_sorted(iter.rev(), &settings.outfile);
        } else {
            print_sorted(iter, &settings.outfile)
        };
    }
}

/// Parse the beginning string into an f64, returning -inf instead of NaN on errors.
fn permissive_f64_parse(a: &str) -> f64{
    //Maybe should be split on non-digit, but then 10e100 won't parse properly.
    //On the flip side, this will give NEG_INFINITY for "1,234", which might be OK
    //because there's no way to handle both CSV and thousands separators without a new flag.
    //GNU sort treats "1,234" as "1" in numeric, so maybe it's fine.
    let sa: &str = a.split_whitespace().next().unwrap();
    match sa.parse::<f64>() {
        Ok(a) => a,
        Err(_) => std::f64::NEG_INFINITY
    }
}

/// Compares two floating point numbers, with errors being assumned to be -inf.
/// Stops coercing at the first whitespace char, so 1e2 will parse as 100 but
/// 1,000 will parse as -inf.
fn numeric_compare(a: &String, b: &String) -> Ordering {
    let fa = permissive_f64_parse(a);
    let fb = permissive_f64_parse(b);
    //f64::cmp isn't implemented because NaN messes with it
    //but we sidestep that with permissive_f64_parse so just fake it
    if fa > fb {
        Ordering::Greater
    }
    else if fa < fb {
        Ordering::Less
    }
    else {
        Ordering::Equal
    }
}

fn human_numeric_convert(a: &String) -> f64 {
    let int_iter = a.chars();
    let suffix_iter = a.chars();
    let int_str: String = int_iter.take_while(|c| c.is_numeric()).collect();
    let suffix = suffix_iter.skip_while(|c| c.is_numeric()).next();
    let int_part = match int_str.parse::<f64>() {
        Ok(i) => i,
        Err(_) => -1f64
    } as f64;
    let suffix: f64 = match suffix.unwrap_or('\0') {
        'K' => 1000f64,
        'M' => 1E6,
        'G' => 1E9,
        'T' => 1E12,
        'P' => 1E15,
        _ => 1f64
    };
    int_part * suffix
}

/// Compare two strings as if they are human readable sizes.
/// AKA 1M > 100k
fn human_numeric_size_compare(a: &String, b: &String) -> Ordering {
    let fa = human_numeric_convert(a);
    let fb = human_numeric_convert(b);
    if fa > fb {
        Ordering::Greater
    }
    else if fa < fb {
        Ordering::Less
    }
    else {
        Ordering::Equal
    }
}

#[derive(Eq, Ord, PartialEq, PartialOrd)]
enum Month {
    Unknown     = 0,
    January     = 1,
    February    = 2,
    March       = 3,
    April       = 4,
    May         = 5,
    June        = 6,
    July        = 7,
    August      = 8,
    September   = 9,
    October     = 10,
    November    = 11,
    December    = 12,
}

/// Parse the beginning string into a Month, returning Month::Unknown on errors.
fn month_parse(line: &String) -> Month {
    match line.split_whitespace().next().unwrap().to_uppercase().as_ref() {
        "JAN" => Month::January,
        "FEB" => Month::February,
        "MAR" => Month::March,
        "APR" => Month::April,
        "MAY" => Month::May,
        "JUN" => Month::June,
        "JUL" => Month::July,
        "AUG" => Month::August,
        "SEP" => Month::September,
        "OCT" => Month::October,
        "NOV" => Month::November,
        "DEC" => Month::December,
        _     => Month::Unknown,
    }
}

fn month_compare(a: &String, b: &String) -> Ordering {
    month_parse(a).cmp(&month_parse(b))
}

fn print_sorted<S, T: Iterator<Item=S>>(iter: T, outfile: &Option<String>) where S: std::fmt::Display {
    let mut file: Box<Write> = match *outfile {
        Some(ref filename) => {
            match File::create(Path::new(&filename)) {
                Ok(f) => Box::new(BufWriter::new(f)) as Box<Write>,
                Err(e) => {
                    show_error!("sort: {0}: {1}", filename, e.to_string());
                    panic!("Could not open output file");
                },
            }
        },
        None => Box::new(stdout()) as Box<Write>,
    };

    for line in iter {
        let str = format!("{}\n", line);
        match file.write_all(str.as_bytes()) {
            Err(e) => {
                show_error!("sort: {0}", e.to_string());
                panic!("Write failed");
            },
            Ok(_) => (),
        }
    }
}

// from cat.rs
fn open(path: &str) -> Option<(Box<Read>, bool)> {
    if path == "-" {
        let stdin = stdin();
        return Some((Box::new(stdin) as Box<Read>, is_stdin_interactive()));
    }

    match File::open(Path::new(path)) {
        Ok(f) => Some((Box::new(f) as Box<Read>, false)),
        Err(e) => {
            show_error!("sort: {0}: {1}", path, e.to_string());
            None
        },
    }
}
<|endoftext|>


<commit_before>pub use ext::framework::command::{Command, CommandType, CommandGroup, CommandOrAlias};
pub use ext::framework::create_command::CreateCommand;

use std::default::Default;
use std::sync::Arc;
use ::client::Context;
use ::model::Message;

#[derive(Default)]
pub struct CreateGroup(pub CommandGroup);

/// Used to create command groups
///
/// # Examples
///
/// Create group named Information where all commands are prefixed with info,
/// and add one command named "name". For example, if prefix is "~", we say "~info name"
/// to call the "name" command.
///
/// ```rust,ignore
/// framework.group("Information", |g| g
///     .prefix("info")
///     .command("name", |c| c
///         .exec_str("Hakase")))
/// ```
impl CreateGroup {
    /// Adds a command to group.
    pub fn command<F>(mut self, command_name: &str, f: F) -> Self
        where F: FnOnce(CreateCommand) -> CreateCommand {
        let cmd = f(CreateCommand(Command::default())).0;

        for n in &cmd.aliases {
            if let Some(ref prefix) = self.0.prefix {
                self.0.commands.insert(format!("{} {}", prefix, n.to_owned()), CommandOrAlias::Alias(format!("{} {}", prefix, command_name.to_string())));
            } else {
                self.0.commands.insert(n.to_owned(), CommandOrAlias::Alias(command_name.to_string()));
            }
        }

        self.0.commands.insert(command_name.to_owned(), CommandOrAlias::Command(Arc::new(cmd)));

        self
    }

    /// Adds a command to group with simplified API.
    /// You can return Err(string) if there's an error.
    pub fn on<F>(mut self, command_name: &str, f: F) -> Self
        where F: Fn(&mut Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static {
        let cmd = Arc::new(Command::new(f));

        self.0.commands.insert(command_name.to_owned(), CommandOrAlias::Command(cmd));

        self
    }

    /// If prefix is set, it will be required before all command names.
    /// For example, if bot prefix is "~" and group prefix is "image"
    /// we'd call a subcommand named "hibiki" by sending "~image hibiki".
    ///
    /// **Note**: serenity automatically puts a space after group prefix.
    ///
    /// **Note**: It's suggested to call this first when making a group.
    pub fn prefix(mut self, desc: &str) -> Self {
        self.0.prefix = Some(desc.to_owned());

        self
    }
}
<commit_msg>Move CreateGroup docs to the struct<commit_after>pub use ext::framework::command::{Command, CommandType, CommandGroup, CommandOrAlias};
pub use ext::framework::create_command::CreateCommand;

use std::default::Default;
use std::sync::Arc;
use ::client::Context;
use ::model::Message;

/// Used to create command groups
///
/// # Examples
///
/// Create group named Information where all commands are prefixed with info,
/// and add one command named "name". For example, if prefix is "~", we say "~info name"
/// to call the "name" command.
///
/// ```rust,ignore
/// framework.group("Information", |g| g
///     .prefix("info")
///     .command("name", |c| c
///         .exec_str("Hakase")))
/// ```
#[derive(Default)]
pub struct CreateGroup(pub CommandGroup);

impl CreateGroup {
    /// Adds a command to group.
    pub fn command<F>(mut self, command_name: &str, f: F) -> Self
        where F: FnOnce(CreateCommand) -> CreateCommand {
        let cmd = f(CreateCommand(Command::default())).0;

        for n in &cmd.aliases {
            if let Some(ref prefix) = self.0.prefix {
                self.0.commands.insert(format!("{} {}", prefix, n.to_owned()), CommandOrAlias::Alias(format!("{} {}", prefix, command_name.to_string())));
            } else {
                self.0.commands.insert(n.to_owned(), CommandOrAlias::Alias(command_name.to_string()));
            }
        }

        self.0.commands.insert(command_name.to_owned(), CommandOrAlias::Command(Arc::new(cmd)));

        self
    }

    /// Adds a command to group with simplified API.
    /// You can return Err(string) if there's an error.
    pub fn on<F>(mut self, command_name: &str, f: F) -> Self
        where F: Fn(&mut Context, &Message, Vec<String>) -> Result<(), String> + Send + Sync + 'static {
        let cmd = Arc::new(Command::new(f));

        self.0.commands.insert(command_name.to_owned(), CommandOrAlias::Command(cmd));

        self
    }

    /// If prefix is set, it will be required before all command names.
    /// For example, if bot prefix is "~" and group prefix is "image"
    /// we'd call a subcommand named "hibiki" by sending "~image hibiki".
    ///
    /// **Note**: serenity automatically puts a space after group prefix.
    ///
    /// **Note**: It's suggested to call this first when making a group.
    pub fn prefix(mut self, desc: &str) -> Self {
        self.0.prefix = Some(desc.to_owned());

        self
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::bundle::{self, Bundle, Exchange, Request, Response, Uri, Version};
use crate::prelude::*;
use cbor_event::Len;
use http::{
    header::{HeaderMap, HeaderName, HeaderValue},
    StatusCode,
};
use std::collections::HashSet;
use std::convert::TryInto;
use std::io::Cursor;

pub(crate) fn parse(bytes: impl AsRef<[u8]>) -> Result<Bundle> {
    Decoder::new(bytes).decode()
}

#[derive(Debug)]
struct SectionOffset {
    name: String,
    offset: u64,
    length: u64,
}

#[derive(Debug)]
struct ResponseLocation {
    offset: u64,
    length: u64,
}

impl ResponseLocation {
    pub fn new(responses_section_offset: u64, offset: u64, length: u64) -> ResponseLocation {
        ResponseLocation {
            offset: responses_section_offset + offset,
            length,
        }
    }
}

#[derive(Debug)]
struct RequestEntry {
    request: Request,
    response_location: ResponseLocation,
}

#[derive(Debug)]
struct Metadata {
    version: Version,
    section_offsets: Vec<SectionOffset>,
    requests: Vec<RequestEntry>,
    primary_url: Option<PrimaryUrl>,
}

type Deserializer<R> = cbor_event::de::Deserializer<R>;

struct Decoder<T> {
    de: Deserializer<Cursor<T>>,
}

impl<T> Decoder<T> {
    fn new(buf: T) -> Self {
        Decoder {
            de: Deserializer::from(Cursor::new(buf)),
        }
    }
}

type PrimaryUrl = Uri;

impl<T: AsRef<[u8]>> Decoder<T> {
    fn decode(&mut self) -> Result<Bundle> {
        let metadata = self.read_metadata()?;
        Ok(Bundle {
            version: metadata.version,
            primary_url: metadata.primary_url,
            exchanges: self.read_responses(metadata.requests)?,
        })
    }

    fn read_metadata(&mut self) -> Result<Metadata> {
        ensure!(
            self.read_array_len()? as usize == bundle::TOP_ARRAY_LEN,
            "Invalid header"
        );
        self.read_magic_bytes()?;
        let version = self.read_version()?;
        log::debug!("version: {:?}", version);
        let section_offsets = self.read_section_offsets()?;
        log::debug!("section_offsets {:?}", section_offsets);
        // let primary_url = self.read_primary_url()?;
        let (requests, primary_url) = self.read_sections(&section_offsets)?;
        Ok(Metadata {
            version,
            section_offsets,
            requests,
            primary_url,
        })
    }

    fn read_magic_bytes(&mut self) -> Result<()> {
        log::debug!("read_magic_bytes");
        let magic: Vec<u8> = self.de.bytes().context("Invalid magic bytes")?;
        ensure!(magic == bundle::HEADER_MAGIC_BYTES, "Header magic mismatch");
        Ok(())
    }

    fn read_version(&mut self) -> Result<Version> {
        log::debug!("read_version");
        let bytes: Vec<u8> = self.de.bytes().context("Invalid version format")?;
        ensure!(
            bytes.len() == bundle::VERSION_BYTES_LEN,
            "Invalid version format"
        );
        let version: [u8; bundle::VERSION_BYTES_LEN] =
            AsRef::<[u8]>::as_ref(&bytes).try_into().unwrap();
        Ok(if &version == bundle::Version::Version1.bytes() {
            Version::Version1
        } else if &version == bundle::Version::VersionB2.bytes() {
            Version::VersionB2
        } else {
            Version::Unknown(version)
        })
    }

    fn read_section_offsets(&mut self) -> Result<Vec<SectionOffset>> {
        let bytes = self
            .de
            .bytes()
            .context("Failed to read sectionLength byte string")?;
        ensure!(
            bytes.len() < 8_192,
            format!("sectionLengthsLength is too long ({} bytes)", bytes.len())
        );
        Decoder::new(bytes).read_section_offsets_cbor(self.position())
    }

    fn read_array_len(&mut self) -> Result<u64> {
        match self.de.array()? {
            Len::Len(n) => Ok(n),
            _ => bail!("bundle: bundle: Failed to decode sectionOffset array header"),
        }
    }

    fn position(&self) -> u64 {
        self.de.as_ref().position()
    }

    fn read_section_offsets_cbor(&mut self, mut offset: u64) -> Result<Vec<SectionOffset>> {
        let n = self
            .read_array_len()
            .context("bundle: bundle: Failed to decode sectionOffset array header")?;
        let section_num = n / 2;
        offset += self.position();
        let mut seen_names = HashSet::new();
        let mut section_offsets = Vec::with_capacity(section_num as usize);
        for _ in 0..section_num {
            let name = self.de.text()?;
            ensure!(!seen_names.contains(&name), "Duplicate section name");
            seen_names.insert(name.clone());
            let length = self.de.unsigned_integer()?;
            section_offsets.push(SectionOffset {
                name,
                offset,
                length,
            });
            offset += length;
        }
        ensure!(!section_offsets.is_empty(), "bundle: section is empty");
        ensure!(
            section_offsets.last().unwrap().name == "responses",
            "bundle: Last section is not \"responses\""
        );
        Ok(section_offsets)
    }

    fn inner_buf(&self) -> &[u8] {
        self.de.as_ref().get_ref().as_ref()
    }

    fn new_decoder_from_range(&self, start: u64, end: u64) -> Decoder<&[u8]> {
        // TODO: Check range, instead of panic
        Decoder::new(&self.inner_buf()[start as usize..end as usize])
    }

    fn read_sections(
        &mut self,
        section_offsets: &[SectionOffset],
    ) -> Result<(Vec<RequestEntry>, Option<PrimaryUrl>)> {
        log::debug!("read_sections");
        let n = self
            .read_array_len()
            .context("Failed to read section header")?;
        log::debug!("n: {:?}", n);
        ensure!(
            n as usize == section_offsets.len(),
            format!(
                "bundle: Expected {} sections, got {} sections",
                section_offsets.len(),
                n
            )
        );

        let responses_section_offset = section_offsets.last().unwrap().offset;

        dbg!(responses_section_offset);

        let mut requests = vec![];
        let mut primary_url: Option<PrimaryUrl> = None;

        for SectionOffset {
            name,
            offset,
            length,
        } in section_offsets
        {
            if !bundle::KNOWN_SECTION_NAMES.iter().any(|&n| n == name) {
                log::warn!("Unknows section name: {}. Skipping", name);
                continue;
            }
            let mut section_decoder = self.new_decoder_from_range(*offset, offset + length);

            // TODO: Support ignoredSections
            match name.as_ref() {
                "index" => {
                    dbg!(name);
                    requests = section_decoder.read_index(responses_section_offset)?;
                }
                "responses" => {
                    dbg!(name);
                    // Skip responses section becuase we read responses later.
                }
                "primary-url" => {
                    dbg!(name);
                    primary_url = Some(section_decoder.read_primary_url()?);
                }
                _ => {
                    log::warn!("Unknown section found: {}", name);
                }
            }
        }
        Ok((requests, primary_url))
    }

    fn read_primary_url(&mut self) -> Result<PrimaryUrl> {
        log::debug!("read_primary_url");
        self.de
            .text()
            .context("bundle: Failed to read primary_url string")?
            .parse()
            .context("Failed to parse primary_url")
    }

    fn read_index(&mut self, responses_section_offset: u64) -> Result<Vec<RequestEntry>> {
        let index_map_len = match self.de.map()? {
            Len::Len(n) => n,
            Len::Indefinite => {
                bail!("bundle: Failed to decode index section map header");
            }
        };
        dbg!(index_map_len);

        let mut requests = vec![];
        for _ in 0..index_map_len {
            let uri = self.de.text()?.parse::<Uri>()?;
            dbg!(&uri);
            ensure!(
                self.read_array_len()? == 2,
                "bundle: Failed to decode index item"
            );
            let offset = self.de.unsigned_integer()?;
            dbg!(offset);
            let length = self.de.unsigned_integer()?;
            dbg!(length);
            requests.push(RequestEntry {
                request: Request::get(uri).body(())?,
                response_location: ResponseLocation::new(responses_section_offset, offset, length),
            });
        }
        Ok(requests)
    }

    fn read_responses(&mut self, requests: Vec<RequestEntry>) -> Result<Vec<Exchange>> {
        requests
            .into_iter()
            .map(
                |RequestEntry {
                     request,
                     response_location: ResponseLocation { offset, length },
                 }| {
                    let response = self
                        .new_decoder_from_range(offset, offset + length)
                        .read_response()?;
                    Ok(Exchange { request, response })
                },
            )
            .collect()
    }

    fn read_response(&mut self) -> Result<Response> {
        let responses_array_len = self
            .read_array_len()
            .context("bundle: Failed to decode responses section array headder")?;
        ensure!(
            responses_array_len == 2,
            "bundle: Failed to decode response entry"
        );
        log::debug!("read_response: headers byte 1");
        let headers = self.de.bytes()?;
        log::debug!("read_response: headers byte 2");
        let mut nested = Decoder::new(headers);
        let (status, headers) = nested.read_headers_cbor()?;
        let body = self.de.bytes()?;
        let mut response = Response::new(body);
        *response.status_mut() = status;
        *response.headers_mut() = headers;
        Ok(response)
    }

    fn read_headers_cbor(&mut self) -> Result<(StatusCode, HeaderMap)> {
        let headers_map_len = match self.de.map()? {
            Len::Len(n) => n,
            Len::Indefinite => {
                bail!("bundle: Failed to decode responses headers map headder");
            }
        };
        let mut headers = HeaderMap::new();
        let mut status = None;
        for _ in 0..headers_map_len {
            let name = String::from_utf8(self.de.bytes()?)?;
            let value = String::from_utf8(self.de.bytes()?)?;
            if name.starts_with(':') {
                ensure!(name == ":status", "Unknown pseudo headers");
                ensure!(status.is_none(), ":status is duplicated");
                status = Some(value.parse()?);
                continue;
            }
            headers.insert(
                HeaderName::from_lowercase(name.as_bytes())?,
                HeaderValue::from_str(value.as_str())?,
            );
        }
        ensure!(status.is_some(), "no :status header");
        Ok((status.unwrap(), headers))
    }
}

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

    /// This test uses an external tool, `gen-bundle`.
    /// See https://github.com/WICG/webpackage/go/bundle
    #[ignore]
    #[tokio::test]
    async fn decode_bundle_encoded_by_go_gen_bundle() -> Result<()> {
        use std::io::Read;

        let base_dir = {
            let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
            path.push("tests/builder");
            path
        };

        let mut file = tempfile::NamedTempFile::new()?;

        // Create a bundle by `gen-bundle`.
        let res = std::process::Command::new("gen-bundle")
            .arg("--version")
            .arg("b2")
            .arg("-dir")
            .arg(base_dir)
            .arg("-baseURL")
            .arg("https://example.com/")
            .arg("-o")
            .arg(file.path())
            .output()?;
        assert!(res.status.success());

        // Parse the created bundle.
        let mut bytes = Vec::new();
        file.read_to_end(&mut bytes)?;
        let bundle = Bundle::from_bytes(bytes)?;

        assert_eq!(bundle.version, Version::VersionB2);
        assert_eq!(bundle.exchanges.len(), 3);
        assert_eq!(bundle.exchanges[0].request.uri(), "https://example.com/");
        assert_eq!(
            bundle.exchanges[1].request.uri(),
            "https://example.com/index.html"
        );
        assert_eq!(
            bundle.exchanges[2].request.uri(),
            "https://example.com/js/hello.js"
        );

        Ok(())
    }
}
<commit_msg>Fix an unused field warning<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::bundle::{self, Bundle, Exchange, Request, Response, Uri, Version};
use crate::prelude::*;
use cbor_event::Len;
use http::{
    header::{HeaderMap, HeaderName, HeaderValue},
    StatusCode,
};
use std::collections::HashSet;
use std::convert::TryInto;
use std::io::Cursor;

pub(crate) fn parse(bytes: impl AsRef<[u8]>) -> Result<Bundle> {
    Decoder::new(bytes).decode()
}

#[derive(Debug)]
struct SectionOffset {
    name: String,
    offset: u64,
    length: u64,
}

#[derive(Debug)]
struct ResponseLocation {
    offset: u64,
    length: u64,
}

impl ResponseLocation {
    pub fn new(responses_section_offset: u64, offset: u64, length: u64) -> ResponseLocation {
        ResponseLocation {
            offset: responses_section_offset + offset,
            length,
        }
    }
}

#[derive(Debug)]
struct RequestEntry {
    request: Request,
    response_location: ResponseLocation,
}

#[derive(Debug)]
struct Metadata {
    version: Version,
    section_offsets: Vec<SectionOffset>,
}

type Deserializer<R> = cbor_event::de::Deserializer<R>;

struct Decoder<T> {
    de: Deserializer<Cursor<T>>,
}

impl<T> Decoder<T> {
    fn new(buf: T) -> Self {
        Decoder {
            de: Deserializer::from(Cursor::new(buf)),
        }
    }
}

type PrimaryUrl = Uri;

impl<T: AsRef<[u8]>> Decoder<T> {
    fn decode(&mut self) -> Result<Bundle> {
        let metadata = self.read_metadata()?;
        log::debug!("metadata {:?}", metadata);

        let (requests, primary_url) = self.read_sections(&metadata.section_offsets)?;
        let exchanges = self.read_responses(requests)?;

        Ok(Bundle {
            version: metadata.version,
            primary_url,
            exchanges,
        })
    }

    fn read_metadata(&mut self) -> Result<Metadata> {
        ensure!(
            self.read_array_len()? as usize == bundle::TOP_ARRAY_LEN,
            "Invalid header"
        );
        self.read_magic_bytes()?;
        let version = self.read_version()?;
        let section_offsets = self.read_section_offsets()?;
        Ok(Metadata {
            version,
            section_offsets,
        })
    }

    fn read_magic_bytes(&mut self) -> Result<()> {
        log::debug!("read_magic_bytes");
        let magic: Vec<u8> = self.de.bytes().context("Invalid magic bytes")?;
        ensure!(magic == bundle::HEADER_MAGIC_BYTES, "Header magic mismatch");
        Ok(())
    }

    fn read_version(&mut self) -> Result<Version> {
        log::debug!("read_version");
        let bytes: Vec<u8> = self.de.bytes().context("Invalid version format")?;
        ensure!(
            bytes.len() == bundle::VERSION_BYTES_LEN,
            "Invalid version format"
        );
        let version: [u8; bundle::VERSION_BYTES_LEN] =
            AsRef::<[u8]>::as_ref(&bytes).try_into().unwrap();
        Ok(if &version == bundle::Version::Version1.bytes() {
            Version::Version1
        } else if &version == bundle::Version::VersionB2.bytes() {
            Version::VersionB2
        } else {
            Version::Unknown(version)
        })
    }

    fn read_section_offsets(&mut self) -> Result<Vec<SectionOffset>> {
        let bytes = self
            .de
            .bytes()
            .context("Failed to read sectionLength byte string")?;
        ensure!(
            bytes.len() < 8_192,
            format!("sectionLengthsLength is too long ({} bytes)", bytes.len())
        );
        Decoder::new(bytes).read_section_offsets_cbor(self.position())
    }

    fn read_array_len(&mut self) -> Result<u64> {
        match self.de.array()? {
            Len::Len(n) => Ok(n),
            _ => bail!("bundle: bundle: Failed to decode sectionOffset array header"),
        }
    }

    fn position(&self) -> u64 {
        self.de.as_ref().position()
    }

    fn read_section_offsets_cbor(&mut self, mut offset: u64) -> Result<Vec<SectionOffset>> {
        let n = self
            .read_array_len()
            .context("bundle: bundle: Failed to decode sectionOffset array header")?;
        let section_num = n / 2;
        offset += self.position();
        let mut seen_names = HashSet::new();
        let mut section_offsets = Vec::with_capacity(section_num as usize);
        for _ in 0..section_num {
            let name = self.de.text()?;
            ensure!(!seen_names.contains(&name), "Duplicate section name");
            seen_names.insert(name.clone());
            let length = self.de.unsigned_integer()?;
            section_offsets.push(SectionOffset {
                name,
                offset,
                length,
            });
            offset += length;
        }
        ensure!(!section_offsets.is_empty(), "bundle: section is empty");
        ensure!(
            section_offsets.last().unwrap().name == "responses",
            "bundle: Last section is not \"responses\""
        );
        Ok(section_offsets)
    }

    fn inner_buf(&self) -> &[u8] {
        self.de.as_ref().get_ref().as_ref()
    }

    fn new_decoder_from_range(&self, start: u64, end: u64) -> Decoder<&[u8]> {
        // TODO: Check range, instead of panic
        Decoder::new(&self.inner_buf()[start as usize..end as usize])
    }

    fn read_sections(
        &mut self,
        section_offsets: &[SectionOffset],
    ) -> Result<(Vec<RequestEntry>, Option<PrimaryUrl>)> {
        log::debug!("read_sections");
        let n = self
            .read_array_len()
            .context("Failed to read section header")?;
        log::debug!("n: {:?}", n);
        ensure!(
            n as usize == section_offsets.len(),
            format!(
                "bundle: Expected {} sections, got {} sections",
                section_offsets.len(),
                n
            )
        );

        let responses_section_offset = section_offsets.last().unwrap().offset;

        dbg!(responses_section_offset);

        let mut requests = vec![];
        let mut primary_url: Option<PrimaryUrl> = None;

        for SectionOffset {
            name,
            offset,
            length,
        } in section_offsets
        {
            if !bundle::KNOWN_SECTION_NAMES.iter().any(|&n| n == name) {
                log::warn!("Unknows section name: {}. Skipping", name);
                continue;
            }
            let mut section_decoder = self.new_decoder_from_range(*offset, offset + length);

            // TODO: Support ignoredSections
            match name.as_ref() {
                "index" => {
                    dbg!(name);
                    requests = section_decoder.read_index(responses_section_offset)?;
                }
                "responses" => {
                    dbg!(name);
                    // Skip responses section becuase we read responses later.
                }
                "primary-url" => {
                    dbg!(name);
                    primary_url = Some(section_decoder.read_primary_url()?);
                }
                _ => {
                    log::warn!("Unknown section found: {}", name);
                }
            }
        }
        Ok((requests, primary_url))
    }

    fn read_primary_url(&mut self) -> Result<PrimaryUrl> {
        log::debug!("read_primary_url");
        self.de
            .text()
            .context("bundle: Failed to read primary_url string")?
            .parse()
            .context("Failed to parse primary_url")
    }

    fn read_index(&mut self, responses_section_offset: u64) -> Result<Vec<RequestEntry>> {
        let index_map_len = match self.de.map()? {
            Len::Len(n) => n,
            Len::Indefinite => {
                bail!("bundle: Failed to decode index section map header");
            }
        };
        dbg!(index_map_len);

        let mut requests = vec![];
        for _ in 0..index_map_len {
            let uri = self.de.text()?.parse::<Uri>()?;
            dbg!(&uri);
            ensure!(
                self.read_array_len()? == 2,
                "bundle: Failed to decode index item"
            );
            let offset = self.de.unsigned_integer()?;
            dbg!(offset);
            let length = self.de.unsigned_integer()?;
            dbg!(length);
            requests.push(RequestEntry {
                request: Request::get(uri).body(())?,
                response_location: ResponseLocation::new(responses_section_offset, offset, length),
            });
        }
        Ok(requests)
    }

    fn read_responses(&mut self, requests: Vec<RequestEntry>) -> Result<Vec<Exchange>> {
        requests
            .into_iter()
            .map(
                |RequestEntry {
                     request,
                     response_location: ResponseLocation { offset, length },
                 }| {
                    let response = self
                        .new_decoder_from_range(offset, offset + length)
                        .read_response()?;
                    Ok(Exchange { request, response })
                },
            )
            .collect()
    }

    fn read_response(&mut self) -> Result<Response> {
        let responses_array_len = self
            .read_array_len()
            .context("bundle: Failed to decode responses section array headder")?;
        ensure!(
            responses_array_len == 2,
            "bundle: Failed to decode response entry"
        );
        log::debug!("read_response: headers byte 1");
        let headers = self.de.bytes()?;
        log::debug!("read_response: headers byte 2");
        let mut nested = Decoder::new(headers);
        let (status, headers) = nested.read_headers_cbor()?;
        let body = self.de.bytes()?;
        let mut response = Response::new(body);
        *response.status_mut() = status;
        *response.headers_mut() = headers;
        Ok(response)
    }

    fn read_headers_cbor(&mut self) -> Result<(StatusCode, HeaderMap)> {
        let headers_map_len = match self.de.map()? {
            Len::Len(n) => n,
            Len::Indefinite => {
                bail!("bundle: Failed to decode responses headers map headder");
            }
        };
        let mut headers = HeaderMap::new();
        let mut status = None;
        for _ in 0..headers_map_len {
            let name = String::from_utf8(self.de.bytes()?)?;
            let value = String::from_utf8(self.de.bytes()?)?;
            if name.starts_with(':') {
                ensure!(name == ":status", "Unknown pseudo headers");
                ensure!(status.is_none(), ":status is duplicated");
                status = Some(value.parse()?);
                continue;
            }
            headers.insert(
                HeaderName::from_lowercase(name.as_bytes())?,
                HeaderValue::from_str(value.as_str())?,
            );
        }
        ensure!(status.is_some(), "no :status header");
        Ok((status.unwrap(), headers))
    }
}

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

    /// This test uses an external tool, `gen-bundle`.
    /// See https://github.com/WICG/webpackage/go/bundle
    #[ignore]
    #[tokio::test]
    async fn decode_bundle_encoded_by_go_gen_bundle() -> Result<()> {
        use std::io::Read;

        let base_dir = {
            let mut path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
            path.push("tests/builder");
            path
        };

        let mut file = tempfile::NamedTempFile::new()?;

        // Create a bundle by `gen-bundle`.
        let res = std::process::Command::new("gen-bundle")
            .arg("--version")
            .arg("b2")
            .arg("-dir")
            .arg(base_dir)
            .arg("-baseURL")
            .arg("https://example.com/")
            .arg("-o")
            .arg(file.path())
            .output()?;
        assert!(res.status.success());

        // Parse the created bundle.
        let mut bytes = Vec::new();
        file.read_to_end(&mut bytes)?;
        let bundle = Bundle::from_bytes(bytes)?;

        assert_eq!(bundle.version, Version::VersionB2);
        assert_eq!(bundle.exchanges.len(), 3);
        assert_eq!(bundle.exchanges[0].request.uri(), "https://example.com/");
        assert_eq!(
            bundle.exchanges[1].request.uri(),
            "https://example.com/index.html"
        );
        assert_eq!(
            bundle.exchanges[2].request.uri(),
            "https://example.com/js/hello.js"
        );

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Decoding and Encoding of TIFF Images
//!
//! TIFF (Tagged Image File Format) is a versatile image format that supports
//! lossless and lossy compression.
//!
//! # Related Links
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification

extern crate tiff;

use std::convert::TryFrom;
use std::io::{self, Cursor, Read, Write, Seek};
use std::marker::PhantomData;
use std::mem;

use crate::color::{ColorType, ExtendedColorType};
use crate::error::{
    DecodingError, EncodingError, ImageError, ImageResult, LimitError, LimitErrorKind,
    ParameterError, ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
};
use crate::image::{ImageDecoder, ImageEncoder, ImageFormat};
use crate::utils;

/// Decoder for TIFF images.
pub struct TiffDecoder<R>
    where R: Read + Seek
{
    dimensions: (u32, u32),
    color_type: ColorType,
    inner: tiff::decoder::Decoder<R>,
}

impl<R> TiffDecoder<R>
    where R: Read + Seek
{
    /// Create a new TiffDecoder.
    pub fn new(r: R) -> Result<TiffDecoder<R>, ImageError> {
        let mut inner = tiff::decoder::Decoder::new(r).map_err(ImageError::from_tiff_decode)?;
        let dimensions = inner.dimensions().map_err(ImageError::from_tiff_decode)?;
        let color_type = match inner.colortype().map_err(ImageError::from_tiff_decode)? {
            tiff::ColorType::Gray(8) => ColorType::L8,
            tiff::ColorType::Gray(16) => ColorType::L16,
            tiff::ColorType::GrayA(8) => ColorType::La8,
            tiff::ColorType::GrayA(16) => ColorType::La16,
            tiff::ColorType::RGB(8) => ColorType::Rgb8,
            tiff::ColorType::RGB(16) => ColorType::Rgb16,
            tiff::ColorType::RGBA(8) => ColorType::Rgba8,
            tiff::ColorType::RGBA(16) => ColorType::Rgba16,

            tiff::ColorType::Palette(n) | tiff::ColorType::Gray(n) => {
                return Err(err_unknown_color_type(n))
            }
            tiff::ColorType::GrayA(n) => return Err(err_unknown_color_type(n * 2)),
            tiff::ColorType::RGB(n) => return Err(err_unknown_color_type(n * 3)),
            tiff::ColorType::RGBA(n) | tiff::ColorType::CMYK(n) => {
                return Err(err_unknown_color_type(n * 4))
            }
        };

        Ok(TiffDecoder {
            dimensions,
            color_type,
            inner,
        })
    }
}

fn err_unknown_color_type(value: u8) -> ImageError {
    ImageError::Unsupported(UnsupportedError::from_format_and_kind(
        ImageFormat::Tiff.into(),
        UnsupportedErrorKind::Color(ExtendedColorType::Unknown(value)),
    ))
}

impl ImageError {
    fn from_tiff_decode(err: tiff::TiffError) -> ImageError {
        match err {
            tiff::TiffError::IoError(err) => ImageError::IoError(err),
            err @ tiff::TiffError::FormatError(_) | err @ tiff::TiffError::IntSizeError => {
                ImageError::Decoding(DecodingError::new(ImageFormat::Tiff.into(), err))
            }
            tiff::TiffError::UnsupportedError(desc) => {
                ImageError::Unsupported(UnsupportedError::from_format_and_kind(
                    ImageFormat::Tiff.into(),
                    UnsupportedErrorKind::GenericFeature(desc.to_string()),
                ))
            }
            tiff::TiffError::LimitsExceeded => {
                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
            }
        }
    }

    fn from_tiff_encode(err: tiff::TiffError) -> ImageError {
        match err {
            tiff::TiffError::IoError(err) => ImageError::IoError(err),
            err @ tiff::TiffError::FormatError(_) | err @ tiff::TiffError::IntSizeError => {
                ImageError::Encoding(EncodingError::new(ImageFormat::Tiff.into(), err))
            }
            tiff::TiffError::UnsupportedError(desc) => {
                ImageError::Unsupported(UnsupportedError::from_format_and_kind(
                    ImageFormat::Tiff.into(),
                    UnsupportedErrorKind::GenericFeature(desc.to_string()),
                ))
            }
            tiff::TiffError::LimitsExceeded => {
                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
            }
        }
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct TiffReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for TiffReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for TiffDecoder<R> {
    type Reader = TiffReader<R>;

    fn dimensions(&self) -> (u32, u32) {
        self.dimensions
    }

    fn color_type(&self) -> ColorType {
        self.color_type
    }

    fn into_reader(mut self) -> ImageResult<Self::Reader> {
        let buf = match self
            .inner
            .read_image()
            .map_err(ImageError::from_tiff_decode)?
        {
            tiff::decoder::DecodingResult::U8(v) => v,
            tiff::decoder::DecodingResult::U16(v) => utils::vec_u16_into_u8(v),
            tiff::decoder::DecodingResult::U32(v) => utils::vec_u32_into_u8(v),
            tiff::decoder::DecodingResult::U64(v) => utils::vec_u64_into_u8(v),
            tiff::decoder::DecodingResult::F32(v) => bytemuck::cast_slice::<f32, u8>(v.as_slice()).to_owned(),
            tiff::decoder::DecodingResult::F64(v) => bytemuck::cast_slice::<f64, u8>(v.as_slice()).to_owned(),
        };

        Ok(TiffReader(Cursor::new(buf), PhantomData))
    }

    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
        match self
            .inner
            .read_image()
            .map_err(ImageError::from_tiff_decode)?
        {
            tiff::decoder::DecodingResult::U8(v) => {
                buf.copy_from_slice(&v);
            }
            tiff::decoder::DecodingResult::U16(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::U32(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::U64(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::F32(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::F64(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
        }
        Ok(())
    }
}

/// Encoder for tiff images
pub struct TiffEncoder<W> {
    w: W,
}

// Utility to simplify and deduplicate error handling during 16-bit encoding.
fn u8_slice_as_u16(buf: &[u8]) -> ImageResult<&[u16]> {
    bytemuck::try_cast_slice(buf).map_err(|err| {
        // If the buffer is not aligned or the correct length for a u16 slice, err.
        //
        // `bytemuck::PodCastError` of bytemuck-1.2.0 does not implement
        // `Error` and `Display` trait.
        // See <https://github.com/Lokathor/bytemuck/issues/22>.
        ImageError::Parameter(ParameterError::from_kind(ParameterErrorKind::Generic(
            format!("{:?}", err),
        )))
    })
}

impl<W: Write + Seek> TiffEncoder<W> {
    /// Create a new encoder that writes its output to `w`
    pub fn new(w: W) -> TiffEncoder<W> {
        TiffEncoder { w }
    }

    /// Encodes the image `image` that has dimensions `width` and `height` and `ColorType` `c`.
    ///
    /// 16-bit types assume the buffer is native endian.
    pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
        let mut encoder =
            tiff::encoder::TiffEncoder::new(self.w).map_err(ImageError::from_tiff_encode)?;
        match color {
            ColorType::L8 => encoder.write_image::<tiff::encoder::colortype::Gray8>(width, height, data),
            ColorType::Rgb8 => encoder.write_image::<tiff::encoder::colortype::RGB8>(width, height, data),
            ColorType::Rgba8 => encoder.write_image::<tiff::encoder::colortype::RGBA8>(width, height, data),
            ColorType::L16 => encoder.write_image::<tiff::encoder::colortype::Gray16>(width, height, &u8_slice_as_u16(data)?),
            ColorType::Rgb16 => encoder.write_image::<tiff::encoder::colortype::RGB16>(width, height, &u8_slice_as_u16(data)?),
            ColorType::Rgba16 => encoder.write_image::<tiff::encoder::colortype::RGBA16>(width, height, &u8_slice_as_u16(data)?),
            _ => {
                return Err(ImageError::Unsupported(
                    UnsupportedError::from_format_and_kind(
                        ImageFormat::Tiff.into(),
                        UnsupportedErrorKind::Color(color.into()),
                    ),
                ))
            }
        }
        .map_err(ImageError::from_tiff_encode)?;

        Ok(())
    }
}

impl<W: Write + Seek> ImageEncoder for TiffEncoder<W> {
    fn write_image(
        self,
        buf: &[u8],
        width: u32,
        height: u32,
        color_type: ColorType,
    ) -> ImageResult<()> {
        self.encode(buf, width, height, color_type)
    }
}
<commit_msg>Explicitly check for support format<commit_after>//! Decoding and Encoding of TIFF Images
//!
//! TIFF (Tagged Image File Format) is a versatile image format that supports
//! lossless and lossy compression.
//!
//! # Related Links
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification

extern crate tiff;

use std::convert::TryFrom;
use std::io::{self, Cursor, Read, Write, Seek};
use std::marker::PhantomData;
use std::mem;

use crate::color::{ColorType, ExtendedColorType};
use crate::error::{
    DecodingError, EncodingError, ImageError, ImageResult, LimitError, LimitErrorKind,
    ParameterError, ParameterErrorKind, UnsupportedError, UnsupportedErrorKind,
};
use crate::image::{ImageDecoder, ImageEncoder, ImageFormat};
use crate::utils;

/// Decoder for TIFF images.
pub struct TiffDecoder<R>
    where R: Read + Seek
{
    dimensions: (u32, u32),
    color_type: ColorType,
    inner: tiff::decoder::Decoder<R>,
}

impl<R> TiffDecoder<R>
    where R: Read + Seek
{
    /// Create a new TiffDecoder.
    pub fn new(r: R) -> Result<TiffDecoder<R>, ImageError> {
        let mut inner = tiff::decoder::Decoder::new(r).map_err(ImageError::from_tiff_decode)?;

        let dimensions = inner.dimensions()
            .map_err(ImageError::from_tiff_decode)?;
        let color_type = inner.colortype()
            .map_err(ImageError::from_tiff_decode)?;
        match inner.find_tag_unsigned_vec::<u16>(tiff::tags::Tag::SampleFormat) {
            Ok(Some(sample_formats)) => {
                for format in sample_formats {
                    check_sample_format(format)?;
                }
            }
            Ok(None) => { /* assume UInt format */ },
            Err(other) => return Err(ImageError::from_tiff_decode(other)),
        };

        let color_type = match color_type {
            tiff::ColorType::Gray(8) => ColorType::L8,
            tiff::ColorType::Gray(16) => ColorType::L16,
            tiff::ColorType::GrayA(8) => ColorType::La8,
            tiff::ColorType::GrayA(16) => ColorType::La16,
            tiff::ColorType::RGB(8) => ColorType::Rgb8,
            tiff::ColorType::RGB(16) => ColorType::Rgb16,
            tiff::ColorType::RGBA(8) => ColorType::Rgba8,
            tiff::ColorType::RGBA(16) => ColorType::Rgba16,

            tiff::ColorType::Palette(n) | tiff::ColorType::Gray(n) => {
                return Err(err_unknown_color_type(n))
            }
            tiff::ColorType::GrayA(n) => return Err(err_unknown_color_type(n * 2)),
            tiff::ColorType::RGB(n) => return Err(err_unknown_color_type(n * 3)),
            tiff::ColorType::RGBA(n) | tiff::ColorType::CMYK(n) => {
                return Err(err_unknown_color_type(n * 4))
            }
        };

        Ok(TiffDecoder {
            dimensions,
            color_type,
            inner,
        })
    }
}

fn check_sample_format(sample_format: u16) -> Result<(), ImageError> {
    match tiff::tags::SampleFormat::from_u16(sample_format) {
        Some(tiff::tags::SampleFormat::Uint) => Ok({}),
        Some(other) => {
            Err(ImageError::Unsupported(UnsupportedError::from_format_and_kind(
                ImageFormat::Tiff.into(),
                UnsupportedErrorKind::GenericFeature(
                    format!("Unhandled TIFF sample format {:?}", other)
                ),
            )))
        }
        None => {
            Err(ImageError::Decoding(
                    DecodingError::from_format_hint(ImageFormat::Tiff.into())
            ))
        }
    }
}

fn err_unknown_color_type(value: u8) -> ImageError {
    ImageError::Unsupported(UnsupportedError::from_format_and_kind(
        ImageFormat::Tiff.into(),
        UnsupportedErrorKind::Color(ExtendedColorType::Unknown(value)),
    ))
}

impl ImageError {
    fn from_tiff_decode(err: tiff::TiffError) -> ImageError {
        match err {
            tiff::TiffError::IoError(err) => ImageError::IoError(err),
            err @ tiff::TiffError::FormatError(_) | err @ tiff::TiffError::IntSizeError => {
                ImageError::Decoding(DecodingError::new(ImageFormat::Tiff.into(), err))
            }
            tiff::TiffError::UnsupportedError(desc) => {
                ImageError::Unsupported(UnsupportedError::from_format_and_kind(
                    ImageFormat::Tiff.into(),
                    UnsupportedErrorKind::GenericFeature(desc.to_string()),
                ))
            }
            tiff::TiffError::LimitsExceeded => {
                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
            }
        }
    }

    fn from_tiff_encode(err: tiff::TiffError) -> ImageError {
        match err {
            tiff::TiffError::IoError(err) => ImageError::IoError(err),
            err @ tiff::TiffError::FormatError(_) | err @ tiff::TiffError::IntSizeError => {
                ImageError::Encoding(EncodingError::new(ImageFormat::Tiff.into(), err))
            }
            tiff::TiffError::UnsupportedError(desc) => {
                ImageError::Unsupported(UnsupportedError::from_format_and_kind(
                    ImageFormat::Tiff.into(),
                    UnsupportedErrorKind::GenericFeature(desc.to_string()),
                ))
            }
            tiff::TiffError::LimitsExceeded => {
                ImageError::Limits(LimitError::from_kind(LimitErrorKind::InsufficientMemory))
            }
        }
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct TiffReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for TiffReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for TiffDecoder<R> {
    type Reader = TiffReader<R>;

    fn dimensions(&self) -> (u32, u32) {
        self.dimensions
    }

    fn color_type(&self) -> ColorType {
        self.color_type
    }

    fn into_reader(mut self) -> ImageResult<Self::Reader> {
        let buf = match self
            .inner
            .read_image()
            .map_err(ImageError::from_tiff_decode)?
        {
            tiff::decoder::DecodingResult::U8(v) => v,
            tiff::decoder::DecodingResult::U16(v) => utils::vec_u16_into_u8(v),
            tiff::decoder::DecodingResult::U32(v) => utils::vec_u32_into_u8(v),
            tiff::decoder::DecodingResult::U64(v) => utils::vec_u64_into_u8(v),
            tiff::decoder::DecodingResult::F32(v) => bytemuck::cast_slice::<f32, u8>(v.as_slice()).to_owned(),
            tiff::decoder::DecodingResult::F64(v) => bytemuck::cast_slice::<f64, u8>(v.as_slice()).to_owned(),
        };

        Ok(TiffReader(Cursor::new(buf), PhantomData))
    }

    fn read_image(mut self, buf: &mut [u8]) -> ImageResult<()> {
        assert_eq!(u64::try_from(buf.len()), Ok(self.total_bytes()));
        match self
            .inner
            .read_image()
            .map_err(ImageError::from_tiff_decode)?
        {
            tiff::decoder::DecodingResult::U8(v) => {
                buf.copy_from_slice(&v);
            }
            tiff::decoder::DecodingResult::U16(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::U32(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::U64(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::F32(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
            tiff::decoder::DecodingResult::F64(v) => {
                buf.copy_from_slice(bytemuck::cast_slice(&v));
            }
        }
        Ok(())
    }
}

/// Encoder for tiff images
pub struct TiffEncoder<W> {
    w: W,
}

// Utility to simplify and deduplicate error handling during 16-bit encoding.
fn u8_slice_as_u16(buf: &[u8]) -> ImageResult<&[u16]> {
    bytemuck::try_cast_slice(buf).map_err(|err| {
        // If the buffer is not aligned or the correct length for a u16 slice, err.
        //
        // `bytemuck::PodCastError` of bytemuck-1.2.0 does not implement
        // `Error` and `Display` trait.
        // See <https://github.com/Lokathor/bytemuck/issues/22>.
        ImageError::Parameter(ParameterError::from_kind(ParameterErrorKind::Generic(
            format!("{:?}", err),
        )))
    })
}

impl<W: Write + Seek> TiffEncoder<W> {
    /// Create a new encoder that writes its output to `w`
    pub fn new(w: W) -> TiffEncoder<W> {
        TiffEncoder { w }
    }

    /// Encodes the image `image` that has dimensions `width` and `height` and `ColorType` `c`.
    ///
    /// 16-bit types assume the buffer is native endian.
    pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
        let mut encoder =
            tiff::encoder::TiffEncoder::new(self.w).map_err(ImageError::from_tiff_encode)?;
        match color {
            ColorType::L8 => encoder.write_image::<tiff::encoder::colortype::Gray8>(width, height, data),
            ColorType::Rgb8 => encoder.write_image::<tiff::encoder::colortype::RGB8>(width, height, data),
            ColorType::Rgba8 => encoder.write_image::<tiff::encoder::colortype::RGBA8>(width, height, data),
            ColorType::L16 => encoder.write_image::<tiff::encoder::colortype::Gray16>(width, height, &u8_slice_as_u16(data)?),
            ColorType::Rgb16 => encoder.write_image::<tiff::encoder::colortype::RGB16>(width, height, &u8_slice_as_u16(data)?),
            ColorType::Rgba16 => encoder.write_image::<tiff::encoder::colortype::RGBA16>(width, height, &u8_slice_as_u16(data)?),
            _ => {
                return Err(ImageError::Unsupported(
                    UnsupportedError::from_format_and_kind(
                        ImageFormat::Tiff.into(),
                        UnsupportedErrorKind::Color(color.into()),
                    ),
                ))
            }
        }
        .map_err(ImageError::from_tiff_encode)?;

        Ok(())
    }
}

impl<W: Write + Seek> ImageEncoder for TiffEncoder<W> {
    fn write_image(
        self,
        buf: &[u8],
        width: u32,
        height: u32,
        color_type: ColorType,
    ) -> ImageResult<()> {
        self.encode(buf, width, height, color_type)
    }
}
<|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/. */

use api::{LayoutPoint, LayoutSize, LayoutVector2D};
use std::f32::consts::FRAC_PI_2;

/// Number of steps to integrate arc length over.
const STEP_COUNT: usize = 20;

/// Represents an ellipse centred at a local space origin.
#[derive(Debug, Clone)]
pub struct Ellipse {
    pub radius: LayoutSize,
    pub total_arc_length: f32,
}

impl Ellipse {
    pub fn new(radius: LayoutSize) -> Ellipse {
        // Approximate the total length of the first quadrant of this ellipse.
        let total_arc_length = get_simpson_length(FRAC_PI_2, radius.width, radius.height);

        Ellipse {
            radius,
            total_arc_length,
        }
    }

    /// Binary search to estimate the angle of an ellipse
    /// for a given arc length. This only searches over the
    /// first quadrant of an ellipse.
    pub fn find_angle_for_arc_length(&self, arc_length: f32) -> f32 {
        // Clamp arc length to [0, pi].
        let arc_length = arc_length.max(0.0).min(self.total_arc_length);

        let epsilon = 0.01;
        let mut low = 0.0;
        let mut high = FRAC_PI_2;
        let mut theta = 0.0;

        while low <= high {
            theta = 0.5 * (low + high);
            let length = get_simpson_length(theta, self.radius.width, self.radius.height);

            if (length - arc_length).abs() < epsilon {
                break;
            } else if length < arc_length {
                low = theta;
            } else {
                high = theta;
            }
        }

        theta
    }

    /// Get a point and tangent on this ellipse from a given angle.
    /// This only works for the first quadrant of the ellipse.
    pub fn get_point_and_tangent(&self, theta: f32) -> (LayoutPoint, LayoutPoint) {
        let (sin_theta, cos_theta) = theta.sin_cos();
        let point = LayoutPoint::new(
            self.radius.width * cos_theta,
            self.radius.height * sin_theta,
        );
        let tangent = LayoutPoint::new(
            -self.radius.width * sin_theta,
            self.radius.height * cos_theta,
        );
        (point, tangent)
    }

    pub fn contains(&self, point: LayoutPoint) -> bool {
        self.signed_distance(point.to_vector()) <= 0.0
    }

    /// Find the signed distance from this ellipse given a point.
    /// Taken from http://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm
    fn signed_distance(&self, point: LayoutVector2D) -> f32 {
        // This algorithm fails for circles, so we handle them here.
        if self.radius.width == self.radius.height {
            return point.length() - self.radius.width;
        }

        let mut p = LayoutVector2D::new(point.x.abs(), point.y.abs());
        let mut ab = self.radius.to_vector();
        if p.x > p.y {
            p = p.yx();
            ab = ab.yx();
        }

        let l = ab.y * ab.y - ab.x * ab.x;

        let m = ab.x * p.x / l;
        let n = ab.y * p.y / l;
        let m2 = m * m;
        let n2 = n * n;

        let c = (m2 + n2 - 1.0) / 3.0;
        let c3 = c * c * c;

        let q = c3 + m2 * n2 * 2.0;
        let d = c3 + m2 * n2;
        let g = m + m * n2;

        let co = if d < 0.0 {
            let p = (q / c3).acos() / 3.0;
            let s = p.cos();
            let t = p.sin() * (3.0_f32).sqrt();
            let rx = (-c * (s + t + 2.0) + m2).sqrt();
            let ry = (-c * (s - t + 2.0) + m2).sqrt();
            (ry + l.signum() * rx + g.abs() / (rx * ry) - m) / 2.0
        } else {
            let h = 2.0 * m * n * d.sqrt();
            let s = (q + h).signum() * (q + h).abs().powf(1.0 / 3.0);
            let u = (q - h).signum() * (q - h).abs().powf(1.0 / 3.0);
            let rx = -s - u - c * 4.0 + 2.0 * m2;
            let ry = (s - u) * (3.0_f32).sqrt();
            let rm = (rx * rx + ry * ry).sqrt();
            let p = ry / (rm - rx).sqrt();
            (p + 2.0 * g / rm - m) / 2.0
        };

        let si = (1.0 - co * co).sqrt();
        let r = LayoutVector2D::new(ab.x * co, ab.y * si);
        (r - p).length() * (p.y - r.y).signum()
    }
}

/// Use Simpsons rule to approximate the arc length of
/// part of an ellipse. Note that this only works over
/// the range of [0, pi/2].
// TODO(gw): This is a simplistic way to estimate the
// arc length of an ellipse segment. We can probably use
// a faster / more accurate method!
fn get_simpson_length(theta: f32, rx: f32, ry: f32) -> f32 {
    let df = theta / STEP_COUNT as f32;
    let mut sum = 0.0;

    for i in 0 .. (STEP_COUNT + 1) {
        let (sin_theta, cos_theta) = (i as f32 * df).sin_cos();
        let a = rx * sin_theta;
        let b = ry * cos_theta;
        let y = (a * a + b * b).sqrt();
        let q = if i == 0 || i == STEP_COUNT {
            1.0
        } else if i % 2 == 0 {
            2.0
        } else {
            4.0
        };

        sum += q * y;
    }

    (df / 3.0) * sum
}
<commit_msg>Fix an infinite loop in ellipse arc length calculation<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/. */

use api::{LayoutPoint, LayoutSize, LayoutVector2D};
use std::f32::consts::FRAC_PI_2;

/// Number of steps to integrate arc length over.
const STEP_COUNT: usize = 20;

/// Represents an ellipse centred at a local space origin.
#[derive(Debug, Clone)]
pub struct Ellipse {
    pub radius: LayoutSize,
    pub total_arc_length: f32,
}

impl Ellipse {
    pub fn new(radius: LayoutSize) -> Ellipse {
        // Approximate the total length of the first quadrant of this ellipse.
        let total_arc_length = get_simpson_length(FRAC_PI_2, radius.width, radius.height);

        Ellipse {
            radius,
            total_arc_length,
        }
    }

    /// Binary search to estimate the angle of an ellipse
    /// for a given arc length. This only searches over the
    /// first quadrant of an ellipse.
    pub fn find_angle_for_arc_length(&self, arc_length: f32) -> f32 {
        // Clamp arc length to [0, pi].
        let arc_length = arc_length.max(0.0).min(self.total_arc_length);

        let epsilon = 0.01;
        let mut low = 0.0;
        let mut high = FRAC_PI_2;
        let mut theta = 0.0;
        let mut new_low = 0.0;
        let mut new_high = FRAC_PI_2;

        while low <= high {
            theta = 0.5 * (low + high);
            let length = get_simpson_length(theta, self.radius.width, self.radius.height);

            if (length - arc_length).abs() < epsilon {
                break;
            } else if length < arc_length {
                new_low = theta;
            } else {
                new_high = theta;
            }

            // If we have stopped moving down the arc, the answer that we have is as good as
            // it is going to get. We break to avoid going into an infinite loop.
            if new_low == low && new_high == high {
                break;
            }

            high = new_high;
            low = new_low;
        }

        theta
    }

    /// Get a point and tangent on this ellipse from a given angle.
    /// This only works for the first quadrant of the ellipse.
    pub fn get_point_and_tangent(&self, theta: f32) -> (LayoutPoint, LayoutPoint) {
        let (sin_theta, cos_theta) = theta.sin_cos();
        let point = LayoutPoint::new(
            self.radius.width * cos_theta,
            self.radius.height * sin_theta,
        );
        let tangent = LayoutPoint::new(
            -self.radius.width * sin_theta,
            self.radius.height * cos_theta,
        );
        (point, tangent)
    }

    pub fn contains(&self, point: LayoutPoint) -> bool {
        self.signed_distance(point.to_vector()) <= 0.0
    }

    /// Find the signed distance from this ellipse given a point.
    /// Taken from http://www.iquilezles.org/www/articles/ellipsedist/ellipsedist.htm
    fn signed_distance(&self, point: LayoutVector2D) -> f32 {
        // This algorithm fails for circles, so we handle them here.
        if self.radius.width == self.radius.height {
            return point.length() - self.radius.width;
        }

        let mut p = LayoutVector2D::new(point.x.abs(), point.y.abs());
        let mut ab = self.radius.to_vector();
        if p.x > p.y {
            p = p.yx();
            ab = ab.yx();
        }

        let l = ab.y * ab.y - ab.x * ab.x;

        let m = ab.x * p.x / l;
        let n = ab.y * p.y / l;
        let m2 = m * m;
        let n2 = n * n;

        let c = (m2 + n2 - 1.0) / 3.0;
        let c3 = c * c * c;

        let q = c3 + m2 * n2 * 2.0;
        let d = c3 + m2 * n2;
        let g = m + m * n2;

        let co = if d < 0.0 {
            let p = (q / c3).acos() / 3.0;
            let s = p.cos();
            let t = p.sin() * (3.0_f32).sqrt();
            let rx = (-c * (s + t + 2.0) + m2).sqrt();
            let ry = (-c * (s - t + 2.0) + m2).sqrt();
            (ry + l.signum() * rx + g.abs() / (rx * ry) - m) / 2.0
        } else {
            let h = 2.0 * m * n * d.sqrt();
            let s = (q + h).signum() * (q + h).abs().powf(1.0 / 3.0);
            let u = (q - h).signum() * (q - h).abs().powf(1.0 / 3.0);
            let rx = -s - u - c * 4.0 + 2.0 * m2;
            let ry = (s - u) * (3.0_f32).sqrt();
            let rm = (rx * rx + ry * ry).sqrt();
            let p = ry / (rm - rx).sqrt();
            (p + 2.0 * g / rm - m) / 2.0
        };

        let si = (1.0 - co * co).sqrt();
        let r = LayoutVector2D::new(ab.x * co, ab.y * si);
        (r - p).length() * (p.y - r.y).signum()
    }
}

/// Use Simpsons rule to approximate the arc length of
/// part of an ellipse. Note that this only works over
/// the range of [0, pi/2].
// TODO(gw): This is a simplistic way to estimate the
// arc length of an ellipse segment. We can probably use
// a faster / more accurate method!
fn get_simpson_length(theta: f32, rx: f32, ry: f32) -> f32 {
    let df = theta / STEP_COUNT as f32;
    let mut sum = 0.0;

    for i in 0 .. (STEP_COUNT + 1) {
        let (sin_theta, cos_theta) = (i as f32 * df).sin_cos();
        let a = rx * sin_theta;
        let b = ry * cos_theta;
        let y = (a * a + b * b).sqrt();
        let q = if i == 0 || i == STEP_COUNT {
            1.0
        } else if i % 2 == 0 {
            2.0
        } else {
            4.0
        };

        sum += q * y;
    }

    (df / 3.0) * sum
}

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

    #[test]
    fn find_angle_for_arc_length_for_long_eclipse() {
        // Ensure that finding the angle on giant ellipses produces and answer and
        // doesn't send us into an infinite loop.
        let ellipse = Ellipse::new(LayoutSize::new(57500.0, 25.0));
        let _ = ellipse.find_angle_for_arc_length(55674.53);
        assert!(true);

        let ellipse = Ellipse::new(LayoutSize::new(25.0, 57500.0));
        let _ = ellipse.find_angle_for_arc_length(55674.53);
        assert!(true);
    }
}
<|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/. */

#![crate_id = "github.com/mozilla/servo"]
#![comment = "The Servo Parallel Browser Project"]
#![license = "MPL"]

#![feature(globs, macro_rules, phase, thread_local)]

#![feature(phase)]
#[phase(syntax, link)]
extern crate log;

extern crate alert;
extern crate azure;
extern crate geom;
extern crate gfx;
#[cfg(not(target_os="android"))]
extern crate glfw;
#[cfg(target_os="android")]
extern crate glut;
extern crate js;
extern crate layers;
extern crate opengles;
extern crate png;
extern crate rustuv;
extern crate script;
#[phase(syntax)]
extern crate servo_macros = "macros";
extern crate servo_net = "net";
extern crate servo_msg = "msg";
extern crate servo_util = "util";
extern crate style;
extern crate sharegl;
extern crate stb_image;

extern crate collections;
extern crate green;
extern crate libc;
extern crate native;
extern crate serialize;
extern crate sync;
extern crate time;
extern crate url;

#[cfg(target_os="macos")]
extern crate core_graphics;
#[cfg(target_os="macos")]
extern crate core_text;

#[cfg(not(test))]
use compositing::{CompositorChan, CompositorTask};
#[cfg(not(test))]
use constellation::Constellation;
#[cfg(not(test))]
use servo_msg::constellation_msg::{ConstellationChan, InitLoadUrlMsg};

#[cfg(not(test))]
use servo_net::image_cache_task::{ImageCacheTask, SyncImageCacheTask};
#[cfg(not(test))]
use servo_net::resource_task::ResourceTask;
#[cfg(not(test))]
use servo_util::time::Profiler;

#[cfg(not(test))]
use servo_util::opts;
#[cfg(not(test))]
use servo_util::url::parse_url;

#[cfg(not(test), not(target_os="android"))]
use std::os;
#[cfg(not(test), target_os="android")]
use std::str;
#[cfg(not(test))]
use std::task::TaskOpts;
#[cfg(not(test))]
use url::Url;

#[path="compositing/compositor_task.rs"]
pub mod compositing;

pub mod css {
    mod node_util;

    pub mod select;
    pub mod matching;
    pub mod node_style;
}

pub mod constellation;
pub mod pipeline;

pub mod layout {
    pub mod block;
    pub mod box_;
    pub mod construct;
    pub mod context;
    pub mod floats;
    pub mod flow;
    pub mod flow_list;
    pub mod layout_task;
    pub mod inline;
    pub mod model;
    pub mod parallel;
    pub mod table_wrapper;
    pub mod table;
    pub mod table_caption;
    pub mod table_colgroup;
    pub mod table_rowgroup;
    pub mod table_row;
    pub mod table_cell;
    pub mod text;
    pub mod util;
    pub mod incremental;
    pub mod wrapper;
    pub mod extra;
}

pub mod windowing;

#[path="platform/mod.rs"]
pub mod platform;

#[cfg(not(test), target_os="linux")]
#[cfg(not(test), target_os="macos")]
#[start]
fn start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, proc() {
        opts::from_cmdline_args(os::args()).map(run);
    })
}

#[cfg(not(test), target_os="android")]
#[no_mangle]
pub extern "C" fn android_start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, proc() {
        let mut args: Vec<~str> = vec!();
        for i in range(0u, argc as uint) {
            unsafe {
                args.push(str::raw::from_c_str(*argv.offset(i as int) as *i8));
            }
        }

        let mut opts = opts::from_cmdline_args(args.as_slice());
        match opts {
            Some(mut o) => {
                // Always use CPU rendering on android.
                o.cpu_painting = true;
                run(o);
            },
            None => {}
        }
    })
}

#[cfg(not(test))]
fn run(opts: opts::Opts) {
    let mut pool_config = green::PoolConfig::new();
    pool_config.event_loop_factory = rustuv::event_loop;
    let mut pool = green::SchedPool::new(pool_config);

    let (compositor_port, compositor_chan) = CompositorChan::new();
    let profiler_chan = Profiler::create(opts.profiler_period);

    let opts_clone = opts.clone();
    let profiler_chan_clone = profiler_chan.clone();

    let (result_chan, result_port) = channel();
    pool.spawn(TaskOpts::new(), proc() {
        let opts = &opts_clone;
        // Create a Servo instance.
        let resource_task = ResourceTask();
        // If we are emitting an output file, then we need to block on
        // image load or we risk emitting an output file missing the
        // image.
        let image_cache_task = if opts.output_file.is_some() {
                SyncImageCacheTask(resource_task.clone())
            } else {
                ImageCacheTask(resource_task.clone())
            };
        let constellation_chan = Constellation::start(compositor_chan,
                                                      opts,
                                                      resource_task,
                                                      image_cache_task,
                                                      profiler_chan_clone);

        // Send the URL command to the constellation.
        for filename in opts.urls.iter() {
            let url = if filename.starts_with("data:") {
                // As a hack for easier command-line testing,
                // assume that data URLs are not URL-encoded.
                Url::new("data".to_owned(), None, "".to_owned(), None,
                    filename.slice_from(5).to_owned(), vec!(), None)
            } else {
                parse_url(*filename, None)
            };

            let ConstellationChan(ref chan) = constellation_chan;
            chan.send(InitLoadUrlMsg(url));
        }

        // Send the constallation Chan as the result
        result_chan.send(constellation_chan);
    });

    let constellation_chan = result_port.recv();

    debug!("preparing to enter main loop");
    CompositorTask::create(opts,
                           compositor_port,
                           constellation_chan,
                           profiler_chan);

    pool.shutdown();
}

<commit_msg>Suppress dead code warnings for start when building servo.rs as a dylib.<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/. */

#![crate_id = "github.com/mozilla/servo"]
#![comment = "The Servo Parallel Browser Project"]
#![license = "MPL"]

#![feature(globs, macro_rules, phase, thread_local)]

#![feature(phase)]
#[phase(syntax, link)]
extern crate log;

extern crate alert;
extern crate azure;
extern crate geom;
extern crate gfx;
#[cfg(not(target_os="android"))]
extern crate glfw;
#[cfg(target_os="android")]
extern crate glut;
extern crate js;
extern crate layers;
extern crate opengles;
extern crate png;
extern crate rustuv;
extern crate script;
#[phase(syntax)]
extern crate servo_macros = "macros";
extern crate servo_net = "net";
extern crate servo_msg = "msg";
extern crate servo_util = "util";
extern crate style;
extern crate sharegl;
extern crate stb_image;

extern crate collections;
extern crate green;
extern crate libc;
extern crate native;
extern crate serialize;
extern crate sync;
extern crate time;
extern crate url;

#[cfg(target_os="macos")]
extern crate core_graphics;
#[cfg(target_os="macos")]
extern crate core_text;

#[cfg(not(test))]
use compositing::{CompositorChan, CompositorTask};
#[cfg(not(test))]
use constellation::Constellation;
#[cfg(not(test))]
use servo_msg::constellation_msg::{ConstellationChan, InitLoadUrlMsg};

#[cfg(not(test))]
use servo_net::image_cache_task::{ImageCacheTask, SyncImageCacheTask};
#[cfg(not(test))]
use servo_net::resource_task::ResourceTask;
#[cfg(not(test))]
use servo_util::time::Profiler;

#[cfg(not(test))]
use servo_util::opts;
#[cfg(not(test))]
use servo_util::url::parse_url;

#[cfg(not(test), not(target_os="android"))]
use std::os;
#[cfg(not(test), target_os="android")]
use std::str;
#[cfg(not(test))]
use std::task::TaskOpts;
#[cfg(not(test))]
use url::Url;

#[path="compositing/compositor_task.rs"]
pub mod compositing;

pub mod css {
    mod node_util;

    pub mod select;
    pub mod matching;
    pub mod node_style;
}

pub mod constellation;
pub mod pipeline;

pub mod layout {
    pub mod block;
    pub mod box_;
    pub mod construct;
    pub mod context;
    pub mod floats;
    pub mod flow;
    pub mod flow_list;
    pub mod layout_task;
    pub mod inline;
    pub mod model;
    pub mod parallel;
    pub mod table_wrapper;
    pub mod table;
    pub mod table_caption;
    pub mod table_colgroup;
    pub mod table_rowgroup;
    pub mod table_row;
    pub mod table_cell;
    pub mod text;
    pub mod util;
    pub mod incremental;
    pub mod wrapper;
    pub mod extra;
}

pub mod windowing;

#[path="platform/mod.rs"]
pub mod platform;

#[cfg(not(test), target_os="linux")]
#[cfg(not(test), target_os="macos")]
#[start]
#[allow(dead_code)]
fn start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, proc() {
        opts::from_cmdline_args(os::args()).map(run);
    })
}

#[cfg(not(test), target_os="android")]
#[no_mangle]
#[allow(dead_code)]
pub extern "C" fn android_start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, proc() {
        let mut args: Vec<~str> = vec!();
        for i in range(0u, argc as uint) {
            unsafe {
                args.push(str::raw::from_c_str(*argv.offset(i as int) as *i8));
            }
        }

        let mut opts = opts::from_cmdline_args(args.as_slice());
        match opts {
            Some(mut o) => {
                // Always use CPU rendering on android.
                o.cpu_painting = true;
                run(o);
            },
            None => {}
        }
    })
}

#[cfg(not(test))]
fn run(opts: opts::Opts) {
    let mut pool_config = green::PoolConfig::new();
    pool_config.event_loop_factory = rustuv::event_loop;
    let mut pool = green::SchedPool::new(pool_config);

    let (compositor_port, compositor_chan) = CompositorChan::new();
    let profiler_chan = Profiler::create(opts.profiler_period);

    let opts_clone = opts.clone();
    let profiler_chan_clone = profiler_chan.clone();

    let (result_chan, result_port) = channel();
    pool.spawn(TaskOpts::new(), proc() {
        let opts = &opts_clone;
        // Create a Servo instance.
        let resource_task = ResourceTask();
        // If we are emitting an output file, then we need to block on
        // image load or we risk emitting an output file missing the
        // image.
        let image_cache_task = if opts.output_file.is_some() {
                SyncImageCacheTask(resource_task.clone())
            } else {
                ImageCacheTask(resource_task.clone())
            };
        let constellation_chan = Constellation::start(compositor_chan,
                                                      opts,
                                                      resource_task,
                                                      image_cache_task,
                                                      profiler_chan_clone);

        // Send the URL command to the constellation.
        for filename in opts.urls.iter() {
            let url = if filename.starts_with("data:") {
                // As a hack for easier command-line testing,
                // assume that data URLs are not URL-encoded.
                Url::new("data".to_owned(), None, "".to_owned(), None,
                    filename.slice_from(5).to_owned(), vec!(), None)
            } else {
                parse_url(*filename, None)
            };

            let ConstellationChan(ref chan) = constellation_chan;
            chan.send(InitLoadUrlMsg(url));
        }

        // Send the constallation Chan as the result
        result_chan.send(constellation_chan);
    });

    let constellation_chan = result_port.recv();

    debug!("preparing to enter main loop");
    CompositorTask::create(opts,
                           compositor_port,
                           constellation_chan,
                           profiler_chan);

    pool.shutdown();
}

<|endoftext|>


<commit_before>macro_rules! toml_template {
    ($name: expr) => {
        format_args!(
            r##"
[package]
name = "{0}-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2018"

[package.metadata]
cargo-fuzz = true

[dependencies.{0}]
path = ".."
[dependencies.libfuzzer-sys]
git = "https://github.com/rust-fuzz/libfuzzer-sys.git"

# Prevent this from interfering with workspaces
[workspace]
members = ["."]
"##,
            $name
        )
    };
}

macro_rules! toml_bin_template {
    ($name: expr) => {
        format_args!(
            r#"
[[bin]]
name = "{0}"
path = "fuzz_targets/{0}.rs"
"#,
            $name
        )
    };
}

macro_rules! gitignore_template {
    () => {
        format_args!(
            r##"
target
corpus
artifacts
"##
        )
    };
}

macro_rules! target_template {
    () => {
        format_args!(
            r##"#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {{
    // fuzzed code goes here
}});
"##
        )
    };
}
<commit_msg>Use the published version of libfuzzer-sys, not git<commit_after>macro_rules! toml_template {
    ($name: expr) => {
        format_args!(
            r##"
[package]
name = "{0}-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2018"

[package.metadata]
cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.2"

[dependencies.{0}]
path = ".."

# Prevent this from interfering with workspaces
[workspace]
members = ["."]
"##,
            $name
        )
    };
}

macro_rules! toml_bin_template {
    ($name: expr) => {
        format_args!(
            r#"
[[bin]]
name = "{0}"
path = "fuzz_targets/{0}.rs"
"#,
            $name
        )
    };
}

macro_rules! gitignore_template {
    () => {
        format_args!(
            r##"
target
corpus
artifacts
"##
        )
    };
}

macro_rules! target_template {
    () => {
        format_args!(
            r##"#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {{
    // fuzzed code goes here
}});
"##
        )
    };
}
<|endoftext|>