<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate gcc;
extern crate build_helper;

use std::process::Command;
use std::env;
use std::path::{PathBuf, Path};

use build_helper::output;

fn detect_llvm_link(llvm_config: &Path) -> (&'static str, Option<&'static str>) {
    let mut version_cmd = Command::new(llvm_config);
    version_cmd.arg("--version");
    let version_output = output(&mut version_cmd);
    let mut parts = version_output.split('.').take(2)
        .filter_map(|s| s.parse::<u32>().ok());
    if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
        if major > 3 || (major == 3 && minor >= 9) {
            // Force the link mode we want, preferring static by default, but
            // possibly overridden by `configure --enable-llvm-link-shared`.
            if env::var_os("LLVM_LINK_SHARED").is_some() {
                return ("dylib", Some("--link-shared"));
            } else {
                return ("static", Some("--link-static"));
            }
        } else if major == 3 && minor == 8 {
            // Find out LLVM's default linking mode.
            let mut mode_cmd = Command::new(llvm_config);
            mode_cmd.arg("--shared-mode");
            if output(&mut mode_cmd).trim() == "shared" {
                return ("dylib", None);
            } else {
                return ("static", None);
            }
        }
    }
    ("static", None)
}

fn main() {
    let target = env::var("TARGET").expect("TARGET was not set");
    let llvm_config = env::var_os("LLVM_CONFIG")
        .map(PathBuf::from)
        .unwrap_or_else(|| {
            if let Some(dir) = env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
                let to_test = dir.parent()
                    .unwrap()
                    .parent()
                    .unwrap()
                    .join(&target)
                    .join("llvm/bin/llvm-config");
                if Command::new(&to_test).output().is_ok() {
                    return to_test;
                }
            }
            PathBuf::from("llvm-config")
        });

    println!("cargo:rerun-if-changed={}", llvm_config.display());

    // Test whether we're cross-compiling LLVM. This is a pretty rare case
    // currently where we're producing an LLVM for a different platform than
    // what this build script is currently running on.
    //
    // In that case, there's no guarantee that we can actually run the target,
    // so the build system works around this by giving us the LLVM_CONFIG for
    // the host platform. This only really works if the host LLVM and target
    // LLVM are compiled the same way, but for us that's typically the case.
    //
    // We *want* detect this cross compiling situation by asking llvm-config
    // what it's host-target is. If that's not the TARGET, then we're cross
    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
    // tricks us into thinking we're doing a cross build when we aren't, so
    // havoc ensues.
    //
    // In any case, if we're cross compiling, this generally just means that we
    // can't trust all the output of llvm-config becaues it might be targeted
    // for the host rather than the target. As a result a bunch of blocks below
    // are gated on `if !is_crossed`
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let is_crossed = target != host;

    let optional_components =
        ["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", "systemz", "jsbackend", "msp430",
         "sparc", "nvptx"];

    // FIXME: surely we don't need all these components, right? Stuff like mcjit
    //        or interpreter the compiler itself never uses.
    let required_components = &["ipo",
                                "bitreader",
                                "bitwriter",
                                "linker",
                                "asmparser",
                                "mcjit",
                                "interpreter",
                                "instrumentation"];

    let components = output(Command::new(&llvm_config).arg("--components"));
    let mut components = components.split_whitespace().collect::<Vec<_>>();
    components.retain(|c| optional_components.contains(c) || required_components.contains(c));

    for component in required_components {
        if !components.contains(component) {
            panic!("require llvm component {} but wasn't found", component);
        }
    }

    for component in components.iter() {
        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
    }

    // Link in our own LLVM shims, compiled with the same flags as LLVM
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--cxxflags");
    let cxxflags = output(&mut cmd);
    let mut cfg = gcc::Config::new();
    for flag in cxxflags.split_whitespace() {
        // Ignore flags like `-m64` when we're doing a cross build
        if is_crossed && flag.starts_with("-m") {
            continue;
        }

        // -Wdate-time is not supported by the netbsd cross compiler
        if is_crossed && target.contains("netbsd") && flag.contains("date-time") {
            continue;
        }

        cfg.flag(flag);
    }

    for component in &components {
        let mut flag = String::from("-DLLVM_COMPONENT_");
        flag.push_str(&component.to_uppercase());
        cfg.flag(&flag);
    }

    if env::var_os("LLVM_RUSTLLVM").is_some() {
        cfg.flag("-DLLVM_RUSTLLVM");
    }

    build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm"));
    cfg.file("../rustllvm/PassWrapper.cpp")
       .file("../rustllvm/RustWrapper.cpp")
       .file("../rustllvm/ArchiveWrapper.cpp")
       .cpp(true)
       .cpp_link_stdlib(None) // we handle this below
       .compile("librustllvm.a");

    let (llvm_kind, llvm_link_arg) = detect_llvm_link(&llvm_config);

    // Link in all LLVM libraries, if we're uwring the "wrong" llvm-config then
    // we don't pick up system libs because unfortunately they're for the host
    // of llvm-config, not the target that we're attempting to link.
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--libs");

    if let Some(link_arg) = llvm_link_arg {
        cmd.arg(link_arg);
    }

    if !is_crossed {
        cmd.arg("--system-libs");
    }
    cmd.args(&components);

    for lib in output(&mut cmd).split_whitespace() {
        let name = if lib.starts_with("-l") {
            &lib[2..]
        } else if lib.starts_with("-") {
            &lib[1..]
        } else if Path::new(lib).exists() {
            // On MSVC llvm-config will print the full name to libraries, but
            // we're only interested in the name part
            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
            name.trim_right_matches(".lib")
        } else if lib.ends_with(".lib") {
            // Some MSVC libraries just come up with `.lib` tacked on, so chop
            // that off
            lib.trim_right_matches(".lib")
        } else {
            continue;
        };

        // Don't need or want this library, but LLVM's CMake build system
        // doesn't provide a way to disable it, so filter it here even though we
        // may or may not have built it. We don't reference anything from this
        // library and it otherwise may just pull in extra dependencies on
        // libedit which we don't want
        if name == "LLVMLineEditor" {
            continue;
        }

        let kind = if name.starts_with("LLVM") {
            llvm_kind
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, name);
    }

    // LLVM ldflags
    //
    // If we're a cross-compile of LLVM then unfortunately we can't trust these
    // ldflags (largely where all the LLVM libs are located). Currently just
    // hack around this by replacing the host triple with the target and pray
    // that those -L directories are the same!
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--ldflags");
    for lib in output(&mut cmd).split_whitespace() {
        if lib.starts_with("-LIBPATH:") {
            println!("cargo:rustc-link-search=native={}", &lib[9..]);
        } else if is_crossed {
            if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}",
                         lib[2..].replace(&host, &target));
            }
        } else if lib.starts_with("-l") {
            println!("cargo:rustc-link-lib={}", &lib[2..]);
        } else if lib.starts_with("-L") {
            println!("cargo:rustc-link-search=native={}", &lib[2..]);
        }
    }

    let llvm_static_stdcpp = env::var_os("LLVM_STATIC_STDCPP");

    let stdcppname = if target.contains("openbsd") {
        // OpenBSD has a particular C++ runtime library name
        "estdc++"
    } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
        // NetBSD uses a separate library when relocation is required
        "stdc++_pic"
    } else {
        "stdc++"
    };

    // C++ runtime library
    if !target.contains("msvc") {
        if let Some(s) = llvm_static_stdcpp {
            assert!(!cxxflags.contains("stdlib=libc++"));
            let path = PathBuf::from(s);
            println!("cargo:rustc-link-search=native={}",
                     path.parent().unwrap().display());
            println!("cargo:rustc-link-lib=static={}", stdcppname);
        } else if cxxflags.contains("stdlib=libc++") {
            println!("cargo:rustc-link-lib=c++");
        } else {
            println!("cargo:rustc-link-lib={}", stdcppname);
        }
    }

    // LLVM requires symbols from this library, but apparently they're not printeds
    // during llvm-config?
    if target.contains("windows") {
        println!("cargo:rustc-link-lib=ole32");
    }
}
<commit_msg>Specify type libraries for llvm-config --ldflags<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

extern crate gcc;
extern crate build_helper;

use std::process::Command;
use std::env;
use std::path::{PathBuf, Path};

use build_helper::output;

fn detect_llvm_link(llvm_config: &Path) -> (&'static str, Option<&'static str>) {
    let mut version_cmd = Command::new(llvm_config);
    version_cmd.arg("--version");
    let version_output = output(&mut version_cmd);
    let mut parts = version_output.split('.').take(2)
        .filter_map(|s| s.parse::<u32>().ok());
    if let (Some(major), Some(minor)) = (parts.next(), parts.next()) {
        if major > 3 || (major == 3 && minor >= 9) {
            // Force the link mode we want, preferring static by default, but
            // possibly overridden by `configure --enable-llvm-link-shared`.
            if env::var_os("LLVM_LINK_SHARED").is_some() {
                return ("dylib", Some("--link-shared"));
            } else {
                return ("static", Some("--link-static"));
            }
        } else if major == 3 && minor == 8 {
            // Find out LLVM's default linking mode.
            let mut mode_cmd = Command::new(llvm_config);
            mode_cmd.arg("--shared-mode");
            if output(&mut mode_cmd).trim() == "shared" {
                return ("dylib", None);
            } else {
                return ("static", None);
            }
        }
    }
    ("static", None)
}

fn main() {
    let target = env::var("TARGET").expect("TARGET was not set");
    let llvm_config = env::var_os("LLVM_CONFIG")
        .map(PathBuf::from)
        .unwrap_or_else(|| {
            if let Some(dir) = env::var_os("CARGO_TARGET_DIR").map(PathBuf::from) {
                let to_test = dir.parent()
                    .unwrap()
                    .parent()
                    .unwrap()
                    .join(&target)
                    .join("llvm/bin/llvm-config");
                if Command::new(&to_test).output().is_ok() {
                    return to_test;
                }
            }
            PathBuf::from("llvm-config")
        });

    println!("cargo:rerun-if-changed={}", llvm_config.display());

    // Test whether we're cross-compiling LLVM. This is a pretty rare case
    // currently where we're producing an LLVM for a different platform than
    // what this build script is currently running on.
    //
    // In that case, there's no guarantee that we can actually run the target,
    // so the build system works around this by giving us the LLVM_CONFIG for
    // the host platform. This only really works if the host LLVM and target
    // LLVM are compiled the same way, but for us that's typically the case.
    //
    // We *want* detect this cross compiling situation by asking llvm-config
    // what it's host-target is. If that's not the TARGET, then we're cross
    // compiling. Unfortunately `llvm-config` seems either be buggy, or we're
    // misconfiguring it, because the `i686-pc-windows-gnu` build of LLVM will
    // report itself with a `--host-target` of `x86_64-pc-windows-gnu`. This
    // tricks us into thinking we're doing a cross build when we aren't, so
    // havoc ensues.
    //
    // In any case, if we're cross compiling, this generally just means that we
    // can't trust all the output of llvm-config becaues it might be targeted
    // for the host rather than the target. As a result a bunch of blocks below
    // are gated on `if !is_crossed`
    let target = env::var("TARGET").expect("TARGET was not set");
    let host = env::var("HOST").expect("HOST was not set");
    let is_crossed = target != host;

    let optional_components =
        ["x86", "arm", "aarch64", "mips", "powerpc", "pnacl", "systemz", "jsbackend", "msp430",
         "sparc", "nvptx"];

    // FIXME: surely we don't need all these components, right? Stuff like mcjit
    //        or interpreter the compiler itself never uses.
    let required_components = &["ipo",
                                "bitreader",
                                "bitwriter",
                                "linker",
                                "asmparser",
                                "mcjit",
                                "interpreter",
                                "instrumentation"];

    let components = output(Command::new(&llvm_config).arg("--components"));
    let mut components = components.split_whitespace().collect::<Vec<_>>();
    components.retain(|c| optional_components.contains(c) || required_components.contains(c));

    for component in required_components {
        if !components.contains(component) {
            panic!("require llvm component {} but wasn't found", component);
        }
    }

    for component in components.iter() {
        println!("cargo:rustc-cfg=llvm_component=\"{}\"", component);
    }

    // Link in our own LLVM shims, compiled with the same flags as LLVM
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--cxxflags");
    let cxxflags = output(&mut cmd);
    let mut cfg = gcc::Config::new();
    for flag in cxxflags.split_whitespace() {
        // Ignore flags like `-m64` when we're doing a cross build
        if is_crossed && flag.starts_with("-m") {
            continue;
        }

        // -Wdate-time is not supported by the netbsd cross compiler
        if is_crossed && target.contains("netbsd") && flag.contains("date-time") {
            continue;
        }

        cfg.flag(flag);
    }

    for component in &components {
        let mut flag = String::from("-DLLVM_COMPONENT_");
        flag.push_str(&component.to_uppercase());
        cfg.flag(&flag);
    }

    if env::var_os("LLVM_RUSTLLVM").is_some() {
        cfg.flag("-DLLVM_RUSTLLVM");
    }

    build_helper::rerun_if_changed_anything_in_dir(Path::new("../rustllvm"));
    cfg.file("../rustllvm/PassWrapper.cpp")
       .file("../rustllvm/RustWrapper.cpp")
       .file("../rustllvm/ArchiveWrapper.cpp")
       .cpp(true)
       .cpp_link_stdlib(None) // we handle this below
       .compile("librustllvm.a");

    let (llvm_kind, llvm_link_arg) = detect_llvm_link(&llvm_config);

    // Link in all LLVM libraries, if we're uwring the "wrong" llvm-config then
    // we don't pick up system libs because unfortunately they're for the host
    // of llvm-config, not the target that we're attempting to link.
    let mut cmd = Command::new(&llvm_config);
    cmd.arg("--libs");

    if let Some(link_arg) = llvm_link_arg {
        cmd.arg(link_arg);
    }

    if !is_crossed {
        cmd.arg("--system-libs");
    }
    cmd.args(&components);

    for lib in output(&mut cmd).split_whitespace() {
        let name = if lib.starts_with("-l") {
            &lib[2..]
        } else if lib.starts_with("-") {
            &lib[1..]
        } else if Path::new(lib).exists() {
            // On MSVC llvm-config will print the full name to libraries, but
            // we're only interested in the name part
            let name = Path::new(lib).file_name().unwrap().to_str().unwrap();
            name.trim_right_matches(".lib")
        } else if lib.ends_with(".lib") {
            // Some MSVC libraries just come up with `.lib` tacked on, so chop
            // that off
            lib.trim_right_matches(".lib")
        } else {
            continue;
        };

        // Don't need or want this library, but LLVM's CMake build system
        // doesn't provide a way to disable it, so filter it here even though we
        // may or may not have built it. We don't reference anything from this
        // library and it otherwise may just pull in extra dependencies on
        // libedit which we don't want
        if name == "LLVMLineEditor" {
            continue;
        }

        let kind = if name.starts_with("LLVM") {
            llvm_kind
        } else {
            "dylib"
        };
        println!("cargo:rustc-link-lib={}={}", kind, name);
    }

    // LLVM ldflags
    //
    // If we're a cross-compile of LLVM then unfortunately we can't trust these
    // ldflags (largely where all the LLVM libs are located). Currently just
    // hack around this by replacing the host triple with the target and pray
    // that those -L directories are the same!
    let mut cmd = Command::new(&llvm_config);
    if let Some(link_arg) = llvm_link_arg {
        cmd.arg(link_arg);
    }
    cmd.arg("--ldflags");
    for lib in output(&mut cmd).split_whitespace() {
        if lib.starts_with("-LIBPATH:") {
            println!("cargo:rustc-link-search=native={}", &lib[9..]);
        } else if is_crossed {
            if lib.starts_with("-L") {
                println!("cargo:rustc-link-search=native={}",
                         lib[2..].replace(&host, &target));
            }
        } else if lib.starts_with("-l") {
            println!("cargo:rustc-link-lib={}", &lib[2..]);
        } else if lib.starts_with("-L") {
            println!("cargo:rustc-link-search=native={}", &lib[2..]);
        }
    }

    let llvm_static_stdcpp = env::var_os("LLVM_STATIC_STDCPP");

    let stdcppname = if target.contains("openbsd") {
        // OpenBSD has a particular C++ runtime library name
        "estdc++"
    } else if target.contains("netbsd") && llvm_static_stdcpp.is_some() {
        // NetBSD uses a separate library when relocation is required
        "stdc++_pic"
    } else {
        "stdc++"
    };

    // C++ runtime library
    if !target.contains("msvc") {
        if let Some(s) = llvm_static_stdcpp {
            assert!(!cxxflags.contains("stdlib=libc++"));
            let path = PathBuf::from(s);
            println!("cargo:rustc-link-search=native={}",
                     path.parent().unwrap().display());
            println!("cargo:rustc-link-lib=static={}", stdcppname);
        } else if cxxflags.contains("stdlib=libc++") {
            println!("cargo:rustc-link-lib=c++");
        } else {
            println!("cargo:rustc-link-lib={}", stdcppname);
        }
    }

    // LLVM requires symbols from this library, but apparently they're not printeds
    // during llvm-config?
    if target.contains("windows") {
        println!("cargo:rustc-link-lib=ole32");
    }
}
<|endoftext|>


<commit_before>use std::borrow::IntoCow;
use syntax::ast::{Expr, Ident, Stmt};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::ptr::P;

use maud;

#[derive(Copy)]
pub enum Escape {
    PassThru,
    Escape,
}

pub struct Renderer<'cx, 's: 'cx> {
    pub cx: &'cx ExtCtxt<'s>,
    stmts: Vec<P<Stmt>>,
    w: Ident,
}

impl<'cx, 's> Renderer<'cx, 's> {
    /// Create a new `Renderer` using the given extension context.
    pub fn new(cx: &'cx ExtCtxt<'s>) -> Renderer<'cx, 's> {
        Renderer {
            cx: cx,
            stmts: vec![],
            w: Ident::new(token::intern("w")),
        }
    }

    /// Create a new `Renderer` under the same context as `self`.
    pub fn fork(&self) -> Renderer<'cx, 's> {
        Renderer {
            cx: self.cx,
            stmts: vec![],
            w: self.w,
        }
    }

    /// Reify the `Renderer` into a block of markup.
    pub fn into_expr(self) -> P<Expr> {
        let Renderer { cx, stmts, w } = self;
        quote_expr!(cx,
            ::maud::rt::make_markup(|$w: &mut ::std::fmt::Write| -> Result<(), ::std::fmt::Error> {
                $stmts
                Ok(())
            }))
    }

    /// Reify the `Renderer` into a raw list of statements.
    pub fn into_stmts(self) -> Vec<P<Stmt>> {
        let Renderer { stmts, .. } = self;
        stmts
    }

    /// Append the list of statements to the output.
    pub fn push_stmts(&mut self, mut stmts: Vec<P<Stmt>>) {
        self.stmts.append(&mut stmts);
    }

    /// Push an expression statement, also wrapping it with `try!`.
    fn push_try(&mut self, expr: P<Expr>) {
        let stmt = self.cx.stmt_expr(self.cx.expr_try(expr.span, expr));
        self.stmts.push(stmt);
    }

    /// Append a literal pre-escaped string.
    fn write(&mut self, s: &str) {
        let w = self.w;
        let expr = quote_expr!(self.cx, $w.write_str($s));
        self.push_try(expr);
    }

    /// Append a literal string, with the specified escaping method.
    pub fn string(&mut self, s: &str, escape: Escape) {
        let s = match escape {
            Escape::PassThru => s.into_cow(),
            Escape::Escape => maud::escape(s).into_cow(),
        };
        self.write(&s);
    }

    /// Append the result of an expression, with the specified escaping method.
    pub fn splice(&mut self, expr: P<Expr>, escape: Escape) {
        let w = self.w;
        let expr = match escape {
            Escape::PassThru =>
                quote_expr!(self.cx, ::maud::rt::write_fmt($w, $expr)),
            Escape::Escape =>
                quote_expr!(self.cx,
                    ::maud::rt::write_fmt(
                        &mut ::maud::rt::Escaper { inner: $w },
                        $expr)),
        };
        self.push_try(expr);
    }

    pub fn element_open_start(&mut self, name: &str) {
        self.write("<");
        self.write(name);
    }

    pub fn attribute_start(&mut self, name: &str) {
        self.write(" ");
        self.write(name);
        self.write("=\"");
    }

    pub fn attribute_empty(&mut self, name: &str) {
        self.write(" ");
        self.write(name);
    }

    pub fn attribute_end(&mut self) {
        self.write("\"");
    }

    pub fn element_open_end(&mut self) {
        self.write(">");
    }

    pub fn element_close(&mut self, name: &str) {
        self.write("</");
        self.write(name);
        self.write(">");
    }

    pub fn emit_if(&mut self, cond: P<Expr>, if_body: Vec<P<Stmt>>,
                   else_body: Option<Vec<P<Stmt>>>) {
        let stmt = match else_body {
            None => quote_stmt!(self.cx, if $cond { $if_body }),
            Some(else_body) =>
                quote_stmt!(self.cx, if $cond { $if_body } else { $else_body }),
        };
        self.stmts.push(stmt);
    }
}
<commit_msg>Remove cow<commit_after>use syntax::ast::{Expr, Ident, Stmt};
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::parse::token;
use syntax::ptr::P;

use maud;

#[derive(Copy)]
pub enum Escape {
    PassThru,
    Escape,
}

pub struct Renderer<'cx, 's: 'cx> {
    pub cx: &'cx ExtCtxt<'s>,
    stmts: Vec<P<Stmt>>,
    w: Ident,
}

impl<'cx, 's> Renderer<'cx, 's> {
    /// Create a new `Renderer` using the given extension context.
    pub fn new(cx: &'cx ExtCtxt<'s>) -> Renderer<'cx, 's> {
        Renderer {
            cx: cx,
            stmts: vec![],
            w: Ident::new(token::intern("w")),
        }
    }

    /// Create a new `Renderer` under the same context as `self`.
    pub fn fork(&self) -> Renderer<'cx, 's> {
        Renderer {
            cx: self.cx,
            stmts: vec![],
            w: self.w,
        }
    }

    /// Reify the `Renderer` into a block of markup.
    pub fn into_expr(self) -> P<Expr> {
        let Renderer { cx, stmts, w } = self;
        quote_expr!(cx,
            ::maud::rt::make_markup(|$w: &mut ::std::fmt::Write| -> Result<(), ::std::fmt::Error> {
                $stmts
                Ok(())
            }))
    }

    /// Reify the `Renderer` into a raw list of statements.
    pub fn into_stmts(self) -> Vec<P<Stmt>> {
        let Renderer { stmts, .. } = self;
        stmts
    }

    /// Append the list of statements to the output.
    pub fn push_stmts(&mut self, mut stmts: Vec<P<Stmt>>) {
        self.stmts.append(&mut stmts);
    }

    /// Push an expression statement, also wrapping it with `try!`.
    fn push_try(&mut self, expr: P<Expr>) {
        let stmt = self.cx.stmt_expr(self.cx.expr_try(expr.span, expr));
        self.stmts.push(stmt);
    }

    /// Append a literal pre-escaped string.
    fn write(&mut self, s: &str) {
        let w = self.w;
        let expr = quote_expr!(self.cx, $w.write_str($s));
        self.push_try(expr);
    }

    /// Append a literal string, with the specified escaping method.
    pub fn string(&mut self, s: &str, escape: Escape) {
        let escaped;
        let s = match escape {
            Escape::PassThru => s,
            Escape::Escape => { escaped = maud::escape(s); &*escaped },
        };
        self.write(s);
    }

    /// Append the result of an expression, with the specified escaping method.
    pub fn splice(&mut self, expr: P<Expr>, escape: Escape) {
        let w = self.w;
        let expr = match escape {
            Escape::PassThru =>
                quote_expr!(self.cx, ::maud::rt::write_fmt($w, $expr)),
            Escape::Escape =>
                quote_expr!(self.cx,
                    ::maud::rt::write_fmt(
                        &mut ::maud::rt::Escaper { inner: $w },
                        $expr)),
        };
        self.push_try(expr);
    }

    pub fn element_open_start(&mut self, name: &str) {
        self.write("<");
        self.write(name);
    }

    pub fn attribute_start(&mut self, name: &str) {
        self.write(" ");
        self.write(name);
        self.write("=\"");
    }

    pub fn attribute_empty(&mut self, name: &str) {
        self.write(" ");
        self.write(name);
    }

    pub fn attribute_end(&mut self) {
        self.write("\"");
    }

    pub fn element_open_end(&mut self) {
        self.write(">");
    }

    pub fn element_close(&mut self, name: &str) {
        self.write("</");
        self.write(name);
        self.write(">");
    }

    pub fn emit_if(&mut self, cond: P<Expr>, if_body: Vec<P<Stmt>>,
                   else_body: Option<Vec<P<Stmt>>>) {
        let stmt = match else_body {
            None => quote_stmt!(self.cx, if $cond { $if_body }),
            Some(else_body) =>
                quote_stmt!(self.cx, if $cond { $if_body } else { $else_body }),
        };
        self.stmts.push(stmt);
    }
}
<|endoftext|>


<commit_before>use common::*;
use la;

use std::libc::{c_ulong};
use std::ptr;
use std::iter::{range_inclusive};
use std::cast::transmute;
use extra::c_vec::CVec;

/// Column major dense matrix type.
pub struct DenseMatrix {
  priv data: CVec<R>,
  priv num_rows: uint,
  priv num_cols: uint,
  priv capacity_cols: uint,
}

impl DenseMatrix {

  pub fn from_fn(num_rows: uint, num_cols: uint, f: |row:uint, col:uint| -> R) -> DenseMatrix {
    let n = num_rows * num_cols;
    let mut data = unsafe { alloc_data(n) };
    for i in range(0u, n) {
      let c = i / num_rows;
      let r = i % num_rows;
      unsafe { unsafe_set(&mut data, i, f(r,c)); }
    }
    DenseMatrix {
      data: data,
      num_rows: num_rows,
      num_cols: num_cols,
      capacity_cols: num_cols,
    }
  }

  // Create dense matrix of indicated size, with unitialized entries.  This constructor should only be used if the
  // part(s) of the matrix to be used are subsequently initialized, such as via the set() function.
  pub fn of_size(num_rows: uint, num_cols: uint) -> DenseMatrix {
    let n = num_rows * num_cols;
    let mut data = unsafe { alloc_data(n) };
    DenseMatrix {
      data: data,
      num_rows: num_rows,
      num_cols: num_cols,
      capacity_cols: num_cols,
    }
  }

  pub fn from_elem(num_rows: uint, num_cols: uint, elem: R) -> DenseMatrix {
    let n = num_rows * num_cols;
    let mut data = unsafe { alloc_data(n) };
    for i in range(0u, n) {
      unsafe { unsafe_set(&mut data, i, elem); }
    }
    DenseMatrix {
      data: data,
      num_rows: num_rows,
      num_cols: num_cols,
      capacity_cols: num_cols,
    }
  }

  pub fn from_elem_with_cols_capacity(num_rows: uint, num_cols: uint, elem: R, capacity_cols: uint) -> DenseMatrix {
    if capacity_cols < num_cols { fail!("Capacity columns must be greater or equal to number of columns."); }
    let cap = num_rows * capacity_cols;
    let mut data = unsafe { alloc_data(cap) };
    for i in range(0u, cap) {
      unsafe { unsafe_set(&mut data, i, elem); }
    }
    DenseMatrix {
      data: data,
      num_rows: num_rows,
      num_cols: num_cols,
      capacity_cols: capacity_cols,
    }
  }

  pub fn of_size_with_cols_capacity(num_rows: uint, num_cols: uint, capacity_cols: uint) -> DenseMatrix {
    if capacity_cols < num_cols { fail!("Capacity columns must be greater or equal to number of columns."); }
    let data = unsafe { alloc_data(num_rows * capacity_cols) };
    DenseMatrix {
      data: data,
      num_rows: num_rows,
      num_cols: num_cols,
      capacity_cols: capacity_cols,
    }
  }

  // NOTE: This constructor does not initialize the lower triangular values.
  pub fn upper_triangle_from_fn(side_len: uint, f: |row:uint, col:uint| -> R) -> DenseMatrix {
    let n = side_len * side_len;
    let mut data = unsafe { alloc_data(n) };
    for c in range(0, side_len) {
      for r in range_inclusive(0, c) {
        unsafe { unsafe_set(&mut data, c * side_len + r, f(r,c)); }
      }
    }
    DenseMatrix {
      data: data,
      num_rows: side_len,
      num_cols: side_len,
      capacity_cols: side_len,
    }
  }

  // NOTE: This constructor does not initialize the upper triangular values.
  pub fn lower_triangle_from_fn(side_len: uint, f: |row:uint, col:uint| -> R) -> DenseMatrix {
    let n = side_len * side_len;
    let mut data = unsafe { alloc_data(n) };
    for r in range(0, side_len) {
      for c in range_inclusive(0, r) {
        unsafe { unsafe_set(&mut data, c * side_len + r, f(r,c)); }
      }
    }
    DenseMatrix {
      data: data,
      num_rows: side_len,
      num_cols: side_len,
      capacity_cols: side_len,
    }
  }

  pub fn from_rows(num_rows: uint, num_cols: uint, elems: &[~[R]]) -> DenseMatrix {
    DenseMatrix::from_fn(num_rows, num_cols, |r,c| elems[r][c])
  }

  #[inline(always)]
  pub fn num_rows(&self) -> uint {
    self.num_rows
  }

  #[inline(always)]
  pub fn num_cols(&self) -> uint {
    self.num_cols
  }

  #[inline(always)]
  pub fn capacity_cols(&self) -> uint {
    self.capacity_cols
  }

  #[inline]
  pub fn get(&self, r: uint, c: uint) -> R {
    if c >= self.num_cols || r >= self.num_rows { fail!("Row or column out of range."); }
    unsafe { unsafe_get(&self.data, c * self.num_rows + r) }
  }

  #[inline]
  pub fn set(&mut self, r: uint, c: uint, value: R) {
    if c >= self.num_cols || r >= self.num_rows { fail!("Row or column out of range."); }
    unsafe { unsafe_set(&mut self.data, c * self.num_rows + r, value); }
  }

  #[inline(never)]
  pub fn copy_into(&self, m: &mut DenseMatrix) {
    if self.num_rows != m.num_rows || self.num_cols > m.num_cols {
      fail!("Matrix layouts not compatible for dense matrix copy-into operation.");
    }
    unsafe {
      la::copy_matrix(transmute(self.data.get(0)), self.num_rows as c_ulong, self.num_cols as c_ulong, transmute(m.data.get(0)));
    }
  }

  #[inline(never)]
  pub fn copy_upper_triangle_into(&self, m: &mut DenseMatrix) {
    if self.num_rows != m.num_rows || self.num_cols != m.num_cols {
      fail!("Matrix layouts not compatible for dense matrix copy-into operation.");
    }
    unsafe {
      la::copy_upper_triangle(transmute(self.data.get(0)), self.num_rows as c_ulong, self.num_cols as c_ulong, transmute(m.data.get(0)));
    }
  }

  #[inline(never)]
  pub fn fill_upper_triangle_from(&mut self, m: &DenseMatrix) {
    if self.num_rows != m.num_rows || m.num_cols > self.num_cols {
      fail!("Matrix layouts not compatible for dense matrix copy-into operation.");
    }
    unsafe {
      la::copy_upper_triangle(transmute(m.data.get(0)), m.num_rows as c_ulong, m.num_cols as c_ulong, transmute(self.data.get(0)));
    }
  }

  #[inline(never)]
  pub fn fill_from_fn(&mut self, f: |row:uint, col:uint| -> R) {
    for r in range(0, self.num_rows) {
      for c in range(0, self.num_cols) {
        unsafe { unsafe_set(&mut self.data, c * self.num_rows + r, f(r,c)); }
      }
    }
  }

  #[inline(always)]
  pub unsafe fn col_maj_data_ptr(&self) -> *R {
    transmute(self.data.get(0))
  }

  #[inline(always)]
  pub unsafe fn mut_col_maj_data_ptr(&self) -> *mut R {
    transmute(self.data.get(0))
  }

  pub fn set_num_cols(&mut self, num_cols: uint) {
    if num_cols > self.capacity_cols  {
      fail!("Cannot increase number of columns beyond capacity.");
    }
    self.num_cols = num_cols;
  }

  pub fn print(&self) {
    for i in range(0, self.num_rows) {
      for j in range(0, self.num_cols) {
        print!(" {:6.3f} ", self.get(i,j));
      }
      println!("");
    }
  }

} // DenseMatrix impl

#[inline(always)]
unsafe fn unsafe_set(v: &mut CVec<R>, i: uint, value: R) {
  *ptr::mut_offset(transmute(v.get(0)), i as int) = value;
}

#[inline(always)]
unsafe fn unsafe_get(v: &CVec<R>, i: uint) -> R {
  *ptr::mut_offset(transmute(v.get(0)), i as int)
}

#[inline(never)]
pub unsafe fn alloc_data(num_doubles: uint) -> CVec<R> {
  let doubles = la::alloc_doubles(num_doubles as c_ulong);
  CVec::new(doubles, num_doubles)
}

#[unsafe_destructor]
impl Drop for DenseMatrix {
  #[inline(never)]
  fn drop(&mut self) {
    unsafe {
      la::free_doubles(transmute(self.data.get(0)))
    }
  }
}

<commit_msg>Remove unnecessary mut.<commit_after>use common::*;
use la;

use std::libc::{c_ulong};
use std::ptr;
use std::iter::{range_inclusive};
use std::cast::transmute;
use extra::c_vec::CVec;

/// Column major dense matrix type.
pub struct DenseMatrix {
  priv data: CVec<R>,
  priv num_rows: uint,
  priv num_cols: uint,
  priv capacity_cols: uint,
}

impl DenseMatrix {

  pub fn from_fn(num_rows: uint, num_cols: uint, f: |row:uint, col:uint| -> R) -> DenseMatrix {
    let n = num_rows * num_cols;
    let mut data = unsafe { alloc_data(n) };
    for i in range(0u, n) {
      let c = i / num_rows;
      let r = i % num_rows;
      unsafe { unsafe_set(&mut data, i, f(r,c)); }
    }
    DenseMatrix {
      data: data,
      num_rows: num_rows,
      num_cols: num_cols,
      capacity_cols: num_cols,
    }
  }

  // Create dense matrix of indicated size, with unitialized entries.  This constructor should only be used if the
  // part(s) of the matrix to be used are subsequently initialized, such as via the set() function.
  pub fn of_size(num_rows: uint, num_cols: uint) -> DenseMatrix {
    let n = num_rows * num_cols;
    let data = unsafe { alloc_data(n) };
    DenseMatrix {
      data: data,
      num_rows: num_rows,
      num_cols: num_cols,
      capacity_cols: num_cols,
    }
  }

  pub fn from_elem(num_rows: uint, num_cols: uint, elem: R) -> DenseMatrix {
    let n = num_rows * num_cols;
    let mut data = unsafe { alloc_data(n) };
    for i in range(0u, n) {
      unsafe { unsafe_set(&mut data, i, elem); }
    }
    DenseMatrix {
      data: data,
      num_rows: num_rows,
      num_cols: num_cols,
      capacity_cols: num_cols,
    }
  }

  pub fn from_elem_with_cols_capacity(num_rows: uint, num_cols: uint, elem: R, capacity_cols: uint) -> DenseMatrix {
    if capacity_cols < num_cols { fail!("Capacity columns must be greater or equal to number of columns."); }
    let cap = num_rows * capacity_cols;
    let mut data = unsafe { alloc_data(cap) };
    for i in range(0u, cap) {
      unsafe { unsafe_set(&mut data, i, elem); }
    }
    DenseMatrix {
      data: data,
      num_rows: num_rows,
      num_cols: num_cols,
      capacity_cols: capacity_cols,
    }
  }

  pub fn of_size_with_cols_capacity(num_rows: uint, num_cols: uint, capacity_cols: uint) -> DenseMatrix {
    if capacity_cols < num_cols { fail!("Capacity columns must be greater or equal to number of columns."); }
    let data = unsafe { alloc_data(num_rows * capacity_cols) };
    DenseMatrix {
      data: data,
      num_rows: num_rows,
      num_cols: num_cols,
      capacity_cols: capacity_cols,
    }
  }

  // NOTE: This constructor does not initialize the lower triangular values.
  pub fn upper_triangle_from_fn(side_len: uint, f: |row:uint, col:uint| -> R) -> DenseMatrix {
    let n = side_len * side_len;
    let mut data = unsafe { alloc_data(n) };
    for c in range(0, side_len) {
      for r in range_inclusive(0, c) {
        unsafe { unsafe_set(&mut data, c * side_len + r, f(r,c)); }
      }
    }
    DenseMatrix {
      data: data,
      num_rows: side_len,
      num_cols: side_len,
      capacity_cols: side_len,
    }
  }

  // NOTE: This constructor does not initialize the upper triangular values.
  pub fn lower_triangle_from_fn(side_len: uint, f: |row:uint, col:uint| -> R) -> DenseMatrix {
    let n = side_len * side_len;
    let mut data = unsafe { alloc_data(n) };
    for r in range(0, side_len) {
      for c in range_inclusive(0, r) {
        unsafe { unsafe_set(&mut data, c * side_len + r, f(r,c)); }
      }
    }
    DenseMatrix {
      data: data,
      num_rows: side_len,
      num_cols: side_len,
      capacity_cols: side_len,
    }
  }

  pub fn from_rows(num_rows: uint, num_cols: uint, elems: &[~[R]]) -> DenseMatrix {
    DenseMatrix::from_fn(num_rows, num_cols, |r,c| elems[r][c])
  }

  #[inline(always)]
  pub fn num_rows(&self) -> uint {
    self.num_rows
  }

  #[inline(always)]
  pub fn num_cols(&self) -> uint {
    self.num_cols
  }

  #[inline(always)]
  pub fn capacity_cols(&self) -> uint {
    self.capacity_cols
  }

  #[inline]
  pub fn get(&self, r: uint, c: uint) -> R {
    if c >= self.num_cols || r >= self.num_rows { fail!("Row or column out of range."); }
    unsafe { unsafe_get(&self.data, c * self.num_rows + r) }
  }

  #[inline]
  pub fn set(&mut self, r: uint, c: uint, value: R) {
    if c >= self.num_cols || r >= self.num_rows { fail!("Row or column out of range."); }
    unsafe { unsafe_set(&mut self.data, c * self.num_rows + r, value); }
  }

  #[inline(never)]
  pub fn copy_into(&self, m: &mut DenseMatrix) {
    if self.num_rows != m.num_rows || self.num_cols > m.num_cols {
      fail!("Matrix layouts not compatible for dense matrix copy-into operation.");
    }
    unsafe {
      la::copy_matrix(transmute(self.data.get(0)), self.num_rows as c_ulong, self.num_cols as c_ulong, transmute(m.data.get(0)));
    }
  }

  #[inline(never)]
  pub fn copy_upper_triangle_into(&self, m: &mut DenseMatrix) {
    if self.num_rows != m.num_rows || self.num_cols != m.num_cols {
      fail!("Matrix layouts not compatible for dense matrix copy-into operation.");
    }
    unsafe {
      la::copy_upper_triangle(transmute(self.data.get(0)), self.num_rows as c_ulong, self.num_cols as c_ulong, transmute(m.data.get(0)));
    }
  }

  #[inline(never)]
  pub fn fill_upper_triangle_from(&mut self, m: &DenseMatrix) {
    if self.num_rows != m.num_rows || m.num_cols > self.num_cols {
      fail!("Matrix layouts not compatible for dense matrix copy-into operation.");
    }
    unsafe {
      la::copy_upper_triangle(transmute(m.data.get(0)), m.num_rows as c_ulong, m.num_cols as c_ulong, transmute(self.data.get(0)));
    }
  }

  #[inline(never)]
  pub fn fill_from_fn(&mut self, f: |row:uint, col:uint| -> R) {
    for r in range(0, self.num_rows) {
      for c in range(0, self.num_cols) {
        unsafe { unsafe_set(&mut self.data, c * self.num_rows + r, f(r,c)); }
      }
    }
  }

  #[inline(always)]
  pub unsafe fn col_maj_data_ptr(&self) -> *R {
    transmute(self.data.get(0))
  }

  #[inline(always)]
  pub unsafe fn mut_col_maj_data_ptr(&self) -> *mut R {
    transmute(self.data.get(0))
  }

  pub fn set_num_cols(&mut self, num_cols: uint) {
    if num_cols > self.capacity_cols  {
      fail!("Cannot increase number of columns beyond capacity.");
    }
    self.num_cols = num_cols;
  }

  pub fn print(&self) {
    for i in range(0, self.num_rows) {
      for j in range(0, self.num_cols) {
        print!(" {:6.3f} ", self.get(i,j));
      }
      println!("");
    }
  }

} // DenseMatrix impl

#[inline(always)]
unsafe fn unsafe_set(v: &mut CVec<R>, i: uint, value: R) {
  *ptr::mut_offset(transmute(v.get(0)), i as int) = value;
}

#[inline(always)]
unsafe fn unsafe_get(v: &CVec<R>, i: uint) -> R {
  *ptr::mut_offset(transmute(v.get(0)), i as int)
}

#[inline(never)]
pub unsafe fn alloc_data(num_doubles: uint) -> CVec<R> {
  let doubles = la::alloc_doubles(num_doubles as c_ulong);
  CVec::new(doubles, num_doubles)
}

#[unsafe_destructor]
impl Drop for DenseMatrix {
  #[inline(never)]
  fn drop(&mut self) {
    unsafe {
      la::free_doubles(transmute(self.data.get(0)))
    }
  }
}

<|endoftext|>


<commit_before>//! Provides a software renderer that can be used without a GPU. The rendering
//! is much slower than with a normal GPU, but might be sufficient for
//! situations where you want to create a screenshot of the layout.

#[cfg(all(
    test,

    // Without MMX the floating point calculations don't follow IEEE 754. So the
    // tests fail to produce the correct checksums.
    // FIXME: We use SSE as an approximation for the cfg, because MMX isn't
    // supported by Rust just yet.
    not(all(
        target_arch = "x86",
        not(target_feature = "sse"),
    )),
))]
mod tests;

use {
    crate::{
        layout::LayoutState,
        rendering::{Backend, Mesh, Renderer, Rgba as LSColor, Transform},
    },
    derive_more::{Add, Mul},
    euc::{
        buffer::Buffer2d,
        rasterizer::{self, BackfaceCullingDisabled, DepthStrategy},
        Interpolate, Pipeline, Target,
    },
    vek::{Mat3, Rgba, Vec2, Vec3},
};

pub use image::{self, RgbaImage};

struct SoftwareBackend {
    dims: [usize; 2],
    color: AlphaBlended,
}

impl Backend for SoftwareBackend {
    type Mesh = Vec<Vertex>;
    type Texture = Texture;

    fn create_mesh(&mut self, mesh: &Mesh) -> Self::Mesh {
        let vertices = mesh.vertices();
        mesh.indices()
            .iter()
            .map(|&index| {
                let v = vertices[index as usize];
                Vertex {
                    position: Vec2::new(v.x, v.y),
                    texcoord: Vec2::new(v.u, v.v),
                }
            })
            .collect()
    }

    fn render_mesh(
        &mut self,
        mesh: &Self::Mesh,
        transform: Transform,
        [tl, tr, br, bl]: [LSColor; 4],
        texture: Option<&Self::Texture>,
    ) {
        let [x1, y1, z1, x2, y2, z2] = transform.to_column_major_array();
        MyPipeline {
            transform: Mat3::new(x1, x2, 0.0, y1, y2, 0.0, z1, z2, 0.0),
            color_tl: Rgba::new(tl[0], tl[1], tl[2], tl[3]),
            color_tr: Rgba::new(tr[0], tr[1], tr[2], tr[3]),
            color_bl: Rgba::new(bl[0], bl[1], bl[2], bl[3]),
            color_br: Rgba::new(br[0], br[1], br[2], br[3]),
            texture,
        }
        .draw::<rasterizer::Triangles<'_, _, BackfaceCullingDisabled>, _>(
            mesh,
            &mut self.color,
            &mut NoDepth(self.dims),
        );
    }

    fn free_mesh(&mut self, _: Self::Mesh) {}

    fn create_texture(&mut self, width: u32, height: u32, data: &[u8]) -> Self::Texture {
        Texture {
            data: data.to_owned(),
            width: width as f32,
            height: height as f32,
            stride: width as usize * 4,
        }
    }
    fn free_texture(&mut self, _: Self::Texture) {}

    fn resize(&mut self, _: f32, _: f32) {}
}

struct NoDepth([usize; 2]);

impl Target for NoDepth {
    type Item = f32;

    fn size(&self) -> [usize; 2] {
        self.0
    }

    unsafe fn set(&mut self, _pos: [usize; 2], _item: Self::Item) {}

    unsafe fn get(&self, _pos: [usize; 2]) -> Self::Item {
        1.0
    }

    fn clear(&mut self, _fill: Self::Item) {}
}

struct AlphaBlended(Buffer2d<Rgba<f32>>);

impl Target for AlphaBlended {
    type Item = Rgba<f32>;

    fn size(&self) -> [usize; 2] {
        self.0.size()
    }

    unsafe fn set(&mut self, pos: [usize; 2], src: Self::Item) {
        debug_assert!(!src.r.is_nan());
        debug_assert!(!src.g.is_nan());
        debug_assert!(!src.b.is_nan());
        debug_assert!(!src.a.is_nan());

        let dst = self.0.get(pos);
        self.0.set(
            pos,
            Rgba::new(
                src.a * src.r + (1.0 - src.a) * dst.r,
                src.a * src.g + (1.0 - src.a) * dst.g,
                src.a * src.b + (1.0 - src.a) * dst.b,
                src.a + (1.0 - src.a) * dst.a,
            ),
        );
    }

    unsafe fn get(&self, pos: [usize; 2]) -> Self::Item {
        self.0.get(pos)
    }

    fn clear(&mut self, fill: Self::Item) {
        self.0.clear(fill)
    }
}

struct Texture {
    data: Vec<u8>,
    width: f32,
    height: f32,
    stride: usize,
}

struct MyPipeline<'a> {
    transform: Mat3<f32>,
    color_tl: Rgba<f32>,
    color_tr: Rgba<f32>,
    color_bl: Rgba<f32>,
    color_br: Rgba<f32>,
    texture: Option<&'a Texture>,
}

struct Vertex {
    position: Vec2<f32>,
    texcoord: Vec2<f32>,
}

#[derive(Clone, Mul, Add)]
struct VsOut {
    color: Rgba<f32>,
    texcoord: Vec2<f32>,
}

impl Interpolate for VsOut {
    #[inline(always)]
    fn lerp2(a: Self, b: Self, x: f32, y: f32) -> Self {
        a * x + b * y
    }
    #[inline(always)]
    fn lerp3(a: Self, b: Self, c: Self, x: f32, y: f32, z: f32) -> Self {
        a * x + b * y + c * z
    }
}

impl Pipeline for MyPipeline<'_> {
    type Vertex = Vertex;
    type VsOut = VsOut;
    type Pixel = Rgba<f32>;

    fn vert(&self, vertex: &Self::Vertex) -> ([f32; 4], Self::VsOut) {
        let left = self.color_tl * (1.0 - vertex.texcoord.y) + self.color_bl * vertex.texcoord.y;
        let right = self.color_tr * (1.0 - vertex.texcoord.y) + self.color_br * vertex.texcoord.y;
        let color = left * (1.0 - vertex.texcoord.x) + right * vertex.texcoord.x;

        let pos = Vec3::new(vertex.position.x, vertex.position.y, 1.0) * self.transform;

        (
            [2.0 * pos.x - 1.0, -2.0 * pos.y + 1.0, 0.0, 1.0],
            VsOut {
                color,
                texcoord: vertex.texcoord,
            },
        )
    }

    fn frag(&self, vsout: &Self::VsOut) -> Self::Pixel {
        if let Some(texture) = self.texture {
            let x = vsout.texcoord.x * texture.width;
            let y = vsout.texcoord.y * texture.height;
            let pixel = &texture.data[texture.stride * y as usize + x as usize * 4..];
            let r = pixel[0];
            let g = pixel[1];
            let b = pixel[2];
            let a = pixel[3];
            return Rgba::new(
                f32::from(r) / 255.0,
                f32::from(g) / 255.0,
                f32::from(b) / 255.0,
                f32::from(a) / 255.0,
            ) * vsout.color;
        }
        vsout.color
    }

    fn get_depth_strategy(&self) -> DepthStrategy {
        DepthStrategy::None
    }
}

/// Renders the layout state provided into an image of the selected resolution.
/// The final render will have pixelated edges as there is not going to be any
/// anti aliasing. Use [`render_anti_aliased`] if you want it to be anti
/// aliased. Note that this is software rendered and thus will be much slower
/// than rendering on the GPU.
///
/// [`render_anti_aliased`]: fn.render_anti_aliased.html
pub fn render(state: &LayoutState, [width, height]: [usize; 2]) -> RgbaImage {
    let mut backend = SoftwareBackend {
        color: AlphaBlended(Buffer2d::new(
            [width, height],
            Rgba::new(0.0, 0.0, 0.0, 0.0),
        )),
        dims: [width, height],
    };

    Renderer::new().render(&mut backend, (width as _, height as _), &state);

    let mut buf = Vec::with_capacity(width * height * 4);
    for pixel in backend.color.0.as_ref() {
        buf.extend(
            pixel
                .map(|e| (e * 255.0) as u8)
                .into_array()
                .iter()
                .cloned(),
        )
    }

    RgbaImage::from_raw(width as _, height as _, buf).unwrap()
}

/// Renders the layout state provided into an image of the selected resolution.
/// The `samples_sqrt` argument is the square root of the amount of samples used
/// for anti aliasing the final image. Note that this is software rendered and
/// thus will be much slower than rendering on the GPU.
pub fn render_anti_aliased(
    state: &LayoutState,
    [width, height]: [usize; 2],
    samples_sqrt: usize,
) -> RgbaImage {
    let image = render(state, [width * samples_sqrt, height * samples_sqrt]);
    image::imageops::thumbnail(&image, width as u32, height as u32)
}
<commit_msg>Disable Rendering Tests on x86_64-unknown-linux-musl<commit_after>//! Provides a software renderer that can be used without a GPU. The rendering
//! is much slower than with a normal GPU, but might be sufficient for
//! situations where you want to create a screenshot of the layout.

#[cfg(all(
    test,
    not(any(
        // Without MMX the floating point calculations don't follow IEEE 754. So the
        // tests fail to produce the correct checksums.
        // FIXME: We use SSE as an approximation for the cfg, because MMX isn't
        // supported by Rust just yet.
        all(
            target_arch = "x86",
            not(target_feature = "sse"),
        ),
        // Additionally the x86_64-unknown-linux-musl target seems to be failing
        // with lyon 0.15. I'd assume it's because of musl's libm doing
        // something different than the glibc one, but I'm honestly not sure.
        // FIXME: We likely want a perceptual image hash instead, which would be
        // much more robust.
        // https://github.com/LiveSplit/livesplit-core/issues/287
        all(
            target_os = "linux",
            target_arch = "x86_64",
            target_env = "musl",
        ),
    )),
))]
mod tests;

use {
    crate::{
        layout::LayoutState,
        rendering::{Backend, Mesh, Renderer, Rgba as LSColor, Transform},
    },
    derive_more::{Add, Mul},
    euc::{
        buffer::Buffer2d,
        rasterizer::{self, BackfaceCullingDisabled, DepthStrategy},
        Interpolate, Pipeline, Target,
    },
    vek::{Mat3, Rgba, Vec2, Vec3},
};

pub use image::{self, RgbaImage};

struct SoftwareBackend {
    dims: [usize; 2],
    color: AlphaBlended,
}

impl Backend for SoftwareBackend {
    type Mesh = Vec<Vertex>;
    type Texture = Texture;

    fn create_mesh(&mut self, mesh: &Mesh) -> Self::Mesh {
        let vertices = mesh.vertices();
        mesh.indices()
            .iter()
            .map(|&index| {
                let v = vertices[index as usize];
                Vertex {
                    position: Vec2::new(v.x, v.y),
                    texcoord: Vec2::new(v.u, v.v),
                }
            })
            .collect()
    }

    fn render_mesh(
        &mut self,
        mesh: &Self::Mesh,
        transform: Transform,
        [tl, tr, br, bl]: [LSColor; 4],
        texture: Option<&Self::Texture>,
    ) {
        let [x1, y1, z1, x2, y2, z2] = transform.to_column_major_array();
        MyPipeline {
            transform: Mat3::new(x1, x2, 0.0, y1, y2, 0.0, z1, z2, 0.0),
            color_tl: Rgba::new(tl[0], tl[1], tl[2], tl[3]),
            color_tr: Rgba::new(tr[0], tr[1], tr[2], tr[3]),
            color_bl: Rgba::new(bl[0], bl[1], bl[2], bl[3]),
            color_br: Rgba::new(br[0], br[1], br[2], br[3]),
            texture,
        }
        .draw::<rasterizer::Triangles<'_, _, BackfaceCullingDisabled>, _>(
            mesh,
            &mut self.color,
            &mut NoDepth(self.dims),
        );
    }

    fn free_mesh(&mut self, _: Self::Mesh) {}

    fn create_texture(&mut self, width: u32, height: u32, data: &[u8]) -> Self::Texture {
        Texture {
            data: data.to_owned(),
            width: width as f32,
            height: height as f32,
            stride: width as usize * 4,
        }
    }
    fn free_texture(&mut self, _: Self::Texture) {}

    fn resize(&mut self, _: f32, _: f32) {}
}

struct NoDepth([usize; 2]);

impl Target for NoDepth {
    type Item = f32;

    fn size(&self) -> [usize; 2] {
        self.0
    }

    unsafe fn set(&mut self, _pos: [usize; 2], _item: Self::Item) {}

    unsafe fn get(&self, _pos: [usize; 2]) -> Self::Item {
        1.0
    }

    fn clear(&mut self, _fill: Self::Item) {}
}

struct AlphaBlended(Buffer2d<Rgba<f32>>);

impl Target for AlphaBlended {
    type Item = Rgba<f32>;

    fn size(&self) -> [usize; 2] {
        self.0.size()
    }

    unsafe fn set(&mut self, pos: [usize; 2], src: Self::Item) {
        debug_assert!(!src.r.is_nan());
        debug_assert!(!src.g.is_nan());
        debug_assert!(!src.b.is_nan());
        debug_assert!(!src.a.is_nan());

        let dst = self.0.get(pos);
        self.0.set(
            pos,
            Rgba::new(
                src.a * src.r + (1.0 - src.a) * dst.r,
                src.a * src.g + (1.0 - src.a) * dst.g,
                src.a * src.b + (1.0 - src.a) * dst.b,
                src.a + (1.0 - src.a) * dst.a,
            ),
        );
    }

    unsafe fn get(&self, pos: [usize; 2]) -> Self::Item {
        self.0.get(pos)
    }

    fn clear(&mut self, fill: Self::Item) {
        self.0.clear(fill)
    }
}

struct Texture {
    data: Vec<u8>,
    width: f32,
    height: f32,
    stride: usize,
}

struct MyPipeline<'a> {
    transform: Mat3<f32>,
    color_tl: Rgba<f32>,
    color_tr: Rgba<f32>,
    color_bl: Rgba<f32>,
    color_br: Rgba<f32>,
    texture: Option<&'a Texture>,
}

struct Vertex {
    position: Vec2<f32>,
    texcoord: Vec2<f32>,
}

#[derive(Clone, Mul, Add)]
struct VsOut {
    color: Rgba<f32>,
    texcoord: Vec2<f32>,
}

impl Interpolate for VsOut {
    #[inline(always)]
    fn lerp2(a: Self, b: Self, x: f32, y: f32) -> Self {
        a * x + b * y
    }
    #[inline(always)]
    fn lerp3(a: Self, b: Self, c: Self, x: f32, y: f32, z: f32) -> Self {
        a * x + b * y + c * z
    }
}

impl Pipeline for MyPipeline<'_> {
    type Vertex = Vertex;
    type VsOut = VsOut;
    type Pixel = Rgba<f32>;

    fn vert(&self, vertex: &Self::Vertex) -> ([f32; 4], Self::VsOut) {
        let left = self.color_tl * (1.0 - vertex.texcoord.y) + self.color_bl * vertex.texcoord.y;
        let right = self.color_tr * (1.0 - vertex.texcoord.y) + self.color_br * vertex.texcoord.y;
        let color = left * (1.0 - vertex.texcoord.x) + right * vertex.texcoord.x;

        let pos = Vec3::new(vertex.position.x, vertex.position.y, 1.0) * self.transform;

        (
            [2.0 * pos.x - 1.0, -2.0 * pos.y + 1.0, 0.0, 1.0],
            VsOut {
                color,
                texcoord: vertex.texcoord,
            },
        )
    }

    fn frag(&self, vsout: &Self::VsOut) -> Self::Pixel {
        if let Some(texture) = self.texture {
            let x = vsout.texcoord.x * texture.width;
            let y = vsout.texcoord.y * texture.height;
            let pixel = &texture.data[texture.stride * y as usize + x as usize * 4..];
            let r = pixel[0];
            let g = pixel[1];
            let b = pixel[2];
            let a = pixel[3];
            return Rgba::new(
                f32::from(r) / 255.0,
                f32::from(g) / 255.0,
                f32::from(b) / 255.0,
                f32::from(a) / 255.0,
            ) * vsout.color;
        }
        vsout.color
    }

    fn get_depth_strategy(&self) -> DepthStrategy {
        DepthStrategy::None
    }
}

/// Renders the layout state provided into an image of the selected resolution.
/// The final render will have pixelated edges as there is not going to be any
/// anti aliasing. Use [`render_anti_aliased`] if you want it to be anti
/// aliased. Note that this is software rendered and thus will be much slower
/// than rendering on the GPU.
///
/// [`render_anti_aliased`]: fn.render_anti_aliased.html
pub fn render(state: &LayoutState, [width, height]: [usize; 2]) -> RgbaImage {
    let mut backend = SoftwareBackend {
        color: AlphaBlended(Buffer2d::new(
            [width, height],
            Rgba::new(0.0, 0.0, 0.0, 0.0),
        )),
        dims: [width, height],
    };

    Renderer::new().render(&mut backend, (width as _, height as _), &state);

    let mut buf = Vec::with_capacity(width * height * 4);
    for pixel in backend.color.0.as_ref() {
        buf.extend(
            pixel
                .map(|e| (e * 255.0) as u8)
                .into_array()
                .iter()
                .cloned(),
        )
    }

    RgbaImage::from_raw(width as _, height as _, buf).unwrap()
}

/// Renders the layout state provided into an image of the selected resolution.
/// The `samples_sqrt` argument is the square root of the amount of samples used
/// for anti aliasing the final image. Note that this is software rendered and
/// thus will be much slower than rendering on the GPU.
pub fn render_anti_aliased(
    state: &LayoutState,
    [width, height]: [usize; 2],
    samples_sqrt: usize,
) -> RgbaImage {
    let image = render(state, [width * samples_sqrt, height * samples_sqrt]);
    image::imageops::thumbnail(&image, width as u32, height as u32)
}
<|endoftext|>


<commit_before>use memory::Memory;
use std::vec::Vec;
use std::sync::atomic::{AtomicIsize, Ordering};

/// Maps multiple `Memory`s into a single address space.
///
/// The `MappedMemory` allows to unify multiple `Memory`s in one address space.
/// A `Memory` is mounted at a certain address and is from now on responsible
/// for all reads and writes starting at that address (the “mount point”) and
/// ending where the mounted memory ends. The read and write calls for the
/// responsible `Memory` will be given an address _relative_ to e mount point.
///
/// ```text
///                   Unmapped
///                <-------->
///       mem_a                   mem_b
/// |--------------|            |------|
///                            NullMemory
///                         |--------------
/// |------------- mapped_mem ----------->
/// |              |            |      |
/// 0            0x100        0x200  0x280
/// ```
///
/// For example: `mapped_mem.get(0x208)` would yield the same value as
/// `mem_b.get(0x008)`.
///
/// # Examples
///
/// ```
/// use osciemu::memory::{Memory, SliceMemory, MappedMemory};
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
/// let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
/// // Now mm =~ [1, _, 2, 2]
/// assert_eq!(mm.get(0), 1);
/// assert_eq!(mm.get(3), 2);
/// ```
///
/// A call to `mount` returns a `MemoryToken`. These tokens can be used to
/// borrow a `Memory` even if it has been mounted. This works similar to a
/// `std::cell::RefCell`.
///
/// ```
/// use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// use osciemu::memory::mappedmemory::MemoryToken;
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// mm.set(0, 99);
/// assert_eq!(mm.get(0), 99);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// The `MemoryToken`s are also used to disable a mounted `Memory`. A disabled
/// memory is effectively unmounted, but remains in the ownership of the
/// `MappedMemory` and can be enabled using `enable_mount()`.
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// mm.disable_mount(&m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// To move a memory out of the `MappedMemory`, a memory can be unmounted:
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// let m2 = mm.unmount(m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(m2.get(0), 99);
/// ```
///
/// # Panics
/// `MappedMemory` panics when an unmapped address is read or written.
pub struct MappedMemory {
    memories: Vec<Entry>,
}

static id_counter: AtomicIsize = AtomicIsize::new(0);

struct Entry {
    id: isize,
    start_address: usize,
    size: usize,
    enabled: bool,
    memory: Box<Memory>,
}

impl Entry {
    fn contains(&self, addr: usize) -> bool {
        self.start_address <= addr && self.start_address + self.size > addr
    }
}

#[derive(Clone)]
pub struct MemoryToken {
    id: isize,
}

impl MappedMemory {
    pub fn new() -> MappedMemory {
        MappedMemory {
            memories: Vec::new(),
        }
    }

    /// Mounts a `Memory` at the given address.
    pub fn mount(&mut self, start_address: usize, memory: Box<Memory>) -> MemoryToken {
        let size = memory.size();
        let id = id_counter.fetch_add(1, Ordering::Relaxed);
        let new_entry = Entry {
            id,
            start_address,
            size,
            memory,
            enabled: true
        };
        self.memories.push(new_entry);
        MemoryToken{id}
    }

    /// Unmounts a memory.
    pub fn unmount(&mut self, token: MemoryToken) -> Box<Memory> {
        let idx = self.memories
            .iter()
            .enumerate()
            .find(|&(_idx, ref entry)| entry.id == token.id)
            .map(|(idx, ref _entry)| idx)
            .unwrap();

        self.memories.remove(idx).memory
    }

    pub fn is_enabled_mount(&self, token: &MemoryToken) -> bool {
        self.entry_for_token(token).enabled
    }

    /// Disables a memory. This is the same as unmounting without moving
    /// ownership
    pub fn disable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.enabled = false;
    }

    // Enables a memory. The mount point remains unchanged.
    pub fn enable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.enabled = true;
    }

    /// Borrows a memory.
    pub fn borrow(&self, token: &MemoryToken) -> &Box<Memory> {
        &self.entry_for_token(token).memory
    }

    /// Mutable borrows a memory.
    pub fn borrow_mut(&mut self, token: &MemoryToken) -> &mut Box<Memory> {
        &mut self.entry_for_token_mut(token).memory
    }

    fn entry_for_token(&self, token: &MemoryToken) -> &Entry {
        self.memories
            .iter()
            .find(|ref entry| entry.id == token.id)
            .unwrap()
    }

    fn entry_for_token_mut(&mut self, token: &MemoryToken) -> &mut Entry {
        self.memories
            .iter_mut()
            .find(|ref mut entry| entry.id == token.id)
            .unwrap()
    }

    fn enabled_entry_at_addr(&self, addr: usize) -> Option<&Entry> {
        self.memories
            .iter()
            .rev()
            .filter(|entry| entry.enabled)
            .find(|entry| entry.contains(addr))
    }

    fn enabled_entry_at_addr_mut(&mut self, addr: usize) -> Option<&mut Entry> {
        self.memories
            .iter_mut()
            .rev()
            .filter(|entry| entry.enabled)
            .find(|entry| entry.contains(addr))
    }
}

impl Memory for MappedMemory {
    fn get(&self, addr: usize) -> i32 {
        self.enabled_entry_at_addr(addr)
            .map(|entry| entry.memory.get(addr - entry.start_address))
            .expect("Out of bounds")
    }

    fn set(&mut self, addr: usize, value: i32) {
        self.enabled_entry_at_addr_mut(addr)
            .map(|entry| entry.memory.set(addr - entry.start_address, value))
            .expect("Out of bounds")
    }

    fn size(&self) -> usize {
        self.memories
            .iter()
            .filter(|entry| entry.enabled)
            .map(|entry| entry.start_address + entry.size)
            .max()
            .unwrap_or(0)
    }
}

#[cfg(test)]
mod tests {
    use memory::{Memory, SliceMemory, NullMemory};

    #[test]
    fn overlapping_mounts() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(NullMemory::new()));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        assert_eq!(mm.get(0), 0);
        assert_eq!(mm.get(1), 2);
        assert_eq!(mm.get(2), 3);
        assert_eq!(mm.get(3), 0);
    }

    #[test]
    fn get_and_set() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));

        assert_eq!(mm.get(0), 1);
        assert_eq!(mm.get(2), 2);

        mm.set(0, 3);
        assert_eq!(mm.get(0), 3);

        mm.set(3, 0);
        assert_eq!(mm.get(3), 0);
        assert_eq!(mm.borrow(&m1).get(0), 3);
        assert_eq!(mm.borrow(&m2).get(1), 0);
    }

    #[test]
    fn size() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        assert_eq!(mm.size(), 0);

        mm.mount(0, m1);
        assert_eq!(mm.size(), 1);

        mm.mount(2, m2);
        assert_eq!(mm.size(), 4);
    }

    #[test]
    fn size_with_overlap() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        let m3 = Box::new(SliceMemory::from_slice(Box::new([3, 3, 3])));

        mm.mount(0, m1);
        mm.mount(2, m2);
        assert_eq!(mm.size(), 5);

        mm.mount(6, m3);
        assert_eq!(mm.size(), 9);
    }

    #[test]
    fn disable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));
        let m3 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([3, 3, 3, 3, 3]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 3);
        }
        mm.disable_mount(&m3);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m1);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    fn enable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
        }
        mm.enable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    fn unmount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        let m2 = mm.unmount(m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
            assert_eq!(m2.get(i), 2);
        }
    }

    #[test]
    fn is_enabled_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));

        assert!(mm.is_enabled_mount(&m2));
        mm.disable_mount(&m2);
        assert!(!mm.is_enabled_mount(&m2));
        mm.enable_mount(&m2);
        assert!(mm.is_enabled_mount(&m2));
    }

    #[test]
    #[should_panic]
    fn read_disabled() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        mm.disable_mount(&m2);
        mm.get(1);
    }
}
<commit_msg>docs(mappedmemory): Slight docs polish<commit_after>use memory::Memory;
use std::vec::Vec;
use std::sync::atomic::{AtomicIsize, Ordering};

/// Maps multiple `Memory`s into a single address space.
///
/// The `MappedMemory` allows to unify multiple `Memory`s in one address space.
/// A `Memory` is mounted at a certain address and is from now on responsible
/// for all reads and writes starting at that address (the “mount point”) and
/// ending where the mounted memory ends. The read and write calls for the
/// responsible `Memory` will be given an address _relative_ to e mount point.
///
/// ```text
///                   Unmapped
///                <-------->
///       mem_a                   mem_b
/// |--------------|            |------|
///                            NullMemory
///                         |--------------
/// |------------- mapped_mem ----------->
/// |              |            |      |
/// 0            0x100        0x200  0x280
/// ```
///
/// For example: `mapped_mem.get(0x208)` would yield the same value as
/// `mem_b.get(0x008)`.
///
/// # Examples
///
/// ```
/// use osciemu::memory::{Memory, SliceMemory, MappedMemory};
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
/// let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
/// // Now mm =~ [1, _, 2, 2]
/// assert_eq!(mm.get(0), 1);
/// assert_eq!(mm.get(3), 2);
/// ```
///
/// A call to `mount` returns a `MemoryToken`. These tokens can be used to
/// borrow a `Memory` even if it has been mounted. This works similar to a
/// `std::cell::RefCell`.
///
/// ```
/// use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// use osciemu::memory::mappedmemory::MemoryToken;
///
/// let mut mm = MappedMemory::new();
/// let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// mm.set(0, 99);
/// assert_eq!(mm.get(0), 99);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// The `MemoryToken`s are also used to disable a mounted `Memory`. A disabled
/// memory is effectively unmounted, but remains in the ownership of the
/// `MappedMemory` and can be enabled using `enable_mount()`.
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// mm.disable_mount(&m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(mm.borrow(&m2).get(0), 99);
/// ```
///
/// To move a memory out of the `MappedMemory`, a memory can be unmounted:
///
/// ```
/// # use osciemu::memory::{Memory, NullMemory, SliceMemory, MappedMemory};
/// # use osciemu::memory::mappedmemory::MemoryToken;
/// # let mut mm = MappedMemory::new();
/// # let m1 = mm.mount(0, Box::new(NullMemory::new()));
/// # let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 2, 3, 4]))));
/// # mm.set(0, 99);
/// // ...
/// let m2 = mm.unmount(m2);
/// assert_eq!(mm.get(0), 0);
/// assert_eq!(m2.get(0), 99);
/// ```
///
/// # Panics
/// `MappedMemory` panics when an unmapped address is read or written.
pub struct MappedMemory {
    memories: Vec<Entry>,
}

static id_counter: AtomicIsize = AtomicIsize::new(0);

struct Entry {
    id: isize,
    start_address: usize,
    size: usize,
    enabled: bool,
    memory: Box<Memory>,
}

impl Entry {
    fn contains(&self, addr: usize) -> bool {
        self.start_address <= addr && self.start_address + self.size > addr
    }
}

#[derive(Clone)]
pub struct MemoryToken {
    id: isize,
}

impl MappedMemory {
    pub fn new() -> MappedMemory {
        MappedMemory {
            memories: Vec::new(),
        }
    }

    /// Mounts a `Memory` at the given address.
    pub fn mount(&mut self, start_address: usize, memory: Box<Memory>) -> MemoryToken {
        let size = memory.size();
        let id = id_counter.fetch_add(1, Ordering::Relaxed);
        let new_entry = Entry {
            id,
            start_address,
            size,
            memory,
            enabled: true
        };
        self.memories.push(new_entry);
        MemoryToken{id}
    }

    /// Unmounts a memory.
    pub fn unmount(&mut self, token: MemoryToken) -> Box<Memory> {
        let idx = self.memories
            .iter()
            .enumerate()
            .find(|&(_idx, ref entry)| entry.id == token.id)
            .map(|(idx, ref _entry)| idx)
            .unwrap();

        self.memories.remove(idx).memory
    }

    // Checks if a mounted memory is enabled.
    pub fn is_enabled_mount(&self, token: &MemoryToken) -> bool {
        self.entry_for_token(token).enabled
    }

    /// Disables a memory. This is the same as unmounting without moving
    /// ownership out of `MappedMemory`.
    pub fn disable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.enabled = false;
    }

    // Enables a memory. The mount point remains unchanged.
    pub fn enable_mount(&mut self, token: &MemoryToken) {
        let entry = self.entry_for_token_mut(token);
        entry.enabled = true;
    }

    /// Borrows a memory.
    pub fn borrow(&self, token: &MemoryToken) -> &Box<Memory> {
        &self.entry_for_token(token).memory
    }

    /// Mutable borrows a memory.
    pub fn borrow_mut(&mut self, token: &MemoryToken) -> &mut Box<Memory> {
        &mut self.entry_for_token_mut(token).memory
    }

    fn entry_for_token(&self, token: &MemoryToken) -> &Entry {
        self.memories
            .iter()
            .find(|ref entry| entry.id == token.id)
            .unwrap()
    }

    fn entry_for_token_mut(&mut self, token: &MemoryToken) -> &mut Entry {
        self.memories
            .iter_mut()
            .find(|ref mut entry| entry.id == token.id)
            .unwrap()
    }

    fn enabled_entry_at_addr(&self, addr: usize) -> Option<&Entry> {
        self.memories
            .iter()
            .rev()
            .filter(|entry| entry.enabled)
            .find(|entry| entry.contains(addr))
    }

    fn enabled_entry_at_addr_mut(&mut self, addr: usize) -> Option<&mut Entry> {
        self.memories
            .iter_mut()
            .rev()
            .filter(|entry| entry.enabled)
            .find(|entry| entry.contains(addr))
    }
}

impl Memory for MappedMemory {
    fn get(&self, addr: usize) -> i32 {
        self.enabled_entry_at_addr(addr)
            .map(|entry| entry.memory.get(addr - entry.start_address))
            .expect("Out of bounds")
    }

    fn set(&mut self, addr: usize, value: i32) {
        self.enabled_entry_at_addr_mut(addr)
            .map(|entry| entry.memory.set(addr - entry.start_address, value))
            .expect("Out of bounds")
    }

    fn size(&self) -> usize {
        self.memories
            .iter()
            .filter(|entry| entry.enabled)
            .map(|entry| entry.start_address + entry.size)
            .max()
            .unwrap_or(0)
    }
}

#[cfg(test)]
mod tests {
    use memory::{Memory, SliceMemory, NullMemory};

    #[test]
    fn overlapping_mounts() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(NullMemory::new()));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        assert_eq!(mm.get(0), 0);
        assert_eq!(mm.get(1), 2);
        assert_eq!(mm.get(2), 3);
        assert_eq!(mm.get(3), 0);
    }

    #[test]
    fn get_and_set() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([2, 2]))));

        assert_eq!(mm.get(0), 1);
        assert_eq!(mm.get(2), 2);

        mm.set(0, 3);
        assert_eq!(mm.get(0), 3);

        mm.set(3, 0);
        assert_eq!(mm.get(3), 0);
        assert_eq!(mm.borrow(&m1).get(0), 3);
        assert_eq!(mm.borrow(&m2).get(1), 0);
    }

    #[test]
    fn size() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        assert_eq!(mm.size(), 0);

        mm.mount(0, m1);
        assert_eq!(mm.size(), 1);

        mm.mount(2, m2);
        assert_eq!(mm.size(), 4);
    }

    #[test]
    fn size_with_overlap() {
        let mut mm = super::MappedMemory::new();
        let m1 = Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1])));
        let m2 = Box::new(SliceMemory::from_slice(Box::new([2, 2])));
        let m3 = Box::new(SliceMemory::from_slice(Box::new([3, 3, 3])));

        mm.mount(0, m1);
        mm.mount(2, m2);
        assert_eq!(mm.size(), 5);

        mm.mount(6, m3);
        assert_eq!(mm.size(), 9);
    }

    #[test]
    fn disable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));
        let m3 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([3, 3, 3, 3, 3]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 3);
        }
        mm.disable_mount(&m3);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m1);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    fn enable_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        mm.disable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
        }
        mm.enable_mount(&m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
    }

    #[test]
    fn unmount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));

        for i in 0..5 {
            assert_eq!(mm.get(i), 2);
        }
        let m2 = mm.unmount(m2);
        for i in 0..5 {
            assert_eq!(mm.get(i), 1);
            assert_eq!(m2.get(i), 2);
        }
    }

    #[test]
    fn is_enabled_mount() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1, 1, 1, 1, 1]))));
        let m2 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([2, 2, 2, 2, 2]))));

        assert!(mm.is_enabled_mount(&m2));
        mm.disable_mount(&m2);
        assert!(!mm.is_enabled_mount(&m2));
        mm.enable_mount(&m2);
        assert!(mm.is_enabled_mount(&m2));
    }

    #[test]
    #[should_panic]
    fn read_disabled() {
        let mut mm = super::MappedMemory::new();
        let m1 = mm.mount(0, Box::new(SliceMemory::from_slice(Box::new([1]))));
        let m2 = mm.mount(1, Box::new(SliceMemory::from_slice(Box::new([2]))));
        let m3 = mm.mount(2, Box::new(SliceMemory::from_slice(Box::new([3]))));
        mm.disable_mount(&m2);
        mm.get(1);
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
//! and margins in CSS.

use std::num::Zero;

/// A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
/// and margins in CSS.
#[deriving(Clone, Eq)]
pub struct SideOffsets2D<T> {
    pub top: T,
    pub right: T,
    pub bottom: T,
    pub left: T,
}

impl<T> SideOffsets2D<T> {
    pub fn new(top: T, right: T, bottom: T, left: T) -> SideOffsets2D<T> {
        SideOffsets2D {
            top: top,
            right: right,
            bottom: bottom,
            left: left,
        }
    }
}

impl<T:Clone> SideOffsets2D<T> {
    pub fn new_all_same(all: T) -> SideOffsets2D<T> {
        SideOffsets2D::new(all.clone(), all.clone(), all.clone(), all.clone())
    }
}

impl<T:Num> SideOffsets2D<T> {
    pub fn horizontal(&self) -> T {
        self.left + self.right
    }

    pub fn vertical(&self) -> T {
        self.top + self.bottom
    }
}

impl<T:Num> Add<SideOffsets2D<T>, SideOffsets2D<T>> for SideOffsets2D<T> {
    fn add(&self, other: &SideOffsets2D<T>) -> SideOffsets2D<T> {
        SideOffsets2D {
            top: self.top + other.top,
            right: self.right + other.right,
            bottom: self.bottom + other.bottom,
            left: self.left + other.left,
        }
    }
}

impl<T:Num> Zero for SideOffsets2D<T> {
    fn zero() -> SideOffsets2D<T> {
        SideOffsets2D {
            top: Zero::zero(),
            right: Zero::zero(),
            bottom: Zero::zero(),
            left: Zero::zero(),
        }
    }

    fn is_zero(&self) -> bool {
        self.top.is_zero() && self.right.is_zero() && self.bottom.is_zero() && self.left.is_zero()
    }
}

/// A SIMD enabled version of SideOffsets2D specialized for i32.
#[deriving(Clone, Eq, Rand)]
#[simd]
pub struct SideOffsets2DSimdI32 {
    pub top: i32,
    pub bottom: i32,
    pub right: i32,
    pub left: i32,
}

impl SideOffsets2DSimdI32 {
    pub fn new(top: i32, right: i32, bottom: i32, left: i32) -> SideOffsets2DSimdI32 {
        SideOffsets2DSimdI32 {
            top: top,
            bottom: bottom,
            right: right,
            left: left,
        }
    }
}

impl SideOffsets2DSimdI32 {
    pub fn new_all_same(all: i32) -> SideOffsets2DSimdI32 {
        SideOffsets2DSimdI32::new(all.clone(), all.clone(), all.clone(), all.clone())
    }
}

impl SideOffsets2DSimdI32 {
    pub fn horizontal(&self) -> i32 {
        self.left + self.right
    }

    pub fn vertical(&self) -> i32 {
        self.top + self.bottom
    }
}

impl Add<SideOffsets2DSimdI32, SideOffsets2DSimdI32> for SideOffsets2DSimdI32 {
    fn add(&self, other: &SideOffsets2DSimdI32) -> SideOffsets2DSimdI32 {
        *self + *other // Use SIMD addition
    }
}

impl Zero for SideOffsets2DSimdI32 {
    fn zero() -> SideOffsets2DSimdI32 {
        SideOffsets2DSimdI32 {
            top: Zero::zero(),
            bottom: Zero::zero(),
            right: Zero::zero(),
            left: Zero::zero(),
        }
    }

    #[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
    fn is_zero(&self) -> bool {
        self.top.is_zero() && self.right.is_zero() && self.bottom.is_zero() && self.left.is_zero()
    }

    #[cfg(target_arch = "x86")]
    #[cfg(target_arch = "x86_64")]
    #[inline]
    fn is_zero(&self) -> bool {
        let is_zero: bool;
        unsafe {
            asm! {
                "ptest $1, $1
                 setz $0"
                : "=r"(is_zero)
                : "x"(*self)
                :
                : "intel"
            };
        }
        is_zero
    }
}

#[cfg(test)]
mod tests {
    use std::num::Zero;
    use super::SideOffsets2DSimdI32;

    #[test]
    fn test_is_zero() {
        assert!(SideOffsets2DSimdI32::new_all_same(0).is_zero());
        assert!(!SideOffsets2DSimdI32::new_all_same(1).is_zero());
        assert!(!SideOffsets2DSimdI32::new(1, 0, 0, 0).is_zero());
        assert!(!SideOffsets2DSimdI32::new(0, 1, 0, 0).is_zero());
        assert!(!SideOffsets2DSimdI32::new(0, 0, 1, 0).is_zero());
        assert!(!SideOffsets2DSimdI32::new(0, 0, 0, 1).is_zero());
    }
}

#[cfg(bench)]
mod bench {
    use test::BenchHarness;
    use std::num::Zero;
    use rand::{XorShiftRng, Rng};
    use super::SideOffsets2DSimdI32;

    #[cfg(target_arch = "x86")]
    #[cfg(target_arch = "x86_64")]
    #[bench]
    fn bench_naive_is_zero(bh: &mut BenchHarness) {
        fn is_zero(x: &SideOffsets2DSimdI32) -> bool {
            x.top.is_zero() && x.right.is_zero() && x.bottom.is_zero() && x.left.is_zero()
        }
        let mut rng = XorShiftRng::new().unwrap();
        bh.iter(|| is_zero(&rng.gen::<SideOffsets2DSimdI32>()))
    }

    #[bench]
    fn bench_is_zero(bh: &mut BenchHarness) {
        let mut rng = XorShiftRng::new().unwrap();
        bh.iter(|| rng.gen::<SideOffsets2DSimdI32>().is_zero())
    }

    #[bench]
    fn bench_naive_add(bh: &mut BenchHarness) {
        fn add(x: &SideOffsets2DSimdI32, y: &SideOffsets2DSimdI32) -> SideOffsets2DSimdI32 {
            SideOffsets2DSimdI32 {
                top: x.top + y.top,
                right: x.right + y.right,
                bottom: x.bottom + y.bottom,
                left: x.left + y.left,
            }
        }
        let mut rng = XorShiftRng::new().unwrap();
        bh.iter(|| add(&rng.gen::<SideOffsets2DSimdI32>(), &rng.gen::<SideOffsets2DSimdI32>()))
    }

    #[bench]
    fn bench_add(bh: &mut BenchHarness) {
        let mut rng = XorShiftRng::new().unwrap();
        bh.iter(|| rng.gen::<SideOffsets2DSimdI32>() + rng.gen::<SideOffsets2DSimdI32>())
    }
}
<commit_msg>Inline all SideOffsets2DSimdI32 functions<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
//! and margins in CSS.

use std::num::Zero;

/// A group of side offsets, which correspond to top/left/bottom/right for borders, padding,
/// and margins in CSS.
#[deriving(Clone, Eq)]
pub struct SideOffsets2D<T> {
    pub top: T,
    pub right: T,
    pub bottom: T,
    pub left: T,
}

impl<T> SideOffsets2D<T> {
    pub fn new(top: T, right: T, bottom: T, left: T) -> SideOffsets2D<T> {
        SideOffsets2D {
            top: top,
            right: right,
            bottom: bottom,
            left: left,
        }
    }
}

impl<T:Clone> SideOffsets2D<T> {
    pub fn new_all_same(all: T) -> SideOffsets2D<T> {
        SideOffsets2D::new(all.clone(), all.clone(), all.clone(), all.clone())
    }
}

impl<T:Num> SideOffsets2D<T> {
    pub fn horizontal(&self) -> T {
        self.left + self.right
    }

    pub fn vertical(&self) -> T {
        self.top + self.bottom
    }
}

impl<T:Num> Add<SideOffsets2D<T>, SideOffsets2D<T>> for SideOffsets2D<T> {
    fn add(&self, other: &SideOffsets2D<T>) -> SideOffsets2D<T> {
        SideOffsets2D {
            top: self.top + other.top,
            right: self.right + other.right,
            bottom: self.bottom + other.bottom,
            left: self.left + other.left,
        }
    }
}

impl<T:Num> Zero for SideOffsets2D<T> {
    fn zero() -> SideOffsets2D<T> {
        SideOffsets2D {
            top: Zero::zero(),
            right: Zero::zero(),
            bottom: Zero::zero(),
            left: Zero::zero(),
        }
    }

    fn is_zero(&self) -> bool {
        self.top.is_zero() && self.right.is_zero() && self.bottom.is_zero() && self.left.is_zero()
    }
}

/// A SIMD enabled version of SideOffsets2D specialized for i32.
#[deriving(Clone, Eq, Rand)]
#[simd]
pub struct SideOffsets2DSimdI32 {
    pub top: i32,
    pub bottom: i32,
    pub right: i32,
    pub left: i32,
}

impl SideOffsets2DSimdI32 {
    #[inline]
    pub fn new(top: i32, right: i32, bottom: i32, left: i32) -> SideOffsets2DSimdI32 {
        SideOffsets2DSimdI32 {
            top: top,
            bottom: bottom,
            right: right,
            left: left,
        }
    }
}

impl SideOffsets2DSimdI32 {
    #[inline]
    pub fn new_all_same(all: i32) -> SideOffsets2DSimdI32 {
        SideOffsets2DSimdI32::new(all.clone(), all.clone(), all.clone(), all.clone())
    }
}

impl SideOffsets2DSimdI32 {
    #[inline]
    pub fn horizontal(&self) -> i32 {
        self.left + self.right
    }

    #[inline]
    pub fn vertical(&self) -> i32 {
        self.top + self.bottom
    }
}

impl Add<SideOffsets2DSimdI32, SideOffsets2DSimdI32> for SideOffsets2DSimdI32 {
    #[inline]
    fn add(&self, other: &SideOffsets2DSimdI32) -> SideOffsets2DSimdI32 {
        *self + *other // Use SIMD addition
    }
}

impl Zero for SideOffsets2DSimdI32 {
    #[inline]
    fn zero() -> SideOffsets2DSimdI32 {
        SideOffsets2DSimdI32 {
            top: Zero::zero(),
            bottom: Zero::zero(),
            right: Zero::zero(),
            left: Zero::zero(),
        }
    }

    #[cfg(not(target_arch = "x86"), not(target_arch = "x86_64"))]
    #[inline]
    fn is_zero(&self) -> bool {
        self.top.is_zero() && self.right.is_zero() && self.bottom.is_zero() && self.left.is_zero()
    }

    #[cfg(target_arch = "x86")]
    #[cfg(target_arch = "x86_64")]
    #[inline]
    fn is_zero(&self) -> bool {
        let is_zero: bool;
        unsafe {
            asm! {
                "ptest $1, $1
                 setz $0"
                : "=r"(is_zero)
                : "x"(*self)
                :
                : "intel"
            };
        }
        is_zero
    }
}

#[cfg(test)]
mod tests {
    use std::num::Zero;
    use super::SideOffsets2DSimdI32;

    #[test]
    fn test_is_zero() {
        assert!(SideOffsets2DSimdI32::new_all_same(0).is_zero());
        assert!(!SideOffsets2DSimdI32::new_all_same(1).is_zero());
        assert!(!SideOffsets2DSimdI32::new(1, 0, 0, 0).is_zero());
        assert!(!SideOffsets2DSimdI32::new(0, 1, 0, 0).is_zero());
        assert!(!SideOffsets2DSimdI32::new(0, 0, 1, 0).is_zero());
        assert!(!SideOffsets2DSimdI32::new(0, 0, 0, 1).is_zero());
    }
}

#[cfg(bench)]
mod bench {
    use test::BenchHarness;
    use std::num::Zero;
    use rand::{XorShiftRng, Rng};
    use super::SideOffsets2DSimdI32;

    #[cfg(target_arch = "x86")]
    #[cfg(target_arch = "x86_64")]
    #[bench]
    fn bench_naive_is_zero(bh: &mut BenchHarness) {
        fn is_zero(x: &SideOffsets2DSimdI32) -> bool {
            x.top.is_zero() && x.right.is_zero() && x.bottom.is_zero() && x.left.is_zero()
        }
        let mut rng = XorShiftRng::new().unwrap();
        bh.iter(|| is_zero(&rng.gen::<SideOffsets2DSimdI32>()))
    }

    #[bench]
    fn bench_is_zero(bh: &mut BenchHarness) {
        let mut rng = XorShiftRng::new().unwrap();
        bh.iter(|| rng.gen::<SideOffsets2DSimdI32>().is_zero())
    }

    #[bench]
    fn bench_naive_add(bh: &mut BenchHarness) {
        fn add(x: &SideOffsets2DSimdI32, y: &SideOffsets2DSimdI32) -> SideOffsets2DSimdI32 {
            SideOffsets2DSimdI32 {
                top: x.top + y.top,
                right: x.right + y.right,
                bottom: x.bottom + y.bottom,
                left: x.left + y.left,
            }
        }
        let mut rng = XorShiftRng::new().unwrap();
        bh.iter(|| add(&rng.gen::<SideOffsets2DSimdI32>(), &rng.gen::<SideOffsets2DSimdI32>()))
    }

    #[bench]
    fn bench_add(bh: &mut BenchHarness) {
        let mut rng = XorShiftRng::new().unwrap();
        bh.iter(|| rng.gen::<SideOffsets2DSimdI32>() + rng.gen::<SideOffsets2DSimdI32>())
    }
}
<|endoftext|>


<commit_before>use super::*;

// crc: u32 4
// kind: u8 1
// seg num: u64 9 (varint)
// pid: u64 9 (varint)
// len: u64 9 (varint)
/// Log messages have a header that might eb up to this length.
pub const MAX_MSG_HEADER_LEN: usize = 32;

/// Log segments have a header of this length.
pub const SEG_HEADER_LEN: usize = 20;

/// The minimum number of items per segment.
/// Items larger than this fraction of an `io_buf`
/// will be stored as an off-log blob.
pub const MINIMUM_ITEMS_PER_SEGMENT: usize = 4;

/// During testing, this should never be exceeded.
#[allow(unused)]
pub const MAX_SPACE_AMPLIFICATION: f64 = 30.;

pub(crate) const META_PID: PageId = 0;
pub(crate) const COUNTER_PID: PageId = 1;
pub(crate) const BATCH_MANIFEST_PID: PageId = PageId::max_value() - 666;

pub(crate) const PAGE_CONSOLIDATION_THRESHOLD: usize = 10;
<commit_msg>Drop maximum space amp from 30 to 5<commit_after>use super::*;

// crc: u32 4
// kind: u8 1
// seg num: u64 9 (varint)
// pid: u64 9 (varint)
// len: u64 9 (varint)
/// Log messages have a header that might eb up to this length.
pub const MAX_MSG_HEADER_LEN: usize = 32;

/// Log segments have a header of this length.
pub const SEG_HEADER_LEN: usize = 20;

/// The minimum number of items per segment.
/// Items larger than this fraction of an `io_buf`
/// will be stored as an off-log blob.
pub const MINIMUM_ITEMS_PER_SEGMENT: usize = 4;

/// During testing, this should never be exceeded.
#[allow(unused)]
pub const MAX_SPACE_AMPLIFICATION: f64 = 5.;

pub(crate) const META_PID: PageId = 0;
pub(crate) const COUNTER_PID: PageId = 1;
pub(crate) const BATCH_MANIFEST_PID: PageId = PageId::max_value() - 666;

pub(crate) const PAGE_CONSOLIDATION_THRESHOLD: usize = 10;
<|endoftext|>