<commit_before>use std::fs::File;
use std::path::{Path, PathBuf};
use std::os::unix::fs::{symlink};

use quire::validate as V;
use quick_error::ResultExt;
use unshare::{Stdio};
use rustc_serialize::json::Json;

use capsule::download::download_file;
use super::super::context::{Context};
use super::super::packages;
use builder::distrib::{Distribution, DistroBox};
use builder::commands::generic::{command, run};
use builder::commands::tarcmd::unpack_subdir;
use build_step::{BuildStep, VersionError, StepError, Digest, Config, Guard};

#[derive(RustcDecodable, Debug, Clone)]
pub struct NpmConfig {
    pub install_node: bool,
    pub npm_exe: String,
}

impl NpmConfig {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("npm_exe", V::Scalar::new().default("npm"))
        .member("install_node", V::Scalar::new().default(true))
    }
}

#[derive(Debug)]
pub struct NpmInstall(Vec<String>);
tuple_struct_decode!(NpmInstall);

impl NpmInstall {
    pub fn config() -> V::Sequence<'static> {
        V::Sequence::new(V::Scalar::new())
    }
}

#[derive(RustcDecodable, Debug)]
pub struct NpmDependencies {
    pub file: PathBuf,
    pub package: bool,
    pub dev: bool,
    pub peer: bool,
    pub bundled: bool,
    pub optional: bool,
}

impl NpmDependencies {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("file", V::Scalar::new().default("package.json"))
        .member("package", V::Scalar::new().default(true))
        .member("dev", V::Scalar::new().default(true))
        .member("peer", V::Scalar::new().default(false))
        .member("bundled", V::Scalar::new().default(true))
        .member("optional", V::Scalar::new().default(false))
    }
}

impl Default for NpmConfig {
    fn default() -> NpmConfig {
        NpmConfig {
            install_node: true,
            npm_exe: "npm".to_string(),
        }
    }
}

#[derive(RustcDecodable, Debug)]
pub struct YarnDependencies {
    pub dir: PathBuf,
    pub production: bool,
}

impl YarnDependencies {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("dir", V::Directory::new().absolute(false).default("."))
        .member("production", V::Scalar::new().default(false))
    }
}

fn scan_features(settings: &NpmConfig, pkgs: &Vec<String>)
    -> Vec<packages::Package>
{
    let mut res = vec!();
    res.push(packages::BuildEssential);
    if settings.install_node {
        res.push(packages::NodeJs);
        res.push(packages::NodeJsDev);
        res.push(packages::Npm);
    }
    for name in pkgs.iter() {
        parse_feature(&name, &mut res);
    }
    return res;
}

pub fn parse_feature(info: &str, features: &mut Vec<packages::Package>) {
    // Note: the info is a package name/git-url in NpmInstall but it's just
    // a version number for NpmDependencies. That's how npm works.
    if info[..].starts_with("git://") {
        features.push(packages::Git);
    } // TODO(tailhook) implement whole a lot of other npm version kinds
}

pub fn npm_install(distro: &mut Box<Distribution>, ctx: &mut Context,
    pkgs: &Vec<String>)
    -> Result<(), StepError>
{
    ctx.add_cache_dir(Path::new("/tmp/npm-cache"),
                           "npm-cache".to_string())?;
    let features = scan_features(&ctx.npm_settings, pkgs);
    packages::ensure_packages(distro, ctx, &features)?;

    if pkgs.len() == 0 {
        return Ok(());
    }

    let mut cmd = command(ctx, &ctx.npm_settings.npm_exe)?;
    cmd.arg("install");
    cmd.arg("--global");
    cmd.arg("--user=root");
    cmd.arg("--cache=/tmp/npm-cache");
    cmd.args(pkgs);
    run(cmd)
}

fn scan_dic(json: &Json, key: &str,
    packages: &mut Vec<String>, features: &mut Vec<packages::Package>)
    -> Result<(), StepError>
{
    match json.find(key) {
        Some(&Json::Object(ref ob)) => {
            for (k, v) in ob {
                if !v.is_string() {
                    return Err(StepError::Compat(format!(
                        "Package {:?} has wrong version {:?}", k, v)));
                }
                let s = v.as_string().unwrap();
                parse_feature(&s, features);
                packages.push(format!("{}@{}", k, s));
                // TODO(tailhook) check the feature
            }
            Ok(())
        }
        None => {
            Ok(())
        }
        Some(_) => {
            Err(StepError::Compat(format!(
                "The {:?} is not a mapping (JSON object)", key)))
        }
    }
}

pub fn npm_deps(distro: &mut Box<Distribution>, ctx: &mut Context,
    info: &NpmDependencies)
    -> Result<(), StepError>
{
    ctx.add_cache_dir(Path::new("/tmp/npm-cache"),
                           "npm-cache".to_string())?;
    let mut features = scan_features(&ctx.npm_settings, &Vec::new());

    let json = File::open(&Path::new("/work").join(&info.file))
        .map_err(|e| format!("Error opening file {:?}: {}", info.file, e))
        .and_then(|mut f| Json::from_reader(&mut f)
        .map_err(|e| format!("Error parsing json {:?}: {}", info.file, e)))?;
    let mut packages = vec![];

    if info.package {
        scan_dic(&json, "dependencies", &mut packages, &mut features)?;
    }
    if info.dev {
        scan_dic(&json, "devDependencies", &mut packages, &mut features)?;
    }
    if info.peer {
        scan_dic(&json, "peerDependencies",
            &mut packages, &mut features)?;
    }
    if info.bundled {
        scan_dic(&json, "bundledDependencies",
            &mut packages, &mut features)?;
        scan_dic(&json, "bundleDependencies",
            &mut packages, &mut features)?;
    }
    if info.optional {
        scan_dic(&json, "optionalDependencies",
            &mut packages, &mut features)?;
    }

    packages::ensure_packages(distro, ctx, &features)?;

    if packages.len() == 0 {
        return Ok(());
    }

    let mut cmd = command(ctx, &ctx.npm_settings.npm_exe)?;
    cmd.arg("install");
    cmd.arg("--global");
    cmd.arg("--user=root");
    cmd.arg("--cache=/tmp/npm-cache");
    cmd.args(&packages);
    run(cmd)
}

pub fn list(ctx: &mut Context) -> Result<(), StepError> {
    let path = Path::new("/vagga/container/npm-list.txt");
    let file = File::create(&path)
        .map_err(|e| StepError::Write(path.to_path_buf(), e))?;
    let mut cmd = command(ctx, &ctx.npm_settings.npm_exe)?;
    cmd.arg("ls");
    cmd.arg("--global");
    cmd.stdout(Stdio::from_file(file));
    run(cmd)
}

fn npm_hash_deps(data: &Json, key: &str, hash: &mut Digest) {
    let deps = data.find(key);
    if let Some(&Json::Object(ref ob)) = deps {
        // Note the BTree is sorted on its own
        for (key, val) in ob {
            hash.field(key, val.as_string().unwrap_or("*"));
        }
    }
}

impl BuildStep for NpmConfig {
    fn name(&self) -> &'static str { "NpmConfig" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        hash.field("npm_exe", &self.npm_exe);
        hash.field("install_node", self.install_node);
        Ok(())
    }
    fn build(&self, guard: &mut Guard, _build: bool)
        -> Result<(), StepError>
    {
        guard.ctx.npm_settings = self.clone();
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

impl BuildStep for NpmInstall {
    fn name(&self) -> &'static str { "NpmInstall" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        hash.field("packages", &self.0);
        Ok(())
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        guard.distro.npm_configure(&mut guard.ctx)?;
        if build {
            npm_install(&mut guard.distro, &mut guard.ctx, &self.0)?;
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

impl BuildStep for NpmDependencies {
    fn name(&self) -> &'static str { "NpmDependencies" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        let path = Path::new("/work").join(&self.file);
        File::open(&path).map_err(|e| VersionError::Io(e, path.clone()))
        .and_then(|mut f| Json::from_reader(&mut f)
            .map_err(|e| VersionError::Json(e, path.to_path_buf())))
        .map(|data| {
            if self.package {
                npm_hash_deps(&data, "dependencies", hash);
            }
            if self.dev {
                npm_hash_deps(&data, "devDependencies", hash);
            }
            if self.peer {
                npm_hash_deps(&data, "peerDependencies", hash);
            }
            if self.bundled {
                npm_hash_deps(&data, "bundledDependencies", hash);
                npm_hash_deps(&data, "bundleDependencies", hash);
            }
            if self.optional {
                npm_hash_deps(&data, "optionalDependencies", hash);
            }
        })
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        guard.distro.npm_configure(&mut guard.ctx)?;
        if build {
            npm_deps(&mut guard.distro, &mut guard.ctx, &self)?;
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

fn yarn_scan_features(settings: &NpmConfig, pkgs: &Vec<String>)
    -> Vec<packages::Package>
{
    let mut res = vec!();
    res.push(packages::BuildEssential);
    if settings.install_node {
        res.push(packages::NodeJs);
        res.push(packages::NodeJsDev);
        res.push(packages::Yarn);
    }
    for name in pkgs.iter() {
        parse_feature(&name, &mut res);
    }
    return res;
}

pub fn setup_yarn(ctx: &mut Context)
    -> Result<(), String>
{
    let filename = download_file(&mut ctx.capsule,
        &["https://yarnpkg.com/latest.tar.gz"], None)?;
    unpack_subdir(ctx, &filename,
        &Path::new("/vagga/root/usr/lib/yarn"), Path::new("dist"))?;
    symlink("/usr/lib/yarn/bin/yarn", "/vagga/root/usr/bin/yarn")
        .map_err(|e| format!("Can't create yarn symlink: {}", e))?;
    Ok(())
}

impl BuildStep for YarnDependencies {
    fn name(&self) -> &'static str { "YarnDependencies" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        hash.field("production", self.production);
        let lock_file = Path::new("/work").join(&self.dir).join("yarn.lock");
        if lock_file.exists() {
            let mut file = File::open(&lock_file).context(&lock_file)?;
            hash.file(&lock_file, &mut file).context(&lock_file)?;
            Ok(())
        } else {
            Err(VersionError::New)
        }
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if build {
            let base_dir = Path::new("/work").join(&self.dir);
            let lock_file = base_dir.join("yarn.lock");

            guard.ctx.add_cache_dir(Path::new("/tmp/yarn-cache"),
                                    "yarn-cache".to_string())?;
            let features = yarn_scan_features(
                &guard.ctx.npm_settings, &Vec::new());
            packages::ensure_packages(
                &mut guard.distro, &mut guard.ctx, &features)?;
            let mut cmd = command(&guard.ctx, "/usr/bin/yarn")?;
            cmd.current_dir(&base_dir);
            cmd.arg("install");
            cmd.arg("--modules-folder=/usr/lib/node_modules");
            cmd.arg("--cache-folder=/tmp/yarn-cache");
            if lock_file.exists() {
                cmd.arg("--frozen-lockfile");
            }
            if self.production {
                cmd.arg("--production");
            }
            run(cmd)?;
            // TODO(tailhook) find some heuristics to determine that lockfile
            // needs to be updated to print some message
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}
<commit_msg>Add `flat: true` flag to `YarnDependencies`<commit_after>use std::fs::File;
use std::path::{Path, PathBuf};
use std::os::unix::fs::{symlink};

use quire::validate as V;
use quick_error::ResultExt;
use unshare::{Stdio};
use rustc_serialize::json::Json;

use capsule::download::download_file;
use super::super::context::{Context};
use super::super::packages;
use builder::distrib::{Distribution, DistroBox};
use builder::commands::generic::{command, run};
use builder::commands::tarcmd::unpack_subdir;
use build_step::{BuildStep, VersionError, StepError, Digest, Config, Guard};

#[derive(RustcDecodable, Debug, Clone)]
pub struct NpmConfig {
    pub install_node: bool,
    pub npm_exe: String,
}

impl NpmConfig {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("npm_exe", V::Scalar::new().default("npm"))
        .member("install_node", V::Scalar::new().default(true))
    }
}

#[derive(Debug)]
pub struct NpmInstall(Vec<String>);
tuple_struct_decode!(NpmInstall);

impl NpmInstall {
    pub fn config() -> V::Sequence<'static> {
        V::Sequence::new(V::Scalar::new())
    }
}

#[derive(RustcDecodable, Debug)]
pub struct NpmDependencies {
    pub file: PathBuf,
    pub package: bool,
    pub dev: bool,
    pub peer: bool,
    pub bundled: bool,
    pub optional: bool,
}

impl NpmDependencies {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("file", V::Scalar::new().default("package.json"))
        .member("package", V::Scalar::new().default(true))
        .member("dev", V::Scalar::new().default(true))
        .member("peer", V::Scalar::new().default(false))
        .member("bundled", V::Scalar::new().default(true))
        .member("optional", V::Scalar::new().default(false))
    }
}

impl Default for NpmConfig {
    fn default() -> NpmConfig {
        NpmConfig {
            install_node: true,
            npm_exe: "npm".to_string(),
        }
    }
}

#[derive(RustcDecodable, Debug)]
pub struct YarnDependencies {
    pub dir: PathBuf,
    pub production: bool,
    pub flat: bool,
}

impl YarnDependencies {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("dir", V::Directory::new().absolute(false).default("."))
        .member("production", V::Scalar::new().default(false))
        .member("flat", V::Scalar::new().default(false))
    }
}

fn scan_features(settings: &NpmConfig, pkgs: &Vec<String>)
    -> Vec<packages::Package>
{
    let mut res = vec!();
    res.push(packages::BuildEssential);
    if settings.install_node {
        res.push(packages::NodeJs);
        res.push(packages::NodeJsDev);
        res.push(packages::Npm);
    }
    for name in pkgs.iter() {
        parse_feature(&name, &mut res);
    }
    return res;
}

pub fn parse_feature(info: &str, features: &mut Vec<packages::Package>) {
    // Note: the info is a package name/git-url in NpmInstall but it's just
    // a version number for NpmDependencies. That's how npm works.
    if info[..].starts_with("git://") {
        features.push(packages::Git);
    } // TODO(tailhook) implement whole a lot of other npm version kinds
}

pub fn npm_install(distro: &mut Box<Distribution>, ctx: &mut Context,
    pkgs: &Vec<String>)
    -> Result<(), StepError>
{
    ctx.add_cache_dir(Path::new("/tmp/npm-cache"),
                           "npm-cache".to_string())?;
    let features = scan_features(&ctx.npm_settings, pkgs);
    packages::ensure_packages(distro, ctx, &features)?;

    if pkgs.len() == 0 {
        return Ok(());
    }

    let mut cmd = command(ctx, &ctx.npm_settings.npm_exe)?;
    cmd.arg("install");
    cmd.arg("--global");
    cmd.arg("--user=root");
    cmd.arg("--cache=/tmp/npm-cache");
    cmd.args(pkgs);
    run(cmd)
}

fn scan_dic(json: &Json, key: &str,
    packages: &mut Vec<String>, features: &mut Vec<packages::Package>)
    -> Result<(), StepError>
{
    match json.find(key) {
        Some(&Json::Object(ref ob)) => {
            for (k, v) in ob {
                if !v.is_string() {
                    return Err(StepError::Compat(format!(
                        "Package {:?} has wrong version {:?}", k, v)));
                }
                let s = v.as_string().unwrap();
                parse_feature(&s, features);
                packages.push(format!("{}@{}", k, s));
                // TODO(tailhook) check the feature
            }
            Ok(())
        }
        None => {
            Ok(())
        }
        Some(_) => {
            Err(StepError::Compat(format!(
                "The {:?} is not a mapping (JSON object)", key)))
        }
    }
}

pub fn npm_deps(distro: &mut Box<Distribution>, ctx: &mut Context,
    info: &NpmDependencies)
    -> Result<(), StepError>
{
    ctx.add_cache_dir(Path::new("/tmp/npm-cache"),
                           "npm-cache".to_string())?;
    let mut features = scan_features(&ctx.npm_settings, &Vec::new());

    let json = File::open(&Path::new("/work").join(&info.file))
        .map_err(|e| format!("Error opening file {:?}: {}", info.file, e))
        .and_then(|mut f| Json::from_reader(&mut f)
        .map_err(|e| format!("Error parsing json {:?}: {}", info.file, e)))?;
    let mut packages = vec![];

    if info.package {
        scan_dic(&json, "dependencies", &mut packages, &mut features)?;
    }
    if info.dev {
        scan_dic(&json, "devDependencies", &mut packages, &mut features)?;
    }
    if info.peer {
        scan_dic(&json, "peerDependencies",
            &mut packages, &mut features)?;
    }
    if info.bundled {
        scan_dic(&json, "bundledDependencies",
            &mut packages, &mut features)?;
        scan_dic(&json, "bundleDependencies",
            &mut packages, &mut features)?;
    }
    if info.optional {
        scan_dic(&json, "optionalDependencies",
            &mut packages, &mut features)?;
    }

    packages::ensure_packages(distro, ctx, &features)?;

    if packages.len() == 0 {
        return Ok(());
    }

    let mut cmd = command(ctx, &ctx.npm_settings.npm_exe)?;
    cmd.arg("install");
    cmd.arg("--global");
    cmd.arg("--user=root");
    cmd.arg("--cache=/tmp/npm-cache");
    cmd.args(&packages);
    run(cmd)
}

pub fn list(ctx: &mut Context) -> Result<(), StepError> {
    let path = Path::new("/vagga/container/npm-list.txt");
    let file = File::create(&path)
        .map_err(|e| StepError::Write(path.to_path_buf(), e))?;
    let mut cmd = command(ctx, &ctx.npm_settings.npm_exe)?;
    cmd.arg("ls");
    cmd.arg("--global");
    cmd.stdout(Stdio::from_file(file));
    run(cmd)
}

fn npm_hash_deps(data: &Json, key: &str, hash: &mut Digest) {
    let deps = data.find(key);
    if let Some(&Json::Object(ref ob)) = deps {
        // Note the BTree is sorted on its own
        for (key, val) in ob {
            hash.field(key, val.as_string().unwrap_or("*"));
        }
    }
}

impl BuildStep for NpmConfig {
    fn name(&self) -> &'static str { "NpmConfig" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        hash.field("npm_exe", &self.npm_exe);
        hash.field("install_node", self.install_node);
        Ok(())
    }
    fn build(&self, guard: &mut Guard, _build: bool)
        -> Result<(), StepError>
    {
        guard.ctx.npm_settings = self.clone();
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

impl BuildStep for NpmInstall {
    fn name(&self) -> &'static str { "NpmInstall" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        hash.field("packages", &self.0);
        Ok(())
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        guard.distro.npm_configure(&mut guard.ctx)?;
        if build {
            npm_install(&mut guard.distro, &mut guard.ctx, &self.0)?;
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

impl BuildStep for NpmDependencies {
    fn name(&self) -> &'static str { "NpmDependencies" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        let path = Path::new("/work").join(&self.file);
        File::open(&path).map_err(|e| VersionError::Io(e, path.clone()))
        .and_then(|mut f| Json::from_reader(&mut f)
            .map_err(|e| VersionError::Json(e, path.to_path_buf())))
        .map(|data| {
            if self.package {
                npm_hash_deps(&data, "dependencies", hash);
            }
            if self.dev {
                npm_hash_deps(&data, "devDependencies", hash);
            }
            if self.peer {
                npm_hash_deps(&data, "peerDependencies", hash);
            }
            if self.bundled {
                npm_hash_deps(&data, "bundledDependencies", hash);
                npm_hash_deps(&data, "bundleDependencies", hash);
            }
            if self.optional {
                npm_hash_deps(&data, "optionalDependencies", hash);
            }
        })
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        guard.distro.npm_configure(&mut guard.ctx)?;
        if build {
            npm_deps(&mut guard.distro, &mut guard.ctx, &self)?;
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

fn yarn_scan_features(settings: &NpmConfig, pkgs: &Vec<String>)
    -> Vec<packages::Package>
{
    let mut res = vec!();
    res.push(packages::BuildEssential);
    if settings.install_node {
        res.push(packages::NodeJs);
        res.push(packages::NodeJsDev);
        res.push(packages::Yarn);
    }
    for name in pkgs.iter() {
        parse_feature(&name, &mut res);
    }
    return res;
}

pub fn setup_yarn(ctx: &mut Context)
    -> Result<(), String>
{
    let filename = download_file(&mut ctx.capsule,
        &["https://yarnpkg.com/latest.tar.gz"], None)?;
    unpack_subdir(ctx, &filename,
        &Path::new("/vagga/root/usr/lib/yarn"), Path::new("dist"))?;
    symlink("/usr/lib/yarn/bin/yarn", "/vagga/root/usr/bin/yarn")
        .map_err(|e| format!("Can't create yarn symlink: {}", e))?;
    Ok(())
}

impl BuildStep for YarnDependencies {
    fn name(&self) -> &'static str { "YarnDependencies" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        hash.field("production", self.production);
        hash.field("flat", self.flat);
        let lock_file = Path::new("/work").join(&self.dir).join("yarn.lock");
        if lock_file.exists() {
            let mut file = File::open(&lock_file).context(&lock_file)?;
            hash.file(&lock_file, &mut file).context(&lock_file)?;
            Ok(())
        } else {
            Err(VersionError::New)
        }
    }
    fn build(&self, guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if build {
            let base_dir = Path::new("/work").join(&self.dir);
            let lock_file = base_dir.join("yarn.lock");

            guard.ctx.add_cache_dir(Path::new("/tmp/yarn-cache"),
                                    "yarn-cache".to_string())?;
            let features = yarn_scan_features(
                &guard.ctx.npm_settings, &Vec::new());
            packages::ensure_packages(
                &mut guard.distro, &mut guard.ctx, &features)?;
            let mut cmd = command(&guard.ctx, "/usr/bin/yarn")?;
            cmd.current_dir(&base_dir);
            cmd.arg("install");
            cmd.arg("--modules-folder=/usr/lib/node_modules");
            cmd.arg("--cache-folder=/tmp/yarn-cache");
            if lock_file.exists() {
                cmd.arg("--frozen-lockfile");
            }
            if self.production {
                cmd.arg("--production");
            }
            if self.flat {
                cmd.arg("--flat");
            }
            run(cmd)?;
            // TODO(tailhook) find some heuristics to determine that lockfile
            // needs to be updated to print some message
        }
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}
<|endoftext|>


<commit_before>extern crate orbfont;
extern crate ransid;

use self::orbfont::Font;

use std::mem;

use orbclient::{Color, Event, EventOption, Window};
use orbclient::event;

pub struct Console {
    pub window: Window,
    pub inner: ransid::Console,
    pub font: Font,
    pub font_bold: Font,
    pub command: String,
}

impl Console {
    pub fn new(width: u32, height: u32) -> Console {
        let mut window = Window::new_flags(-1, -1, width, height, "Terminal", true).unwrap();
        window.sync();
        Console {
            window: window,
            inner: ransid::Console::new(width as usize/8, height as usize/16),
            font: Font::find(Some("Mono"), None, Some("Regular")).unwrap(),
            font_bold: Font::find(Some("Mono"), None, Some("Bold")).unwrap(),
            command: String::new(),
        }
    }

    pub fn event(&mut self, event: Event) -> Option<String> {
        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.pressed {
                    if self.inner.raw_mode {
                        match key_event.scancode {
                            event::K_BKSP => self.command.push_str("\x08"),
                            event::K_UP => self.command.push_str("\x1B[A"),
                            event::K_DOWN => self.command.push_str("\x1B[B"),
                            event::K_RIGHT => self.command.push_str("\x1B[C"),
                            event::K_LEFT => self.command.push_str("\x1B[D"),
                            _ => match key_event.character {
                                '\0' => {},
                                c => {
                                    self.command.push(c);
                                }
                            },
                        }

                        if ! self.command.is_empty() {
                            let mut command = String::new();
                            mem::swap(&mut self.command, &mut command);
                            return Some(command);
                        }
                    } else {
                        match key_event.scancode {
                            event::K_BKSP => if ! self.command.is_empty() {
                                self.inner.redraw = true;

                                self.write(&[8]);
                                self.command.pop();
                            },
                            _ => match key_event.character {
                                '\0' => (),
                                c => {
                                    self.inner.redraw = true;

                                    self.write(&[c as u8]);
                                    self.command.push(c);

                                    if c == '\n' {
                                        let mut command = String::new();
                                        mem::swap(&mut self.command, &mut command);
                                        return Some(command);
                                    }
                                }
                            },
                        }
                    }
                }
            },
            _ => (),
        }

        None
    }

    pub fn write(&mut self, bytes: &[u8]) {
        self.inner.write(bytes);
        //TODO: if self.inner.redraw
        {
            self.inner.redraw = false;

            self.window.set(Color {
                data: self.inner.background.data
            });
            for y in 0..self.inner.h {
                for x in 0..self.inner.w {
                    let block = self.inner.display[y * self.inner.w + x];
                    self.window.rect(x as i32 * 8, y as i32 * 16, 8, 16, Color {
                        data: block.bg.data
                    });
                    if block.c != ' ' {
                        match block.style {
                            ransid::Style::Normal => {
                                self.font.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                    data: block.fg.data
                                });
                            },
                            ransid::Style::Bold => {
                                self.font_bold.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                    data: block.fg.data
                                });
                            }
                        }
                    }
                }
            }
            if self.inner.cursor {
                self.window.rect(self.inner.x as i32 * 8, self.inner.y as i32 * 16, 8, 16, Color {
                    data: self.inner.foreground.data
                });
            }
            self.window.sync();
        }
    }
}
<commit_msg>Fix issue with raw mode backspace<commit_after>extern crate orbfont;
extern crate ransid;

use self::orbfont::Font;

use std::mem;

use orbclient::{Color, Event, EventOption, Window};
use orbclient::event;

pub struct Console {
    pub window: Window,
    pub inner: ransid::Console,
    pub font: Font,
    pub font_bold: Font,
    pub command: String,
}

impl Console {
    pub fn new(width: u32, height: u32) -> Console {
        let mut window = Window::new_flags(-1, -1, width, height, "Terminal", true).unwrap();
        window.sync();
        Console {
            window: window,
            inner: ransid::Console::new(width as usize/8, height as usize/16),
            font: Font::find(Some("Mono"), None, Some("Regular")).unwrap(),
            font_bold: Font::find(Some("Mono"), None, Some("Bold")).unwrap(),
            command: String::new(),
        }
    }

    pub fn event(&mut self, event: Event) -> Option<String> {
        match event.to_option() {
            EventOption::Key(key_event) => {
                if key_event.pressed {
                    if self.inner.raw_mode {
                        match key_event.scancode {
                            event::K_BKSP => self.command.push_str("\x7F"),
                            event::K_UP => self.command.push_str("\x1B[A"),
                            event::K_DOWN => self.command.push_str("\x1B[B"),
                            event::K_RIGHT => self.command.push_str("\x1B[C"),
                            event::K_LEFT => self.command.push_str("\x1B[D"),
                            _ => match key_event.character {
                                '\0' => {},
                                c => {
                                    self.command.push(c);
                                }
                            },
                        }

                        if ! self.command.is_empty() {
                            let mut command = String::new();
                            mem::swap(&mut self.command, &mut command);
                            return Some(command);
                        }
                    } else {
                        match key_event.scancode {
                            event::K_BKSP => if ! self.command.is_empty() {
                                self.inner.redraw = true;

                                self.write(&[8]);
                                self.command.pop();
                            },
                            _ => match key_event.character {
                                '\0' => (),
                                c => {
                                    self.inner.redraw = true;

                                    self.write(&[c as u8]);
                                    self.command.push(c);

                                    if c == '\n' {
                                        let mut command = String::new();
                                        mem::swap(&mut self.command, &mut command);
                                        return Some(command);
                                    }
                                }
                            },
                        }
                    }
                }
            },
            _ => (),
        }

        None
    }

    pub fn write(&mut self, bytes: &[u8]) {
        self.inner.write(bytes);
        //TODO: if self.inner.redraw
        {
            self.inner.redraw = false;

            self.window.set(Color {
                data: self.inner.background.data
            });
            for y in 0..self.inner.h {
                for x in 0..self.inner.w {
                    let block = self.inner.display[y * self.inner.w + x];
                    self.window.rect(x as i32 * 8, y as i32 * 16, 8, 16, Color {
                        data: block.bg.data
                    });
                    if block.c != ' ' {
                        match block.style {
                            ransid::Style::Normal => {
                                self.font.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                    data: block.fg.data
                                });
                            },
                            ransid::Style::Bold => {
                                self.font_bold.render(&block.c.to_string(), 16.0).draw(&mut self.window, x as i32 * 8, y as i32 * 16, Color {
                                    data: block.fg.data
                                });
                            }
                        }
                    }
                }
            }
            if self.inner.cursor {
                self.window.rect(self.inner.x as i32 * 8, self.inner.y as i32 * 16, 8, 16, Color {
                    data: self.inner.foreground.data
                });
            }
            self.window.sync();
        }
    }
}
<|endoftext|>


<commit_before>// * This file is part of the uutils coreutils package.
// *
// * (c) 2015 Wiktor Kuropatwa <wiktor.kuropatwa@gmail.com>
// * (c) 2020 nicoo            <nicoo@debian.org>
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.

use num_traits::{
    identities::{One, Zero},
    int::PrimInt,
    ops::wrapping::{WrappingMul, WrappingNeg, WrappingSub},
};
use std::fmt::{Debug, Display};
use std::mem::swap;

// This is incorrectly reported as dead code,
//  presumably when included in build.rs.
#[allow(dead_code)]
pub fn gcd(mut a: u64, mut b: u64) -> u64 {
    while b > 0 {
        a %= b;
        swap(&mut a, &mut b);
    }
    a
}

pub(crate) trait Arithmetic: Copy + Sized {
    // The type of integers mod m, in some opaque representation
    type ModInt: Copy + Sized + Eq;

    fn new(m: u64) -> Self;
    fn modulus(&self) -> u64;
    fn from_u64(&self, n: u64) -> Self::ModInt;
    fn to_u64(&self, n: Self::ModInt) -> u64;
    fn add(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt;
    fn mul(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt;

    fn pow(&self, mut a: Self::ModInt, mut b: u64) -> Self::ModInt {
        let (_a, _b) = (a, b);
        let mut result = self.one();
        while b > 0 {
            if b & 1 != 0 {
                result = self.mul(result, a);
            }
            a = self.mul(a, a);
            b >>= 1;
        }

        // Check that r (reduced back to the usual representation) equals
        //  a^b % n, unless the latter computation overflows
        // Temporarily commented-out, as there u64::checked_pow is not available
        //  on the minimum supported Rust version, nor is an appropriate method
        //  for compiling the check conditionally.
        //debug_assert!(self
        //    .to_u64(_a)
        //    .checked_pow(_b as u32)
        //    .map(|r| r % self.modulus() == self.to_u64(result))
        //    .unwrap_or(true));

        result
    }

    fn one(&self) -> Self::ModInt {
        self.from_u64(1)
    }
    fn minus_one(&self) -> Self::ModInt {
        self.from_u64(self.modulus() - 1)
    }
    fn zero(&self) -> Self::ModInt {
        self.from_u64(0)
    }
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct Montgomery<T: DoubleInt> {
    a: T,
    n: T,
}

impl<T: DoubleInt> Montgomery<T> {
    /// computes x/R mod n efficiently
    fn reduce(&self, x: T::DoubleWidth) -> T {
        let t_bits = T::zero().count_zeros() as usize;

        debug_assert!(x < (self.n.as_double_width()) << t_bits);
        // TODO: optimiiiiiiise
        let Montgomery { a, n } = self;
        let m = T::from_double_width(x).wrapping_mul(a);
        let nm = (n.as_double_width()) * (m.as_double_width());
        let (xnm, overflow) = x.overflowing_add_(nm); // x + n*m
        debug_assert_eq!(
            xnm % (T::DoubleWidth::one() << T::zero().count_zeros() as usize),
            T::DoubleWidth::zero()
        );

        // (x + n*m) / R
        // in case of overflow, this is (2¹²⁸ + xnm)/2⁶⁴ - n = xnm/2⁶⁴ + (2⁶⁴ - n)
        let y = T::from_double_width(xnm >> t_bits)
            + if !overflow {
                T::zero()
            } else {
                n.wrapping_neg()
            };

        if y >= *n {
            y - *n
        } else {
            y
        }
    }
}

impl<T: DoubleInt> Arithmetic for Montgomery<T> {
    // Montgomery transform, R=2⁶⁴
    // Provides fast arithmetic mod n (n odd, u64)
    type ModInt = T;

    fn new(n: u64) -> Self {
        debug_assert!(T::zero().count_zeros() >= 64 || n < (1 << T::zero().count_zeros() as usize));
        let n = T::from_u64(n);
        let a = modular_inverse(n).wrapping_neg();
        debug_assert_eq!(n.wrapping_mul(&a), T::one().wrapping_neg());
        Montgomery { a, n }
    }

    fn modulus(&self) -> u64 {
        self.n.as_u64()
    }

    fn from_u64(&self, x: u64) -> Self::ModInt {
        // TODO: optimise!
        debug_assert!(x < self.n.as_u64());
        let r = T::from_double_width(
            ((T::DoubleWidth::from_u64(x)) << T::zero().count_zeros() as usize)
                % self.n.as_double_width(),
        );
        debug_assert_eq!(x, self.to_u64(r));
        r
    }

    fn to_u64(&self, n: Self::ModInt) -> u64 {
        self.reduce(n.as_double_width()).as_u64()
    }

    fn add(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt {
        let (r, overflow) = a.overflowing_add_(b);

        // In case of overflow, a+b = 2⁶⁴ + r = (2⁶⁴ - n) + r (working mod n)
        let r = if !overflow {
            r
        } else {
            r + self.n.wrapping_neg()
        };

        // Normalise to [0; n[
        let r = if r < self.n { r } else { r - self.n };

        // Check that r (reduced back to the usual representation) equals
        // a+b % n
        #[cfg(debug_assertions)]
        {
            let a_r = self.to_u64(a) as u128;
            let b_r = self.to_u64(b) as u128;
            let r_r = self.to_u64(r);
            let r_2 = ((a_r + b_r) % self.n.as_u128()) as u64;
            debug_assert_eq!(
                r_r, r_2,
                "[{}] = {} ≠ {} = {} + {} = [{}] + [{}] mod {}; a = {}",
                r, r_r, r_2, a_r, b_r, a, b, self.n, self.a
            );
        }
        r
    }

    fn mul(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt {
        let r = self.reduce(a.as_double_width() * b.as_double_width());

        // Check that r (reduced back to the usual representation) equals
        // a*b % n
        #[cfg(debug_assertions)]
        {
            let a_r = self.to_u64(a) as u128;
            let b_r = self.to_u64(b) as u128;
            let r_r = self.to_u64(r);
            let r_2: u64 = ((a_r * b_r) % self.n.as_u128()) as u64;
            debug_assert_eq!(
                r_r, r_2,
                "[{}] = {} ≠ {} = {} * {} = [{}] * [{}] mod {}; a = {}",
                r, r_r, r_2, a_r, b_r, a, b, self.n, self.a
            );
        }
        r
    }
}

// NOTE: Trait can be removed once num-traits adds a similar one;
//       see https://github.com/rust-num/num-traits/issues/168
pub(crate) trait OverflowingAdd: Sized {
    fn overflowing_add_(self, n: Self) -> (Self, bool);
}
macro_rules! overflowing {
    ($x:ty) => {
        impl OverflowingAdd for $x {
            fn overflowing_add_(self, n: Self) -> (Self, bool) {
                self.overflowing_add(n)
            }
        }
    };
}
overflowing!(u32);
overflowing!(u64);
overflowing!(u128);

pub(crate) trait Int:
    Display + Debug + PrimInt + OverflowingAdd + WrappingNeg + WrappingSub + WrappingMul
{
    fn as_u64(&self) -> u64;
    fn from_u64(n: u64) -> Self;

    #[cfg(debug_assertions)]
    fn as_u128(&self) -> u128;
}

pub(crate) trait DoubleInt: Int {
    /// An integer type with twice the width of `Self`.
    /// In particular, multiplications (of `Int` values) can be performed in
    ///  `Self::DoubleWidth` without possibility of overflow.
    type DoubleWidth: Int;

    fn as_double_width(self) -> Self::DoubleWidth;
    fn from_double_width(n: Self::DoubleWidth) -> Self;
}

macro_rules! int {
    ( $x:ty ) => {
        impl Int for $x {
            fn as_u64(&self) -> u64 {
                *self as u64
            }
            fn from_u64(n: u64) -> Self {
                n as _
            }
            #[cfg(debug_assertions)]
            fn as_u128(&self) -> u128 {
                *self as u128
            }
        }
    };
}
macro_rules! double_int {
    ( $x:ty, $y:ty ) => {
        int!($x);
        impl DoubleInt for $x {
            type DoubleWidth = $y;

            fn as_double_width(self) -> $y {
                self as _
            }
            fn from_double_width(n: $y) -> $x {
                n as _
            }
        }
    };
}

double_int!(u32, u64);
double_int!(u64, u128);
int!(u128);

// extended Euclid algorithm
// precondition: a is odd
pub(crate) fn modular_inverse<T: Int>(a: T) -> T {
    let zero = T::zero();
    let one = T::one();
    debug_assert!(a % (one + one) == one, "{:?} is not odd", a);

    let mut t = zero;
    let mut newt = one;
    let mut r = zero;
    let mut newr = a;

    while newr != zero {
        let quot = if r == zero {
            // special case when we're just starting out
            // This works because we know that
            // a does not divide 2^64, so floor(2^64 / a) == floor((2^64-1) / a);
            T::max_value()
        } else {
            r
        } / newr;

        let newtp = t.wrapping_sub(&quot.wrapping_mul(&newt));
        t = newt;
        newt = newtp;

        let newrp = r.wrapping_sub(&quot.wrapping_mul(&newr));
        r = newr;
        newr = newrp;
    }

    debug_assert_eq!(r, one);
    t
}

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

    macro_rules! parametrized_check {
        ( $f:ident ) => {
            paste::item! {
                #[test]
                fn [< $f _ u32 >]() {
                    $f::<u32>()
                }
                #[test]
                fn [< $f _ u64 >]() {
                    $f::<u64>()
                }
            }
        };
    }

    fn test_inverter<T: Int>() {
        // All odd integers from 1 to 20 000
        let one = T::from(1).unwrap();
        let two = T::from(2).unwrap();
        let mut test_values = (0..10_000)
            .map(|i| T::from(i).unwrap())
            .map(|i| two * i + one);

        assert!(test_values.all(|x| x.wrapping_mul(&modular_inverse(x)) == one));
    }
    parametrized_check!(test_inverter);

    fn test_add<A: DoubleInt>() {
        for n in 0..100 {
            let n = 2 * n + 1;
            let m = Montgomery::<A>::new(n);
            for x in 0..n {
                let m_x = m.from_u64(x);
                for y in 0..=x {
                    let m_y = m.from_u64(y);
                    println!("{n:?}, {x:?}, {y:?}", n = n, x = x, y = y);
                    assert_eq!((x + y) % n, m.to_u64(m.add(m_x, m_y)));
                }
            }
        }
    }
    parametrized_check!(test_add);

    fn test_mult<A: DoubleInt>() {
        for n in 0..100 {
            let n = 2 * n + 1;
            let m = Montgomery::<A>::new(n);
            for x in 0..n {
                let m_x = m.from_u64(x);
                for y in 0..=x {
                    let m_y = m.from_u64(y);
                    assert_eq!((x * y) % n, m.to_u64(m.mul(m_x, m_y)));
                }
            }
        }
    }
    parametrized_check!(test_mult);

    fn test_roundtrip<A: DoubleInt>() {
        for n in 0..100 {
            let n = 2 * n + 1;
            let m = Montgomery::<A>::new(n);
            for x in 0..n {
                let x_ = m.from_u64(x);
                assert_eq!(x, m.to_u64(x_));
            }
        }
    }
    parametrized_check!(test_roundtrip);
}
<commit_msg>factor::numeric::gcd: Implement Stein's binary GCD algorithm<commit_after>// * This file is part of the uutils coreutils package.
// *
// * (c) 2015 Wiktor Kuropatwa <wiktor.kuropatwa@gmail.com>
// * (c) 2020 nicoo            <nicoo@debian.org>
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.

use num_traits::{
    identities::{One, Zero},
    int::PrimInt,
    ops::wrapping::{WrappingMul, WrappingNeg, WrappingSub},
};
use std::fmt::{Debug, Display};
use std::mem::swap;

// This is incorrectly reported as dead code,
//  presumably when included in build.rs.
#[allow(dead_code)]
pub fn gcd(mut n: u64, mut m: u64) -> u64 {
    // Stein's binary GCD algorithm
    // Base cases: gcd(n, 0) = gcd(0, n) = n
    if n == 0 {
        return m;
    } else if m == 0 {
        return n;
    }

    // First, let's extract common factor-2: gcd(2ⁱ n, 2ⁱ m) = 2ⁱ gcd(n, m)
    let k = (n | m).trailing_zeros();
    n >>= k;
    m >>= k;

    // Second, ensure n is odd; gcd(n, m) = gcd(n/2, m) when n or m is odd
    n >>= n.trailing_zeros();

    loop {
        // Invariant: n odd
        debug_assert!(n % 2 == 1, "n = {} is even", n);

        m >>= m.trailing_zeros();

        if n > m {
            swap(&mut n, &mut m);
        }
        m -= n;

        if m == 0 {
            return n << k;
        }
    }
}

pub(crate) trait Arithmetic: Copy + Sized {
    // The type of integers mod m, in some opaque representation
    type ModInt: Copy + Sized + Eq;

    fn new(m: u64) -> Self;
    fn modulus(&self) -> u64;
    fn from_u64(&self, n: u64) -> Self::ModInt;
    fn to_u64(&self, n: Self::ModInt) -> u64;
    fn add(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt;
    fn mul(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt;

    fn pow(&self, mut a: Self::ModInt, mut b: u64) -> Self::ModInt {
        let (_a, _b) = (a, b);
        let mut result = self.one();
        while b > 0 {
            if b & 1 != 0 {
                result = self.mul(result, a);
            }
            a = self.mul(a, a);
            b >>= 1;
        }

        // Check that r (reduced back to the usual representation) equals
        //  a^b % n, unless the latter computation overflows
        // Temporarily commented-out, as there u64::checked_pow is not available
        //  on the minimum supported Rust version, nor is an appropriate method
        //  for compiling the check conditionally.
        //debug_assert!(self
        //    .to_u64(_a)
        //    .checked_pow(_b as u32)
        //    .map(|r| r % self.modulus() == self.to_u64(result))
        //    .unwrap_or(true));

        result
    }

    fn one(&self) -> Self::ModInt {
        self.from_u64(1)
    }
    fn minus_one(&self) -> Self::ModInt {
        self.from_u64(self.modulus() - 1)
    }
    fn zero(&self) -> Self::ModInt {
        self.from_u64(0)
    }
}

#[derive(Clone, Copy, Debug)]
pub(crate) struct Montgomery<T: DoubleInt> {
    a: T,
    n: T,
}

impl<T: DoubleInt> Montgomery<T> {
    /// computes x/R mod n efficiently
    fn reduce(&self, x: T::DoubleWidth) -> T {
        let t_bits = T::zero().count_zeros() as usize;

        debug_assert!(x < (self.n.as_double_width()) << t_bits);
        // TODO: optimiiiiiiise
        let Montgomery { a, n } = self;
        let m = T::from_double_width(x).wrapping_mul(a);
        let nm = (n.as_double_width()) * (m.as_double_width());
        let (xnm, overflow) = x.overflowing_add_(nm); // x + n*m
        debug_assert_eq!(
            xnm % (T::DoubleWidth::one() << T::zero().count_zeros() as usize),
            T::DoubleWidth::zero()
        );

        // (x + n*m) / R
        // in case of overflow, this is (2¹²⁸ + xnm)/2⁶⁴ - n = xnm/2⁶⁴ + (2⁶⁴ - n)
        let y = T::from_double_width(xnm >> t_bits)
            + if !overflow {
                T::zero()
            } else {
                n.wrapping_neg()
            };

        if y >= *n {
            y - *n
        } else {
            y
        }
    }
}

impl<T: DoubleInt> Arithmetic for Montgomery<T> {
    // Montgomery transform, R=2⁶⁴
    // Provides fast arithmetic mod n (n odd, u64)
    type ModInt = T;

    fn new(n: u64) -> Self {
        debug_assert!(T::zero().count_zeros() >= 64 || n < (1 << T::zero().count_zeros() as usize));
        let n = T::from_u64(n);
        let a = modular_inverse(n).wrapping_neg();
        debug_assert_eq!(n.wrapping_mul(&a), T::one().wrapping_neg());
        Montgomery { a, n }
    }

    fn modulus(&self) -> u64 {
        self.n.as_u64()
    }

    fn from_u64(&self, x: u64) -> Self::ModInt {
        // TODO: optimise!
        debug_assert!(x < self.n.as_u64());
        let r = T::from_double_width(
            ((T::DoubleWidth::from_u64(x)) << T::zero().count_zeros() as usize)
                % self.n.as_double_width(),
        );
        debug_assert_eq!(x, self.to_u64(r));
        r
    }

    fn to_u64(&self, n: Self::ModInt) -> u64 {
        self.reduce(n.as_double_width()).as_u64()
    }

    fn add(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt {
        let (r, overflow) = a.overflowing_add_(b);

        // In case of overflow, a+b = 2⁶⁴ + r = (2⁶⁴ - n) + r (working mod n)
        let r = if !overflow {
            r
        } else {
            r + self.n.wrapping_neg()
        };

        // Normalise to [0; n[
        let r = if r < self.n { r } else { r - self.n };

        // Check that r (reduced back to the usual representation) equals
        // a+b % n
        #[cfg(debug_assertions)]
        {
            let a_r = self.to_u64(a) as u128;
            let b_r = self.to_u64(b) as u128;
            let r_r = self.to_u64(r);
            let r_2 = ((a_r + b_r) % self.n.as_u128()) as u64;
            debug_assert_eq!(
                r_r, r_2,
                "[{}] = {} ≠ {} = {} + {} = [{}] + [{}] mod {}; a = {}",
                r, r_r, r_2, a_r, b_r, a, b, self.n, self.a
            );
        }
        r
    }

    fn mul(&self, a: Self::ModInt, b: Self::ModInt) -> Self::ModInt {
        let r = self.reduce(a.as_double_width() * b.as_double_width());

        // Check that r (reduced back to the usual representation) equals
        // a*b % n
        #[cfg(debug_assertions)]
        {
            let a_r = self.to_u64(a) as u128;
            let b_r = self.to_u64(b) as u128;
            let r_r = self.to_u64(r);
            let r_2: u64 = ((a_r * b_r) % self.n.as_u128()) as u64;
            debug_assert_eq!(
                r_r, r_2,
                "[{}] = {} ≠ {} = {} * {} = [{}] * [{}] mod {}; a = {}",
                r, r_r, r_2, a_r, b_r, a, b, self.n, self.a
            );
        }
        r
    }
}

// NOTE: Trait can be removed once num-traits adds a similar one;
//       see https://github.com/rust-num/num-traits/issues/168
pub(crate) trait OverflowingAdd: Sized {
    fn overflowing_add_(self, n: Self) -> (Self, bool);
}
macro_rules! overflowing {
    ($x:ty) => {
        impl OverflowingAdd for $x {
            fn overflowing_add_(self, n: Self) -> (Self, bool) {
                self.overflowing_add(n)
            }
        }
    };
}
overflowing!(u32);
overflowing!(u64);
overflowing!(u128);

pub(crate) trait Int:
    Display + Debug + PrimInt + OverflowingAdd + WrappingNeg + WrappingSub + WrappingMul
{
    fn as_u64(&self) -> u64;
    fn from_u64(n: u64) -> Self;

    #[cfg(debug_assertions)]
    fn as_u128(&self) -> u128;
}

pub(crate) trait DoubleInt: Int {
    /// An integer type with twice the width of `Self`.
    /// In particular, multiplications (of `Int` values) can be performed in
    ///  `Self::DoubleWidth` without possibility of overflow.
    type DoubleWidth: Int;

    fn as_double_width(self) -> Self::DoubleWidth;
    fn from_double_width(n: Self::DoubleWidth) -> Self;
}

macro_rules! int {
    ( $x:ty ) => {
        impl Int for $x {
            fn as_u64(&self) -> u64 {
                *self as u64
            }
            fn from_u64(n: u64) -> Self {
                n as _
            }
            #[cfg(debug_assertions)]
            fn as_u128(&self) -> u128 {
                *self as u128
            }
        }
    };
}
macro_rules! double_int {
    ( $x:ty, $y:ty ) => {
        int!($x);
        impl DoubleInt for $x {
            type DoubleWidth = $y;

            fn as_double_width(self) -> $y {
                self as _
            }
            fn from_double_width(n: $y) -> $x {
                n as _
            }
        }
    };
}

double_int!(u32, u64);
double_int!(u64, u128);
int!(u128);

// extended Euclid algorithm
// precondition: a is odd
pub(crate) fn modular_inverse<T: Int>(a: T) -> T {
    let zero = T::zero();
    let one = T::one();
    debug_assert!(a % (one + one) == one, "{:?} is not odd", a);

    let mut t = zero;
    let mut newt = one;
    let mut r = zero;
    let mut newr = a;

    while newr != zero {
        let quot = if r == zero {
            // special case when we're just starting out
            // This works because we know that
            // a does not divide 2^64, so floor(2^64 / a) == floor((2^64-1) / a);
            T::max_value()
        } else {
            r
        } / newr;

        let newtp = t.wrapping_sub(&quot.wrapping_mul(&newt));
        t = newt;
        newt = newtp;

        let newrp = r.wrapping_sub(&quot.wrapping_mul(&newr));
        r = newr;
        newr = newrp;
    }

    debug_assert_eq!(r, one);
    t
}

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

    quickcheck! {
        fn gcd(a: u64, b: u64) -> bool {
            // Test against the Euclidean algorithm
            let g = {
                let (mut a, mut b) = (a, b);
                while b > 0 {
                    a %= b;
                    swap(&mut a, &mut b);
                }
                a
            };
            super::gcd(a, b) == g
        }
    }

    macro_rules! parametrized_check {
        ( $f:ident ) => {
            paste::item! {
                #[test]
                fn [< $f _ u32 >]() {
                    $f::<u32>()
                }
                #[test]
                fn [< $f _ u64 >]() {
                    $f::<u64>()
                }
            }
        };
    }

    fn test_inverter<T: Int>() {
        // All odd integers from 1 to 20 000
        let one = T::from(1).unwrap();
        let two = T::from(2).unwrap();
        let mut test_values = (0..10_000)
            .map(|i| T::from(i).unwrap())
            .map(|i| two * i + one);

        assert!(test_values.all(|x| x.wrapping_mul(&modular_inverse(x)) == one));
    }
    parametrized_check!(test_inverter);

    fn test_add<A: DoubleInt>() {
        for n in 0..100 {
            let n = 2 * n + 1;
            let m = Montgomery::<A>::new(n);
            for x in 0..n {
                let m_x = m.from_u64(x);
                for y in 0..=x {
                    let m_y = m.from_u64(y);
                    println!("{n:?}, {x:?}, {y:?}", n = n, x = x, y = y);
                    assert_eq!((x + y) % n, m.to_u64(m.add(m_x, m_y)));
                }
            }
        }
    }
    parametrized_check!(test_add);

    fn test_mult<A: DoubleInt>() {
        for n in 0..100 {
            let n = 2 * n + 1;
            let m = Montgomery::<A>::new(n);
            for x in 0..n {
                let m_x = m.from_u64(x);
                for y in 0..=x {
                    let m_y = m.from_u64(y);
                    assert_eq!((x * y) % n, m.to_u64(m.mul(m_x, m_y)));
                }
            }
        }
    }
    parametrized_check!(test_mult);

    fn test_roundtrip<A: DoubleInt>() {
        for n in 0..100 {
            let n = 2 * n + 1;
            let m = Montgomery::<A>::new(n);
            for x in 0..n {
                let x_ = m.from_u64(x);
                assert_eq!(x, m.to_u64(x_));
            }
        }
    }
    parametrized_check!(test_roundtrip);
}
<|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/. */

// FIXME
#![allow(unused_imports)]

pub use self::interface::{QuirksMode, Quirks, LimitedQuirks, NoQuirks};
pub use self::interface::TreeSink;

use tokenizer;
use tokenizer::{Doctype, Attribute, AttrName, TagKind, StartTag, EndTag, Tag};
use tokenizer::TokenSink;

use util::str::{strip_leading_whitespace, none_as_empty};
use util::namespace::HTML;

use std::default::Default;
use std::mem::replace;

mod interface;
mod states;
mod data;

/// We mostly only work with these tokens. Everything else is handled
/// specially at the beginning of `process_in_mode`.
#[deriving(Eq, TotalEq, Clone, Show)]
enum Token {
    TagToken(Tag),
    CommentToken(StrBuf),
    CharacterTokens(StrBuf),
    EOFToken,
}

/// Tree builder options, with an impl for Default.
#[deriving(Clone)]
pub struct TreeBuilderOpts {
    /// Is scripting enabled?
    pub scripting_enabled: bool,

    /// Is this an iframe srcdoc document?
    pub iframe_srcdoc: bool,
}

impl Default for TreeBuilderOpts {
    fn default() -> TreeBuilderOpts {
        TreeBuilderOpts {
            scripting_enabled: true,
            iframe_srcdoc: false,
        }
    }
}

pub struct TreeBuilder<'sink, Handle, Sink> {
    /// Options controlling the behavior of the tree builder.
    opts: TreeBuilderOpts,

    /// Consumer of tree modifications.
    sink: &'sink mut Sink,

    /// Insertion mode.
    mode: states::InsertionMode,

    /// Original insertion mode, used by Text and InTableText modes.
    orig_mode: states::InsertionMode,

    /// The document node, which is created by the sink.
    doc_handle: Handle,

    /// Stack of open elements, most recently added at end.
    open_elems: Vec<Handle>,

    /// Head element pointer.
    head_elem: Option<Handle>,
}

/// Remove leading whitespace from character tokens;
/// return None if the entire string is removed.
fn drop_whitespace(token: Token) -> Option<Token> {
    match token {
        CharacterTokens(ref x) => match strip_leading_whitespace(x.as_slice()) {
            "" => return None,
            // FIXME: We don't absolutely need to copy here, but it's hard
            // to correctly handle reconsumption without it.
            y if y.len() != x.len() => return Some(CharacterTokens(y.to_strbuf())),
            _ => (),
        },
        _ => (),
    }
    // Fall through to un-borrow `token`.
    Some(token)
}

// We use guards, so we can't bind tags by move.  Instead, bind by ref
// mut and take attrs with `replace`.  This is basically fine since
// empty `Vec` doesn't allocate.
fn take_attrs(t: &mut Tag) -> Vec<Attribute> {
    replace(&mut t.attrs, vec!())
}

enum ProcessResult {
    Done,
    Reprocess(states::InsertionMode, Token),
}

macro_rules! drop_whitespace ( ($x:expr) => (
    unwrap_or_return!(drop_whitespace($x), Done)
))

macro_rules! start ( ($var:ident) => (
    TagToken(ref mut $var @ Tag { kind: StartTag, ..})
))

macro_rules! end ( ($var:ident) => (
    TagToken(ref mut $var @ Tag { kind: EndTag, ..})
))

macro_rules! named ( ($t:expr, $($atom:ident)*) => (
    match_atom!($t.name { $($atom)* => true, _ => false })
))

impl<'sink, Handle: Clone, Sink: TreeSink<Handle>> TreeBuilder<'sink, Handle, Sink> {
    pub fn new(sink: &'sink mut Sink, opts: TreeBuilderOpts) -> TreeBuilder<'sink, Handle, Sink> {
        let doc_handle = sink.get_document();
        TreeBuilder {
            opts: opts,
            sink: sink,
            mode: states::Initial,
            orig_mode: states::Initial,
            doc_handle: doc_handle,
            open_elems: vec!(),
            head_elem: None,
        }
    }

    fn push(&mut self, elem: &Handle) {
        self.open_elems.push(elem.clone());
    }

    fn create_root(&mut self, attrs: Vec<Attribute>) {
        let elem = self.sink.create_element(HTML, atom!(html), attrs);
        self.push(&elem);
        self.sink.append_element(self.doc_handle.clone(), elem);
        // FIXME: application cache selection algorithm
    }

    fn process_in_mode(&mut self, mut mode: states::InsertionMode, token: tokenizer::Token) {
        // Handle `ParseError` and `DoctypeToken`; convert everything else to the local `Token` type.
        let mut token = match token {
            tokenizer::ParseError(e) => {
                self.sink.parse_error(e);
                return;
            }

            tokenizer::DoctypeToken(dt) => if mode == states::Initial {
                let (err, quirk) = data::doctype_error_and_quirks(&dt, self.opts.iframe_srcdoc);
                if err {
                    self.sink.parse_error(format!("Bad DOCTYPE: {}", dt));
                }
                let Doctype { name, public_id, system_id, force_quirks: _ } = dt;
                self.sink.append_doctype_to_document(
                    none_as_empty(name),
                    none_as_empty(public_id),
                    none_as_empty(system_id)
                );
                self.sink.set_quirks_mode(quirk);

                self.mode = states::BeforeHtml;
                return;
            } else {
                self.sink.parse_error(format!("DOCTYPE in insertion mode {:?}", mode));
                return;
            },

            tokenizer::TagToken(x) => TagToken(x),
            tokenizer::CommentToken(x) => CommentToken(x),
            tokenizer::CharacterTokens(x) => CharacterTokens(x),
            tokenizer::EOFToken => EOFToken,
        };

        loop {
            match self.process_local(mode, token) {
                Done => return,
                Reprocess(m, t) => {
                    mode = m;
                    token = t;
                }
            }
        }
    }

    fn process_local(&mut self, mode: states::InsertionMode, token: Token) -> ProcessResult {
        debug!("processing {} in insertion mode {:?}", token, mode);

        match mode {
            states::Initial => match drop_whitespace!(token) {
                CommentToken(text) => {
                    self.sink.append_comment(self.doc_handle.clone(), text);
                    Done
                }
                token => {
                    if !self.opts.iframe_srcdoc {
                        self.sink.parse_error(format!("Bad token in Initial insertion mode: {}", token));
                        self.sink.set_quirks_mode(Quirks);
                    }
                    Reprocess(states::BeforeHtml, token)
                }
            },

            states::BeforeHtml => match drop_whitespace!(token) {
                CommentToken(text) => {
                    self.sink.append_comment(self.doc_handle.clone(), text);
                    Done
                }
                start!(t) if named!(t, html) => {
                    self.create_root(take_attrs(t));
                    Done
                }
                end!(t) if !named!(t, head body html br) => {
                    self.sink.parse_error(format!("Unexpected end tag in BeforeHtml mode: {}", t));
                    Done
                }
                token => {
                    self.create_root(vec!());
                    Reprocess(states::BeforeHead, token)
                }
            },

              states::BeforeHead
            | states::InHead
            | states::InHeadNoscript
            | states::AfterHead
            | states::InBody
            | states::Text
            | states::InTable
            | states::InTableText
            | states::InCaption
            | states::InColumnGroup
            | states::InTableBody
            | states::InRow
            | states::InCell
            | states::InSelect
            | states::InSelectInTable
            | states::InTemplate
            | states::AfterBody
            | states::InFrameset
            | states::AfterFrameset
            | states::AfterAfterBody
            | states::AfterAfterFrameset
                => fail!("not implemented"),
        }
    }
}

impl<'sink, Handle: Clone, Sink: TreeSink<Handle>> TokenSink for TreeBuilder<'sink, Handle, Sink> {
    fn process_token(&mut self, token: tokenizer::Token) {
        self.process_in_mode(self.mode, token);
    }
}

test_eq!(drop_not_characters, drop_whitespace(EOFToken), Some(EOFToken))
test_eq!(drop_all_whitespace, drop_whitespace(CharacterTokens("    ".to_strbuf())), None)
test_eq!(drop_some_whitespace,
    drop_whitespace(CharacterTokens("   hello".to_strbuf())),
    Some(CharacterTokens("hello".to_strbuf())))
test_eq!(drop_no_whitespace,
    drop_whitespace(CharacterTokens("hello".to_strbuf())),
    Some(CharacterTokens("hello".to_strbuf())))

#[test]
fn empty_drop_doesnt_reallocate() {
    fn get_ptr(token: &Token) -> *u8 {
        match *token {
            CharacterTokens(ref x) => x.as_slice().as_ptr(),
            _ => fail!("not characters"),
        }
    }

    let x = CharacterTokens("hello".to_strbuf());
    let p = get_ptr(&x);
    assert_eq!(p, get_ptr(&drop_whitespace(x).unwrap()));
}
<commit_msg>BeforeHead mode<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/. */

// FIXME
#![allow(unused_imports)]

pub use self::interface::{QuirksMode, Quirks, LimitedQuirks, NoQuirks};
pub use self::interface::TreeSink;

use tokenizer;
use tokenizer::{Doctype, Attribute, AttrName, TagKind, StartTag, EndTag, Tag};
use tokenizer::TokenSink;

use util::str::{strip_leading_whitespace, none_as_empty};
use util::atom::Atom;
use util::namespace::HTML;

use std::default::Default;
use std::mem::replace;

mod interface;
mod states;
mod data;

/// We mostly only work with these tokens. Everything else is handled
/// specially at the beginning of `process_in_mode`.
#[deriving(Eq, TotalEq, Clone, Show)]
enum Token {
    TagToken(Tag),
    CommentToken(StrBuf),
    CharacterTokens(StrBuf),
    EOFToken,
}

/// Tree builder options, with an impl for Default.
#[deriving(Clone)]
pub struct TreeBuilderOpts {
    /// Is scripting enabled?
    pub scripting_enabled: bool,

    /// Is this an iframe srcdoc document?
    pub iframe_srcdoc: bool,
}

impl Default for TreeBuilderOpts {
    fn default() -> TreeBuilderOpts {
        TreeBuilderOpts {
            scripting_enabled: true,
            iframe_srcdoc: false,
        }
    }
}

pub struct TreeBuilder<'sink, Handle, Sink> {
    /// Options controlling the behavior of the tree builder.
    opts: TreeBuilderOpts,

    /// Consumer of tree modifications.
    sink: &'sink mut Sink,

    /// Insertion mode.
    mode: states::InsertionMode,

    /// Original insertion mode, used by Text and InTableText modes.
    orig_mode: states::InsertionMode,

    /// The document node, which is created by the sink.
    doc_handle: Handle,

    /// Stack of open elements, most recently added at end.
    open_elems: Vec<Handle>,

    /// Head element pointer.
    head_elem: Option<Handle>,
}

/// Remove leading whitespace from character tokens;
/// return None if the entire string is removed.
fn drop_whitespace(token: Token) -> Option<Token> {
    match token {
        CharacterTokens(ref x) => match strip_leading_whitespace(x.as_slice()) {
            "" => return None,
            // FIXME: We don't absolutely need to copy here, but it's hard
            // to correctly handle reconsumption without it.
            y if y.len() != x.len() => return Some(CharacterTokens(y.to_strbuf())),
            _ => (),
        },
        _ => (),
    }
    // Fall through to un-borrow `token`.
    Some(token)
}

// We use guards, so we can't bind tags by move.  Instead, bind by ref
// mut and take attrs with `replace`.  This is basically fine since
// empty `Vec` doesn't allocate.
fn take_attrs(t: &mut Tag) -> Vec<Attribute> {
    replace(&mut t.attrs, vec!())
}

enum ProcessResult {
    Done,
    Reprocess(states::InsertionMode, Token),
}

macro_rules! drop_whitespace ( ($x:expr) => (
    unwrap_or_return!(drop_whitespace($x), Done)
))

macro_rules! tag_pattern (
    ($kind:ident     $var:ident) => ( TagToken(ref     $var @ Tag { kind: $kind, ..}) );
    ($kind:ident mut $var:ident) => ( TagToken(ref mut $var @ Tag { kind: $kind, ..}) );
)

macro_rules! start ( ($($args:tt)*) => ( tag_pattern!(StartTag $($args)*) ))
macro_rules! end   ( ($($args:tt)*) => ( tag_pattern!(EndTag   $($args)*) ))

macro_rules! named ( ($t:expr, $($atom:ident)*) => (
    match_atom!($t.name { $($atom)* => true, _ => false })
))

macro_rules! kind_named ( ($kind:ident $t:expr, $($atom:ident)*) => (
    match $t {
        tag_pattern!($kind t) => named!(t, $($atom)*),
        _ => false,
    }
))

macro_rules! start_named ( ($($args:tt)*) => ( kind_named!(StartTag $($args)*) ))
macro_rules! end_named   ( ($($args:tt)*) => ( kind_named!(EndTag   $($args)*) ))

impl<'sink, Handle: Clone, Sink: TreeSink<Handle>> TreeBuilder<'sink, Handle, Sink> {
    pub fn new(sink: &'sink mut Sink, opts: TreeBuilderOpts) -> TreeBuilder<'sink, Handle, Sink> {
        let doc_handle = sink.get_document();
        TreeBuilder {
            opts: opts,
            sink: sink,
            mode: states::Initial,
            orig_mode: states::Initial,
            doc_handle: doc_handle,
            open_elems: vec!(),
            head_elem: None,
        }
    }

    // The "appropriate place for inserting a node".
    fn target(&self) -> Handle {
        // FIXME: foster parenting, templates, other nonsense
        self.open_elems.last().expect("no current element").clone()
    }

    fn push(&mut self, elem: &Handle) {
        self.open_elems.push(elem.clone());
    }

    fn create_root(&mut self, attrs: Vec<Attribute>) {
        let elem = self.sink.create_element(HTML, atom!(html), attrs);
        self.push(&elem);
        self.sink.append_element(self.doc_handle.clone(), elem);
        // FIXME: application cache selection algorithm
    }

    fn create_element(&mut self, name: Atom, attrs: Vec<Attribute>) -> Handle {
        let target = self.target();
        let elem = self.sink.create_element(HTML, name, attrs);
        self.push(&elem);
        self.sink.append_element(target, elem.clone());
        // FIXME: Remove from the stack if we can't append?
        elem
    }

    fn process_in_mode(&mut self, mut mode: states::InsertionMode, token: tokenizer::Token) {
        // Handle `ParseError` and `DoctypeToken`; convert everything else to the local `Token` type.
        let mut token = match token {
            tokenizer::ParseError(e) => {
                self.sink.parse_error(e);
                return;
            }

            tokenizer::DoctypeToken(dt) => if mode == states::Initial {
                let (err, quirk) = data::doctype_error_and_quirks(&dt, self.opts.iframe_srcdoc);
                if err {
                    self.sink.parse_error(format!("Bad DOCTYPE: {}", dt));
                }
                let Doctype { name, public_id, system_id, force_quirks: _ } = dt;
                self.sink.append_doctype_to_document(
                    none_as_empty(name),
                    none_as_empty(public_id),
                    none_as_empty(system_id)
                );
                self.sink.set_quirks_mode(quirk);

                self.mode = states::BeforeHtml;
                return;
            } else {
                self.sink.parse_error(format!("DOCTYPE in insertion mode {:?}", mode));
                return;
            },

            tokenizer::TagToken(x) => TagToken(x),
            tokenizer::CommentToken(x) => CommentToken(x),
            tokenizer::CharacterTokens(x) => CharacterTokens(x),
            tokenizer::EOFToken => EOFToken,
        };

        loop {
            match self.process_local(mode, token) {
                Done => return,
                Reprocess(m, t) => {
                    mode = m;
                    token = t;
                }
            }
        }
    }

    fn process_local(&mut self, mode: states::InsertionMode, token: Token) -> ProcessResult {
        debug!("processing {} in insertion mode {:?}", token, mode);

        match mode {
            states::Initial => match drop_whitespace!(token) {
                CommentToken(text) => {
                    self.sink.append_comment(self.doc_handle.clone(), text);
                    Done
                }
                token => {
                    if !self.opts.iframe_srcdoc {
                        self.sink.parse_error(format!("Bad token in Initial insertion mode: {}", token));
                        self.sink.set_quirks_mode(Quirks);
                    }
                    Reprocess(states::BeforeHtml, token)
                }
            },

            states::BeforeHtml => match drop_whitespace!(token) {
                CommentToken(text) => {
                    self.sink.append_comment(self.doc_handle.clone(), text);
                    Done
                }
                start!(mut t) if named!(t, html) => {
                    self.create_root(take_attrs(t));
                    Done
                }
                end!(t) if !named!(t, head body html br) => {
                    self.sink.parse_error(format!("Unexpected end tag in BeforeHtml mode: {}", t));
                    Done
                }
                token => {
                    self.create_root(vec!());
                    Reprocess(states::BeforeHead, token)
                }
            },

            states::BeforeHead => match drop_whitespace!(token) {
                CommentToken(text) => {
                    let target = self.target();
                    self.sink.append_comment(target, text);
                    Done
                }
                end!(t) if !named!(t, head body html br) => {
                    self.sink.parse_error(format!("Unexpected end tag in BeforeHead mode: {}", t));
                    Done
                }
                start!(mut t) if named!(t, head) => {
                    self.head_elem = Some(self.create_element(atom!(head), take_attrs(t)));
                    self.mode = states::InHead;
                    Done
                }
                token => if start_named!(token, html) {
                    // Do this here because we can't move out of `token` when it's borrowed.
                    self.process_local(states::InBody, token)
                } else {
                    self.head_elem = Some(self.create_element(atom!(head), vec!()));
                    Reprocess(states::InHead, token)
                },
            },

              states::InHead
            | states::InHeadNoscript
            | states::AfterHead
            | states::InBody
            | states::Text
            | states::InTable
            | states::InTableText
            | states::InCaption
            | states::InColumnGroup
            | states::InTableBody
            | states::InRow
            | states::InCell
            | states::InSelect
            | states::InSelectInTable
            | states::InTemplate
            | states::AfterBody
            | states::InFrameset
            | states::AfterFrameset
            | states::AfterAfterBody
            | states::AfterAfterFrameset
                => fail!("not implemented"),
        }
    }
}

impl<'sink, Handle: Clone, Sink: TreeSink<Handle>> TokenSink for TreeBuilder<'sink, Handle, Sink> {
    fn process_token(&mut self, token: tokenizer::Token) {
        self.process_in_mode(self.mode, token);
    }
}

test_eq!(drop_not_characters, drop_whitespace(EOFToken), Some(EOFToken))
test_eq!(drop_all_whitespace, drop_whitespace(CharacterTokens("    ".to_strbuf())), None)
test_eq!(drop_some_whitespace,
    drop_whitespace(CharacterTokens("   hello".to_strbuf())),
    Some(CharacterTokens("hello".to_strbuf())))
test_eq!(drop_no_whitespace,
    drop_whitespace(CharacterTokens("hello".to_strbuf())),
    Some(CharacterTokens("hello".to_strbuf())))

#[test]
fn empty_drop_doesnt_reallocate() {
    fn get_ptr(token: &Token) -> *u8 {
        match *token {
            CharacterTokens(ref x) => x.as_slice().as_ptr(),
            _ => fail!("not characters"),
        }
    }

    let x = CharacterTokens("hello".to_strbuf());
    let p = get_ptr(&x);
    assert_eq!(p, get_ptr(&drop_whitespace(x).unwrap()));
}
<|endoftext|>