<commit_before>use anyhow::Error;
use flate2::read::GzDecoder;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use tar::Archive;

const DEFAULT_TARGET: &str = "x86_64-unknown-linux-gnu";
const RUSTC_VERSION: &str = include_str!("../../../version");

#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub(crate) enum PkgType {
    Rust,
    RustSrc,
    Rustc,
    Cargo,
    Rls,
    RustAnalyzer,
    Clippy,
    Rustfmt,
    LlvmTools,
    Miri,
    Other(String),
}

impl PkgType {
    pub(crate) fn from_component(component: &str) -> Self {
        match component {
            "rust" => PkgType::Rust,
            "rust-src" => PkgType::RustSrc,
            "rustc" => PkgType::Rustc,
            "cargo" => PkgType::Cargo,
            "rls" | "rls-preview" => PkgType::Rls,
            "rust-analyzer" | "rust-analyzer-preview" => PkgType::RustAnalyzer,
            "clippy" | "clippy-preview" => PkgType::Clippy,
            "rustfmt" | "rustfmt-preview" => PkgType::Rustfmt,
            "llvm-tools" | "llvm-tools-preview" => PkgType::LlvmTools,
            "miri" | "miri-preview" => PkgType::Miri,
            other => PkgType::Other(other.into()),
        }
    }

    /// First part of the tarball name.
    fn tarball_component_name(&self) -> &str {
        match self {
            PkgType::Rust => "rust",
            PkgType::RustSrc => "rust-src",
            PkgType::Rustc => "rustc",
            PkgType::Cargo => "cargo",
            PkgType::Rls => "rls",
            PkgType::RustAnalyzer => "rust-analyzer",
            PkgType::Clippy => "clippy",
            PkgType::Rustfmt => "rustfmt",
            PkgType::LlvmTools => "llvm-tools",
            PkgType::Miri => "miri",
            PkgType::Other(component) => component,
        }
    }

    /// Whether this package has the same version as Rust itself, or has its own `version` and
    /// `git-commit-hash` files inside the tarball.
    fn should_use_rust_version(&self) -> bool {
        match self {
            PkgType::Cargo => false,
            PkgType::Rls => false,
            PkgType::RustAnalyzer => false,
            PkgType::Clippy => false,
            PkgType::Rustfmt => false,
            PkgType::LlvmTools => false,
            PkgType::Miri => false,

            PkgType::Rust => true,
            PkgType::RustSrc => true,
            PkgType::Rustc => true,
            PkgType::Other(_) => true,
        }
    }

    /// Whether this package is target-independent or not.
    fn target_independent(&self) -> bool {
        *self == PkgType::RustSrc
    }
}

#[derive(Debug, Default, Clone)]
pub(crate) struct VersionInfo {
    pub(crate) version: Option<String>,
    pub(crate) git_commit: Option<String>,
    pub(crate) present: bool,
}

pub(crate) struct Versions {
    channel: String,
    dist_path: PathBuf,
    versions: HashMap<PkgType, VersionInfo>,
}

impl Versions {
    pub(crate) fn new(channel: &str, dist_path: &Path) -> Result<Self, Error> {
        Ok(Self { channel: channel.into(), dist_path: dist_path.into(), versions: HashMap::new() })
    }

    pub(crate) fn channel(&self) -> &str {
        &self.channel
    }

    pub(crate) fn version(&mut self, mut package: &PkgType) -> Result<VersionInfo, Error> {
        if package.should_use_rust_version() {
            package = &PkgType::Rust;
        }

        match self.versions.get(package) {
            Some(version) => Ok(version.clone()),
            None => {
                let version_info = self.load_version_from_tarball(package)?;
                self.versions.insert(package.clone(), version_info.clone());
                Ok(version_info)
            }
        }
    }

    fn load_version_from_tarball(&mut self, package: &PkgType) -> Result<VersionInfo, Error> {
        let tarball_name = self.tarball_name(package, DEFAULT_TARGET)?;
        let tarball = self.dist_path.join(tarball_name);

        let file = match File::open(&tarball) {
            Ok(file) => file,
            Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
                // Missing tarballs do not return an error, but return empty data.
                return Ok(VersionInfo::default());
            }
            Err(err) => return Err(err.into()),
        };
        let mut tar = Archive::new(GzDecoder::new(file));

        let mut version = None;
        let mut git_commit = None;
        for entry in tar.entries()? {
            let mut entry = entry?;

            let dest;
            match entry.path()?.components().nth(1).and_then(|c| c.as_os_str().to_str()) {
                Some("version") => dest = &mut version,
                Some("git-commit-hash") => dest = &mut git_commit,
                _ => continue,
            }
            let mut buf = String::new();
            entry.read_to_string(&mut buf)?;
            *dest = Some(buf);

            // Short circuit to avoid reading the whole tar file if not necessary.
            if version.is_some() && git_commit.is_some() {
                break;
            }
        }

        Ok(VersionInfo { version, git_commit, present: true })
    }

    pub(crate) fn disable_version(&mut self, package: &PkgType) {
        match self.versions.get_mut(package) {
            Some(version) => {
                *version = VersionInfo::default();
            }
            None => {
                self.versions.insert(package.clone(), VersionInfo::default());
            }
        }
    }

    pub(crate) fn archive_name(
        &mut self,
        package: &PkgType,
        target: &str,
        extension: &str,
    ) -> Result<String, Error> {
        let component_name = package.tarball_component_name();
        let version = match self.channel.as_str() {
            "stable" => RUSTC_VERSION.into(),
            "beta" => "beta".into(),
            "nightly" => "nightly".into(),
            _ => format!("{}-dev", RUSTC_VERSION),
        };

        if package.target_independent() {
            Ok(format!("{}-{}.{}", component_name, version, extension))
        } else {
            Ok(format!("{}-{}-{}.{}", component_name, version, target, extension))
        }
    }

    pub(crate) fn tarball_name(
        &mut self,
        package: &PkgType,
        target: &str,
    ) -> Result<String, Error> {
        self.archive_name(package, target, "tar.gz")
    }

    pub(crate) fn rustc_version(&self) -> &str {
        RUSTC_VERSION
    }
}
<commit_msg>build-manifest: strip newline from rustc version<commit_after>use anyhow::Error;
use flate2::read::GzDecoder;
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use tar::Archive;

const DEFAULT_TARGET: &str = "x86_64-unknown-linux-gnu";

#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub(crate) enum PkgType {
    Rust,
    RustSrc,
    Rustc,
    Cargo,
    Rls,
    RustAnalyzer,
    Clippy,
    Rustfmt,
    LlvmTools,
    Miri,
    Other(String),
}

impl PkgType {
    pub(crate) fn from_component(component: &str) -> Self {
        match component {
            "rust" => PkgType::Rust,
            "rust-src" => PkgType::RustSrc,
            "rustc" => PkgType::Rustc,
            "cargo" => PkgType::Cargo,
            "rls" | "rls-preview" => PkgType::Rls,
            "rust-analyzer" | "rust-analyzer-preview" => PkgType::RustAnalyzer,
            "clippy" | "clippy-preview" => PkgType::Clippy,
            "rustfmt" | "rustfmt-preview" => PkgType::Rustfmt,
            "llvm-tools" | "llvm-tools-preview" => PkgType::LlvmTools,
            "miri" | "miri-preview" => PkgType::Miri,
            other => PkgType::Other(other.into()),
        }
    }

    /// First part of the tarball name.
    fn tarball_component_name(&self) -> &str {
        match self {
            PkgType::Rust => "rust",
            PkgType::RustSrc => "rust-src",
            PkgType::Rustc => "rustc",
            PkgType::Cargo => "cargo",
            PkgType::Rls => "rls",
            PkgType::RustAnalyzer => "rust-analyzer",
            PkgType::Clippy => "clippy",
            PkgType::Rustfmt => "rustfmt",
            PkgType::LlvmTools => "llvm-tools",
            PkgType::Miri => "miri",
            PkgType::Other(component) => component,
        }
    }

    /// Whether this package has the same version as Rust itself, or has its own `version` and
    /// `git-commit-hash` files inside the tarball.
    fn should_use_rust_version(&self) -> bool {
        match self {
            PkgType::Cargo => false,
            PkgType::Rls => false,
            PkgType::RustAnalyzer => false,
            PkgType::Clippy => false,
            PkgType::Rustfmt => false,
            PkgType::LlvmTools => false,
            PkgType::Miri => false,

            PkgType::Rust => true,
            PkgType::RustSrc => true,
            PkgType::Rustc => true,
            PkgType::Other(_) => true,
        }
    }

    /// Whether this package is target-independent or not.
    fn target_independent(&self) -> bool {
        *self == PkgType::RustSrc
    }
}

#[derive(Debug, Default, Clone)]
pub(crate) struct VersionInfo {
    pub(crate) version: Option<String>,
    pub(crate) git_commit: Option<String>,
    pub(crate) present: bool,
}

pub(crate) struct Versions {
    channel: String,
    dist_path: PathBuf,
    versions: HashMap<PkgType, VersionInfo>,
}

impl Versions {
    pub(crate) fn new(channel: &str, dist_path: &Path) -> Result<Self, Error> {
        Ok(Self { channel: channel.into(), dist_path: dist_path.into(), versions: HashMap::new() })
    }

    pub(crate) fn channel(&self) -> &str {
        &self.channel
    }

    pub(crate) fn version(&mut self, mut package: &PkgType) -> Result<VersionInfo, Error> {
        if package.should_use_rust_version() {
            package = &PkgType::Rust;
        }

        match self.versions.get(package) {
            Some(version) => Ok(version.clone()),
            None => {
                let version_info = self.load_version_from_tarball(package)?;
                self.versions.insert(package.clone(), version_info.clone());
                Ok(version_info)
            }
        }
    }

    fn load_version_from_tarball(&mut self, package: &PkgType) -> Result<VersionInfo, Error> {
        let tarball_name = self.tarball_name(package, DEFAULT_TARGET)?;
        let tarball = self.dist_path.join(tarball_name);

        let file = match File::open(&tarball) {
            Ok(file) => file,
            Err(err) if err.kind() == std::io::ErrorKind::NotFound => {
                // Missing tarballs do not return an error, but return empty data.
                return Ok(VersionInfo::default());
            }
            Err(err) => return Err(err.into()),
        };
        let mut tar = Archive::new(GzDecoder::new(file));

        let mut version = None;
        let mut git_commit = None;
        for entry in tar.entries()? {
            let mut entry = entry?;

            let dest;
            match entry.path()?.components().nth(1).and_then(|c| c.as_os_str().to_str()) {
                Some("version") => dest = &mut version,
                Some("git-commit-hash") => dest = &mut git_commit,
                _ => continue,
            }
            let mut buf = String::new();
            entry.read_to_string(&mut buf)?;
            *dest = Some(buf);

            // Short circuit to avoid reading the whole tar file if not necessary.
            if version.is_some() && git_commit.is_some() {
                break;
            }
        }

        Ok(VersionInfo { version, git_commit, present: true })
    }

    pub(crate) fn disable_version(&mut self, package: &PkgType) {
        match self.versions.get_mut(package) {
            Some(version) => {
                *version = VersionInfo::default();
            }
            None => {
                self.versions.insert(package.clone(), VersionInfo::default());
            }
        }
    }

    pub(crate) fn archive_name(
        &mut self,
        package: &PkgType,
        target: &str,
        extension: &str,
    ) -> Result<String, Error> {
        let component_name = package.tarball_component_name();
        let version = match self.channel.as_str() {
            "stable" => self.rustc_version().into(),
            "beta" => "beta".into(),
            "nightly" => "nightly".into(),
            _ => format!("{}-dev", self.rustc_version()),
        };

        if package.target_independent() {
            Ok(format!("{}-{}.{}", component_name, version, extension))
        } else {
            Ok(format!("{}-{}-{}.{}", component_name, version, target, extension))
        }
    }

    pub(crate) fn tarball_name(
        &mut self,
        package: &PkgType,
        target: &str,
    ) -> Result<String, Error> {
        self.archive_name(package, target, "tar.gz")
    }

    pub(crate) fn rustc_version(&self) -> &str {
        const RUSTC_VERSION: &str = include_str!("../../../version");
        RUSTC_VERSION.trim()
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    build.no_default_flags(true);
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP").flag("/clang:-undef");
    } else {
        build.flag("-xc").flag("-P").flag("-undef");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    // SWGL relies heavily on inlining for performance so override -Oz with -O2
    if build.get_compiler().args().contains(&std::ffi::OsString::from("-Oz")) {
        build.flag("-O2");
    }

    build.file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<commit_msg>Bug 1698156 - Set up SWGLPP env target with null CFLAGS and CXXFLAGS. r=jrmuizel<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    // Use SWGLPP target to avoid pulling CFLAGS/CXXFLAGS.
    build.target("SWGLPP");
    build.no_default_flags(true);
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP").flag("/clang:-undef");
    } else {
        build.flag("-xc").flag("-P").flag("-undef");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    // We need to ensure that the C preprocessor does not pull compiler flags from
    // the host or target environment. Set up a SWGLPP target with empty flags to
    // work around this.
    std::env::set_var("CFLAGS_SWGLPP", "");
    std::env::set_var("CXXFLAGS_SWGLPP", "");

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    // SWGL relies heavily on inlining for performance so override -Oz with -O2
    if build.get_compiler().args().contains(&std::ffi::OsString::from("-Oz")) {
        build.flag("-O2");
    }

    build.file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<|endoftext|>


<commit_before>mod builder;

pub use self::builder::*;

use super::*;
use full_rusttype::gpu_cache::Cache;
use rustc_hash::{FxHashMap, FxHashSet};
use std::borrow::Cow;
use std::collections::{hash_map::Entry, HashMap, HashSet};
use std::hash::{BuildHasher, BuildHasherDefault, Hash, Hasher};
use std::{fmt, i32};

/// A hash of `Section` data
type SectionHash = u64;

/// A "practically collision free" `Section` hasher
type DefaultSectionHasher = BuildHasherDefault<seahash::SeaHasher>;

/// Object allowing glyph drawing, containing cache state. Manages glyph positioning cacheing,
/// glyph draw caching & efficient GPU texture cache updating.
///
/// Build using a [`GlyphBrushBuilder`](struct.GlyphBrushBuilder.html).
///
/// # Caching behaviour
///
/// Calls to [`GlyphBrush::queue`](#method.queue),
/// [`GlyphBrush::pixel_bounds`](#method.pixel_bounds), [`GlyphBrush::glyphs`](#method.glyphs)
/// calculate the positioned glyphs for a section.
/// This is cached so future calls to any of the methods for the same section are much
/// cheaper. In the case of [`GlyphBrush::queue`](#method.queue) the calculations will also be
/// used for actual drawing.
///
/// The cache for a section will be **cleared** after a
/// [`GlyphBrush::draw_queued`](#method.draw_queued) call when that section has not been used since
/// the previous draw call.
pub struct GlyphBrush<'font, H = DefaultSectionHasher> {
    fonts: Vec<Font<'font>>,
    texture_cache: Cache<'font>,
    draw_cache: Option<DrawnGlyphBrush>,

    // cache of section-layout hash -> computed glyphs, this avoid repeated glyph computation
    // for identical layout/sections common to repeated frame rendering
    calculate_glyph_cache: FxHashMap<SectionHash, GlyphedSection<'font>>,

    // buffer of section-layout hashs (that must exist in the calculate_glyph_cache)
    // to be rendered on the next `draw_queued` call
    section_buffer: Vec<SectionHash>,

    // Set of section hashs to keep in the glyph cache this frame even if they haven't been drawn
    keep_in_cache: FxHashSet<SectionHash>,

    // config
    cache_glyph_positioning: bool,
    cache_glyph_drawing: bool,

    section_hasher: H,
}

impl<'font, H> fmt::Debug for GlyphBrush<'font, H> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "GlyphBrush")
    }
}

impl<'font, H: BuildHasher> GlyphCruncher<'font> for GlyphBrush<'font, H> {
    fn pixel_bounds_custom_layout<'a, S, L>(
        &mut self,
        section: S,
        custom_layout: &L,
    ) -> Option<Rect<i32>>
    where
        L: GlyphPositioner + Hash,
        S: Into<Cow<'a, VariedSection<'a>>>,
    {
        let section_hash = self.cache_glyphs(&section.into(), custom_layout);
        self.keep_in_cache.insert(section_hash);
        self.calculate_glyph_cache[&section_hash].pixel_bounds()
    }

    fn glyphs_custom_layout<'a, 'b, S, L>(
        &'b mut self,
        section: S,
        custom_layout: &L,
    ) -> PositionedGlyphIter<'b, 'font>
    where
        L: GlyphPositioner + Hash,
        S: Into<Cow<'a, VariedSection<'a>>>,
    {
        let section_hash = self.cache_glyphs(&section.into(), custom_layout);
        self.keep_in_cache.insert(section_hash);
        self.calculate_glyph_cache[&section_hash].glyphs()
    }
}

impl<'font, H: BuildHasher> GlyphBrush<'font, H> {
    /// Queues a section/layout to be processed by the next call of
    /// [`process_queued`](struct.GlyphBrush.html#method.process_queued). Can be called multiple
    /// times to queue multiple sections for drawing.
    ///
    /// Used to provide custom `GlyphPositioner` logic, if using built-in
    /// [`Layout`](enum.Layout.html) simply use [`queue`](struct.GlyphBrush.html#method.queue)
    ///
    /// Benefits from caching, see [caching behaviour](#caching-behaviour).
    pub fn queue_custom_layout<'a, S, G>(&mut self, section: S, custom_layout: &G)
    where
        G: GlyphPositioner,
        S: Into<Cow<'a, VariedSection<'a>>>,
    {
        let section = section.into();
        if cfg!(debug_assertions) {
            for text in &section.text {
                assert!(self.fonts.len() > text.font_id.0, "Invalid font id");
            }
        }
        let section_hash = self.cache_glyphs(&section, custom_layout);
        self.section_buffer.push(section_hash);
    }

    /// Queues a section/layout to be processed by the next call of
    /// [`process_queued`](struct.GlyphBrush.html#method.process_queued). Can be called multiple
    /// times to queue multiple sections for drawing.
    ///
    /// Benefits from caching, see [caching behaviour](#caching-behaviour).
    pub fn queue<'a, S>(&mut self, section: S)
    where
        S: Into<Cow<'a, VariedSection<'a>>>,
    {
        let section = section.into();
        let layout = section.layout;
        self.queue_custom_layout(section, &layout)
    }

    fn hash<T: Hash>(&self, hashable: &T) -> SectionHash {
        let mut s = self.section_hasher.build_hasher();
        hashable.hash(&mut s);
        s.finish()
    }

    /// Returns the calculate_glyph_cache key for this sections glyphs
    fn cache_glyphs<L>(&mut self, section: &VariedSection, layout: &L) -> SectionHash
    where
        L: GlyphPositioner,
    {
        let section_hash = self.hash(&(section, layout));

        if self.cache_glyph_positioning {
            if let Entry::Vacant(entry) = self.calculate_glyph_cache.entry(section_hash) {
                let geometry = SectionGeometry::from(section);
                entry.insert(GlyphedSection {
                    bounds: layout.bounds_rect(&geometry),
                    glyphs: layout.calculate_glyphs(&self.fonts, &geometry, &section.text),
                    z: section.z,
                });
            }
        } else {
            let geometry = SectionGeometry::from(section);
            self.calculate_glyph_cache.insert(
                section_hash,
                GlyphedSection {
                    bounds: layout.bounds_rect(&geometry),
                    glyphs: layout.calculate_glyphs(&self.fonts, &geometry, &section.text),
                    z: section.z,
                },
            );
        }
        section_hash
    }

    /// Processes all queued sections, calling texture update logic when necessary &
    /// returning a `BrushAction`.
    /// See [`queue`](struct.GlyphBrush.html#method.queue).
    ///
    /// Trims the cache, see [caching behaviour](#caching-behaviour).
    ///
    /// ```no_run
    /// # extern crate glyph_brush;
    /// # use glyph_brush::*;
    /// # fn main() -> Result<(), BrushError> {
    /// # let dejavu: &[u8] = include_bytes!("../../../fonts/DejaVuSans.ttf");
    /// # let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(dejavu).build();
    /// # let update_texture = |_, _| {};
    /// # let into_vertex = |_| ();
    /// glyph_brush.process_queued(
    ///     (1024, 768),
    ///     |rect, tex_data| update_texture(rect, tex_data),
    ///     |vertex_data| into_vertex(vertex_data),
    /// )?
    /// # ;
    /// # Ok(())
    /// # }
    /// ```
    pub fn process_queued<V, F1, F2>(
        &mut self,
        (screen_w, screen_h): (u32, u32),
        update_texture: F1,
        to_vertex: F2,
    ) -> Result<BrushAction<V>, BrushError>
    where
        F1: FnMut(Rect<u32>, &[u8]),
        F2: Fn(GlyphVertex) -> V,
    {
        let current_text_state = self.hash(&(&self.section_buffer, screen_w, screen_h));

        let result = if !self.cache_glyph_drawing
            || self.draw_cache.is_none()
            || self.draw_cache.as_ref().unwrap().last_text_state != current_text_state
        {
            let mut no_text = true;

            for section_hash in &self.section_buffer {
                let GlyphedSection { ref glyphs, .. } = self.calculate_glyph_cache[section_hash];
                for &(ref glyph, _, font_id) in glyphs {
                    self.texture_cache.queue_glyph(font_id.0, glyph.clone());
                    no_text = false;
                }
            }

            if no_text {
                self.clear_section_buffer();
                return Ok(BrushAction::Draw(vec![]));
            }

            if self.texture_cache.cache_queued(update_texture).is_err() {
                let (width, height) = self.texture_cache.dimensions();
                return Err(BrushError::TextureTooSmall {
                    current: (width, height),
                    suggested: (width * 2, height * 2),
                });
            }

            let verts: Vec<V> = {
                let sections: Vec<_> = self
                    .section_buffer
                    .iter()
                    .map(|hash| &self.calculate_glyph_cache[hash])
                    .collect();

                let mut verts = Vec::with_capacity(
                    sections
                        .iter()
                        .map(|section| section.glyphs.len())
                        .sum::<usize>(),
                );

                for &GlyphedSection {
                    ref glyphs,
                    bounds,
                    z,
                } in sections
                {
                    verts.extend(glyphs.into_iter().filter_map(|(glyph, color, font_id)| {
                        match self.texture_cache.rect_for(font_id.0, glyph) {
                            Err(err) => {
                                error!("Cache miss?: {:?}, {:?}: {}", font_id, glyph, err);
                                None
                            }
                            Ok(None) => None,
                            Ok(Some((tex_coords, pixel_coords))) => {
                                if pixel_coords.min.x as f32 > bounds.max.x
                                    || pixel_coords.min.y as f32 > bounds.max.y
                                    || bounds.min.x > pixel_coords.max.x as f32
                                    || bounds.min.y > pixel_coords.max.y as f32
                                {
                                    // glyph is totally outside the bounds
                                    None
                                } else {
                                    Some(to_vertex(GlyphVertex {
                                        tex_coords,
                                        pixel_coords,
                                        bounds,
                                        screen_dimensions: (screen_w as f32, screen_h as f32),
                                        color: *color,
                                        z,
                                    }))
                                }
                            }
                        }
                    }));
                }

                verts
            };

            self.draw_cache = Some(
                self.draw_cache
                    .take()
                    .map(|mut cache| {
                        cache.last_text_state = current_text_state;
                        cache
                    }).unwrap_or_default(),
            );

            BrushAction::Draw(verts)
        } else {
            BrushAction::ReDraw
        };

        self.clear_section_buffer();
        Ok(result)
    }

    /// Rebuilds the texture cache with new dimensions. Should be avoided if possible.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use glyph_brush::GlyphBrushBuilder;
    /// # let dejavu: &[u8] = include_bytes!("../../../fonts/DejaVuSans.ttf");
    /// # let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(dejavu).build();
    /// glyph_brush.resize_texture(512, 512);
    /// ```
    pub fn resize_texture(&mut self, new_width: u32, new_height: u32) {
        self.texture_cache
            .to_builder()
            .dimensions(new_width, new_height)
            .rebuild(&mut self.texture_cache);

        self.draw_cache.take();
    }

    /// Returns [`FontMap`](type.FontMap.html) of available fonts.
    pub fn fonts(&self) -> &impl FontMap<'font> {
        &self.fonts
    }

    fn clear_section_buffer(&mut self) {
        if self.cache_glyph_positioning {
            // clear section_buffer & trim calculate_glyph_cache to active sections
            let mut active =
                HashSet::with_capacity(self.section_buffer.len() + self.keep_in_cache.len());

            for h in self.section_buffer.drain(..) {
                active.insert(h);
            }
            for h in self.keep_in_cache.drain() {
                active.insert(h);
            }
            self.calculate_glyph_cache
                .retain(|key, _| active.contains(key));
        } else {
            self.section_buffer.clear();
            self.calculate_glyph_cache.clear();
            self.keep_in_cache.clear();
        }
    }

    /// Adds an additional font to the one(s) initially added on build.
    ///
    /// Returns a new [`FontId`](struct.FontId.html) to reference this font.
    ///
    /// # Example
    ///
    /// ```no_run
    /// extern crate glyph_brush;
    /// use glyph_brush::{GlyphBrushBuilder, Section};
    /// # fn main() {
    ///
    /// // dejavu is built as default `FontId(0)`
    /// let dejavu: &[u8] = include_bytes!("../../../fonts/DejaVuSans.ttf");
    /// let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(dejavu).build();
    ///
    /// // some time later, add another font referenced by a new `FontId`
    /// let open_sans_italic: &[u8] = include_bytes!("../../../fonts/OpenSans-Italic.ttf");
    /// let open_sans_italic_id = glyph_brush.add_font_bytes(open_sans_italic);
    /// # }
    /// ```
    pub fn add_font_bytes<'a: 'font, B: Into<SharedBytes<'a>>>(&mut self, font_data: B) -> FontId {
        self.add_font(Font::from_bytes(font_data.into()).unwrap())
    }

    /// Adds an additional font to the one(s) initially added on build.
    ///
    /// Returns a new [`FontId`](struct.FontId.html) to reference this font.
    pub fn add_font<'a: 'font>(&mut self, font_data: Font<'a>) -> FontId {
        self.fonts.push(font_data);
        FontId(self.fonts.len() - 1)
    }
}

#[derive(Debug, Default)]
struct DrawnGlyphBrush {
    last_text_state: u64,
}

// glyph: &PositionedGlyph,
// color: Color,
// font_id: FontId,
// cache: &Cache,
// bounds: Rect<f32>,
// z: f32,
// (screen_width, screen_height): (f32, f32),

/// Data used to generate vertex information for a single glyph
#[derive(Debug)]
pub struct GlyphVertex {
    pub tex_coords: Rect<f32>,
    pub pixel_coords: Rect<i32>,
    pub bounds: Rect<f32>,
    pub screen_dimensions: (f32, f32),
    pub color: Color,
    pub z: f32,
}

/// Actions that should be taken after processing queue data
pub enum BrushAction<V> {
    /// Draw new/changed vertix data.
    Draw(Vec<V>),
    /// Re-draw last frame's vertices unmodified.
    ReDraw,
}

#[derive(Debug)]
pub enum BrushError {
    /// Texture is too small to cache queued glyphs.alloc
    ///
    /// A larger suggested size is included.
    TextureTooSmall {
        current: (u32, u32),
        suggested: (u32, u32),
    },
}
impl fmt::Display for BrushError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", std::error::Error::description(self))
    }
}
impl std::error::Error for BrushError {
    fn description(&self) -> &str {
        match self {
            BrushError::TextureTooSmall { .. } => "Texture is too small to cache queued glyphs",
        }
    }
}
<commit_msg>Fix doc typo<commit_after>mod builder;

pub use self::builder::*;

use super::*;
use full_rusttype::gpu_cache::Cache;
use rustc_hash::{FxHashMap, FxHashSet};
use std::borrow::Cow;
use std::collections::{hash_map::Entry, HashMap, HashSet};
use std::hash::{BuildHasher, BuildHasherDefault, Hash, Hasher};
use std::{fmt, i32};

/// A hash of `Section` data
type SectionHash = u64;

/// A "practically collision free" `Section` hasher
type DefaultSectionHasher = BuildHasherDefault<seahash::SeaHasher>;

/// Object allowing glyph drawing, containing cache state. Manages glyph positioning cacheing,
/// glyph draw caching & efficient GPU texture cache updating.
///
/// Build using a [`GlyphBrushBuilder`](struct.GlyphBrushBuilder.html).
///
/// # Caching behaviour
///
/// Calls to [`GlyphBrush::queue`](#method.queue),
/// [`GlyphBrush::pixel_bounds`](#method.pixel_bounds), [`GlyphBrush::glyphs`](#method.glyphs)
/// calculate the positioned glyphs for a section.
/// This is cached so future calls to any of the methods for the same section are much
/// cheaper. In the case of [`GlyphBrush::queue`](#method.queue) the calculations will also be
/// used for actual drawing.
///
/// The cache for a section will be **cleared** after a
/// [`GlyphBrush::draw_queued`](#method.draw_queued) call when that section has not been used since
/// the previous draw call.
pub struct GlyphBrush<'font, H = DefaultSectionHasher> {
    fonts: Vec<Font<'font>>,
    texture_cache: Cache<'font>,
    draw_cache: Option<DrawnGlyphBrush>,

    // cache of section-layout hash -> computed glyphs, this avoid repeated glyph computation
    // for identical layout/sections common to repeated frame rendering
    calculate_glyph_cache: FxHashMap<SectionHash, GlyphedSection<'font>>,

    // buffer of section-layout hashs (that must exist in the calculate_glyph_cache)
    // to be rendered on the next `draw_queued` call
    section_buffer: Vec<SectionHash>,

    // Set of section hashs to keep in the glyph cache this frame even if they haven't been drawn
    keep_in_cache: FxHashSet<SectionHash>,

    // config
    cache_glyph_positioning: bool,
    cache_glyph_drawing: bool,

    section_hasher: H,
}

impl<'font, H> fmt::Debug for GlyphBrush<'font, H> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "GlyphBrush")
    }
}

impl<'font, H: BuildHasher> GlyphCruncher<'font> for GlyphBrush<'font, H> {
    fn pixel_bounds_custom_layout<'a, S, L>(
        &mut self,
        section: S,
        custom_layout: &L,
    ) -> Option<Rect<i32>>
    where
        L: GlyphPositioner + Hash,
        S: Into<Cow<'a, VariedSection<'a>>>,
    {
        let section_hash = self.cache_glyphs(&section.into(), custom_layout);
        self.keep_in_cache.insert(section_hash);
        self.calculate_glyph_cache[&section_hash].pixel_bounds()
    }

    fn glyphs_custom_layout<'a, 'b, S, L>(
        &'b mut self,
        section: S,
        custom_layout: &L,
    ) -> PositionedGlyphIter<'b, 'font>
    where
        L: GlyphPositioner + Hash,
        S: Into<Cow<'a, VariedSection<'a>>>,
    {
        let section_hash = self.cache_glyphs(&section.into(), custom_layout);
        self.keep_in_cache.insert(section_hash);
        self.calculate_glyph_cache[&section_hash].glyphs()
    }
}

impl<'font, H: BuildHasher> GlyphBrush<'font, H> {
    /// Queues a section/layout to be processed by the next call of
    /// [`process_queued`](struct.GlyphBrush.html#method.process_queued). Can be called multiple
    /// times to queue multiple sections for drawing.
    ///
    /// Used to provide custom `GlyphPositioner` logic, if using built-in
    /// [`Layout`](enum.Layout.html) simply use [`queue`](struct.GlyphBrush.html#method.queue)
    ///
    /// Benefits from caching, see [caching behaviour](#caching-behaviour).
    pub fn queue_custom_layout<'a, S, G>(&mut self, section: S, custom_layout: &G)
    where
        G: GlyphPositioner,
        S: Into<Cow<'a, VariedSection<'a>>>,
    {
        let section = section.into();
        if cfg!(debug_assertions) {
            for text in &section.text {
                assert!(self.fonts.len() > text.font_id.0, "Invalid font id");
            }
        }
        let section_hash = self.cache_glyphs(&section, custom_layout);
        self.section_buffer.push(section_hash);
    }

    /// Queues a section/layout to be processed by the next call of
    /// [`process_queued`](struct.GlyphBrush.html#method.process_queued). Can be called multiple
    /// times to queue multiple sections for drawing.
    ///
    /// Benefits from caching, see [caching behaviour](#caching-behaviour).
    pub fn queue<'a, S>(&mut self, section: S)
    where
        S: Into<Cow<'a, VariedSection<'a>>>,
    {
        let section = section.into();
        let layout = section.layout;
        self.queue_custom_layout(section, &layout)
    }

    fn hash<T: Hash>(&self, hashable: &T) -> SectionHash {
        let mut s = self.section_hasher.build_hasher();
        hashable.hash(&mut s);
        s.finish()
    }

    /// Returns the calculate_glyph_cache key for this sections glyphs
    fn cache_glyphs<L>(&mut self, section: &VariedSection, layout: &L) -> SectionHash
    where
        L: GlyphPositioner,
    {
        let section_hash = self.hash(&(section, layout));

        if self.cache_glyph_positioning {
            if let Entry::Vacant(entry) = self.calculate_glyph_cache.entry(section_hash) {
                let geometry = SectionGeometry::from(section);
                entry.insert(GlyphedSection {
                    bounds: layout.bounds_rect(&geometry),
                    glyphs: layout.calculate_glyphs(&self.fonts, &geometry, &section.text),
                    z: section.z,
                });
            }
        } else {
            let geometry = SectionGeometry::from(section);
            self.calculate_glyph_cache.insert(
                section_hash,
                GlyphedSection {
                    bounds: layout.bounds_rect(&geometry),
                    glyphs: layout.calculate_glyphs(&self.fonts, &geometry, &section.text),
                    z: section.z,
                },
            );
        }
        section_hash
    }

    /// Processes all queued sections, calling texture update logic when necessary &
    /// returning a `BrushAction`.
    /// See [`queue`](struct.GlyphBrush.html#method.queue).
    ///
    /// Trims the cache, see [caching behaviour](#caching-behaviour).
    ///
    /// ```no_run
    /// # extern crate glyph_brush;
    /// # use glyph_brush::*;
    /// # fn main() -> Result<(), BrushError> {
    /// # let dejavu: &[u8] = include_bytes!("../../../fonts/DejaVuSans.ttf");
    /// # let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(dejavu).build();
    /// # let update_texture = |_, _| {};
    /// # let into_vertex = |_| ();
    /// glyph_brush.process_queued(
    ///     (1024, 768),
    ///     |rect, tex_data| update_texture(rect, tex_data),
    ///     |vertex_data| into_vertex(vertex_data),
    /// )?
    /// # ;
    /// # Ok(())
    /// # }
    /// ```
    pub fn process_queued<V, F1, F2>(
        &mut self,
        (screen_w, screen_h): (u32, u32),
        update_texture: F1,
        to_vertex: F2,
    ) -> Result<BrushAction<V>, BrushError>
    where
        F1: FnMut(Rect<u32>, &[u8]),
        F2: Fn(GlyphVertex) -> V,
    {
        let current_text_state = self.hash(&(&self.section_buffer, screen_w, screen_h));

        let result = if !self.cache_glyph_drawing
            || self.draw_cache.is_none()
            || self.draw_cache.as_ref().unwrap().last_text_state != current_text_state
        {
            let mut no_text = true;

            for section_hash in &self.section_buffer {
                let GlyphedSection { ref glyphs, .. } = self.calculate_glyph_cache[section_hash];
                for &(ref glyph, _, font_id) in glyphs {
                    self.texture_cache.queue_glyph(font_id.0, glyph.clone());
                    no_text = false;
                }
            }

            if no_text {
                self.clear_section_buffer();
                return Ok(BrushAction::Draw(vec![]));
            }

            if self.texture_cache.cache_queued(update_texture).is_err() {
                let (width, height) = self.texture_cache.dimensions();
                return Err(BrushError::TextureTooSmall {
                    current: (width, height),
                    suggested: (width * 2, height * 2),
                });
            }

            let verts: Vec<V> = {
                let sections: Vec<_> = self
                    .section_buffer
                    .iter()
                    .map(|hash| &self.calculate_glyph_cache[hash])
                    .collect();

                let mut verts = Vec::with_capacity(
                    sections
                        .iter()
                        .map(|section| section.glyphs.len())
                        .sum::<usize>(),
                );

                for &GlyphedSection {
                    ref glyphs,
                    bounds,
                    z,
                } in sections
                {
                    verts.extend(glyphs.into_iter().filter_map(|(glyph, color, font_id)| {
                        match self.texture_cache.rect_for(font_id.0, glyph) {
                            Err(err) => {
                                error!("Cache miss?: {:?}, {:?}: {}", font_id, glyph, err);
                                None
                            }
                            Ok(None) => None,
                            Ok(Some((tex_coords, pixel_coords))) => {
                                if pixel_coords.min.x as f32 > bounds.max.x
                                    || pixel_coords.min.y as f32 > bounds.max.y
                                    || bounds.min.x > pixel_coords.max.x as f32
                                    || bounds.min.y > pixel_coords.max.y as f32
                                {
                                    // glyph is totally outside the bounds
                                    None
                                } else {
                                    Some(to_vertex(GlyphVertex {
                                        tex_coords,
                                        pixel_coords,
                                        bounds,
                                        screen_dimensions: (screen_w as f32, screen_h as f32),
                                        color: *color,
                                        z,
                                    }))
                                }
                            }
                        }
                    }));
                }

                verts
            };

            self.draw_cache = Some(
                self.draw_cache
                    .take()
                    .map(|mut cache| {
                        cache.last_text_state = current_text_state;
                        cache
                    }).unwrap_or_default(),
            );

            BrushAction::Draw(verts)
        } else {
            BrushAction::ReDraw
        };

        self.clear_section_buffer();
        Ok(result)
    }

    /// Rebuilds the texture cache with new dimensions. Should be avoided if possible.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use glyph_brush::GlyphBrushBuilder;
    /// # let dejavu: &[u8] = include_bytes!("../../../fonts/DejaVuSans.ttf");
    /// # let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(dejavu).build();
    /// glyph_brush.resize_texture(512, 512);
    /// ```
    pub fn resize_texture(&mut self, new_width: u32, new_height: u32) {
        self.texture_cache
            .to_builder()
            .dimensions(new_width, new_height)
            .rebuild(&mut self.texture_cache);

        self.draw_cache.take();
    }

    /// Returns [`FontMap`](type.FontMap.html) of available fonts.
    pub fn fonts(&self) -> &impl FontMap<'font> {
        &self.fonts
    }

    fn clear_section_buffer(&mut self) {
        if self.cache_glyph_positioning {
            // clear section_buffer & trim calculate_glyph_cache to active sections
            let mut active =
                HashSet::with_capacity(self.section_buffer.len() + self.keep_in_cache.len());

            for h in self.section_buffer.drain(..) {
                active.insert(h);
            }
            for h in self.keep_in_cache.drain() {
                active.insert(h);
            }
            self.calculate_glyph_cache
                .retain(|key, _| active.contains(key));
        } else {
            self.section_buffer.clear();
            self.calculate_glyph_cache.clear();
            self.keep_in_cache.clear();
        }
    }

    /// Adds an additional font to the one(s) initially added on build.
    ///
    /// Returns a new [`FontId`](struct.FontId.html) to reference this font.
    ///
    /// # Example
    ///
    /// ```no_run
    /// extern crate glyph_brush;
    /// use glyph_brush::{GlyphBrushBuilder, Section};
    /// # fn main() {
    ///
    /// // dejavu is built as default `FontId(0)`
    /// let dejavu: &[u8] = include_bytes!("../../../fonts/DejaVuSans.ttf");
    /// let mut glyph_brush = GlyphBrushBuilder::using_font_bytes(dejavu).build();
    ///
    /// // some time later, add another font referenced by a new `FontId`
    /// let open_sans_italic: &[u8] = include_bytes!("../../../fonts/OpenSans-Italic.ttf");
    /// let open_sans_italic_id = glyph_brush.add_font_bytes(open_sans_italic);
    /// # }
    /// ```
    pub fn add_font_bytes<'a: 'font, B: Into<SharedBytes<'a>>>(&mut self, font_data: B) -> FontId {
        self.add_font(Font::from_bytes(font_data.into()).unwrap())
    }

    /// Adds an additional font to the one(s) initially added on build.
    ///
    /// Returns a new [`FontId`](struct.FontId.html) to reference this font.
    pub fn add_font<'a: 'font>(&mut self, font_data: Font<'a>) -> FontId {
        self.fonts.push(font_data);
        FontId(self.fonts.len() - 1)
    }
}

#[derive(Debug, Default)]
struct DrawnGlyphBrush {
    last_text_state: u64,
}

// glyph: &PositionedGlyph,
// color: Color,
// font_id: FontId,
// cache: &Cache,
// bounds: Rect<f32>,
// z: f32,
// (screen_width, screen_height): (f32, f32),

/// Data used to generate vertex information for a single glyph
#[derive(Debug)]
pub struct GlyphVertex {
    pub tex_coords: Rect<f32>,
    pub pixel_coords: Rect<i32>,
    pub bounds: Rect<f32>,
    pub screen_dimensions: (f32, f32),
    pub color: Color,
    pub z: f32,
}

/// Actions that should be taken after processing queue data
pub enum BrushAction<V> {
    /// Draw new/changed vertix data.
    Draw(Vec<V>),
    /// Re-draw last frame's vertices unmodified.
    ReDraw,
}

#[derive(Debug)]
pub enum BrushError {
    /// Texture is too small to cache queued glyphs
    ///
    /// A larger suggested size is included.
    TextureTooSmall {
        current: (u32, u32),
        suggested: (u32, u32),
    },
}
impl fmt::Display for BrushError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", std::error::Error::description(self))
    }
}
impl std::error::Error for BrushError {
    fn description(&self) -> &str {
        match self {
            BrushError::TextureTooSmall { .. } => "Texture is too small to cache queued glyphs",
        }
    }
}
<|endoftext|>


<commit_before>use colored::*;
use git2::Repository;
use std::env;

type MaybePrompt = Option<String>;
type Prompt = String;

fn main() {
    let path = env::var("PWD")
        .unwrap_or(String::new());
    print!("{} ", get_prompt(&path))
}

/// Returns the prompt with the git repository information at the given path
fn get_prompt(path: &str) -> Prompt {
    if let Some(i) = get_maybe_prompt(path) {
        format!("{} {}{}{} {}",
                i.revision.white(),
                i.behind.white(),
                i.ahead.white(),
                i.status,
                i.state)
    } else {
        String::new()
    }
}

#[cfg(test)]
mod get_prompt_tests {
    use super::get_prompt;
    use std::env;

    macro_rules! invalid_input_tests {
        ($($name:ident: $value:expr,)*) => {
        $(
            #[test]
            fn $name() {
                let (input, expected) = $value;
                assert_eq!(expected, get_prompt(input));
            }
        ) *
        }
    }

    invalid_input_tests! {
        empty_for_root: ("/", ""),
        empty_for_usr_local_bin: ("/usr/local/bin", ""),
        empty_for_non_existant_path: ("/tmp/i/do/not/exist", ""),
    }

    #[test]
    fn non_empty_for_pwd() {
        let path = env::var("PWD")
            .unwrap_or(String::new());
        assert_ne!(get_prompt(&path), "");
    }
}

fn get_maybe_prompt(path: &str) -> Option<PromptInfo> {
    let repo = Repository::discover(path).ok()?;

    Some(PromptInfo{
        revision: get_branch(&repo)?,
        behind: behind_count(&repo),
        ahead: ahead_count(&repo),
        status: status(&repo).unwrap_or(String::new()),
        state: state(&repo),
    })
}

struct PromptInfo {
    revision: String,
    behind: String,
    ahead: String,
    status: String,
    state: String,
}

fn get_branch(repo: &git2::Repository) -> MaybePrompt {
    repo.head().ok()
        .as_ref()
        .and_then(|h| h.shorthand())
        .map(|rev| if rev == "HEAD" { get_sha(repo)[..8].to_string() }
             else { rev.to_string() })
}

fn get_sha(repo: &git2::Repository) -> Prompt {
    repo.head()
        .ok()
        .map(|h| h.target().map(|t| t.to_string()))
        .unwrap_or(None)
        .unwrap_or(String::new())
}

fn ahead_count(repo: &git2::Repository) -> Prompt {
    non_zero("↑", count_between(repo, "origin/master", "HEAD"))
}

fn behind_count(repo: &git2::Repository) -> Prompt {
    non_zero("↓", count_between(repo, "HEAD", "origin/master"))
}

fn count_between(repo: &git2::Repository, from: &str, to: &str) -> usize {
    if let Ok(mut revwalk) = repo.revwalk() {
        if revwalk.push_range(&format!("{}..{}", from, to)).is_ok() {
            return revwalk.count();
        }
    }
    0
}

fn status(repo: &git2::Repository) -> MaybePrompt {
    let mut opts = git2::StatusOptions::new();
    opts.include_untracked(false);
    opts.exclude_submodules(true);

    let statuses = repo.statuses(Some(&mut opts)).ok()?;
    let mut changes: usize = 0;
    let mut staged: usize = 0;
    let mut conflicts: usize = 0;
    for entry in statuses.iter().filter(|e| e.status() != git2::Status::CURRENT) {
        let s = entry.status();
        // NOTE: a single entry might belong to multiple categories
        if is_index_change(&s)    {staged += 1}
        if is_worktree_change(&s) {changes += 1}
        if s.is_conflicted()      {conflicts += 1}
    }

    if changes == 0 && staged == 0 && conflicts == 0 {
         Some(format!("{}", "✔".green()))
    } else {
        Some(format!("{}{}{}",
             non_zero("•", staged).green(),
             non_zero("✖", conflicts).red(),
             non_zero("+", changes).yellow()))
    }
}

fn is_index_change(s: &git2::Status) -> bool {
    s.is_index_deleted() ||
        s.is_index_modified() ||
        s.is_index_new() ||
        s.is_index_renamed() ||
        s.is_index_typechange()
}

fn is_worktree_change(s: &git2::Status) -> bool {
    s.is_wt_deleted() ||
        s.is_wt_modified() ||
        s.is_wt_new() ||
        s.is_wt_renamed() ||
        s.is_wt_typechange()
}

fn non_zero(prefix: &str, number: usize) -> String {
    if number == 0 {
        String::new()
    } else {
        format!("{}{}", prefix, number)
    }
}

// https://docs.rs/git2/0.7.5/git2/enum.RepositoryState.html
fn state(repo: &git2::Repository) -> Prompt {
    match repo.state() {
        git2::RepositoryState::Merge => format!("{}", "merge".red()),
        git2::RepositoryState::Rebase => format!("{}", "rebase".yellow()),
        git2::RepositoryState::RebaseInteractive => format!("{}", "rebase".yellow()),
        _ => String::new()
    }
}
<commit_msg>get the branch when getting the behind/ahead count<commit_after>use colored::*;
use git2::Repository;
use std::env;

type MaybePrompt = Option<String>;
type Prompt = String;

fn main() {
    let path = env::var("PWD")
        .unwrap_or(String::new());
    print!("{} ", get_prompt(&path))
}

/// Returns the prompt with the git repository information at the given path
fn get_prompt(path: &str) -> Prompt {
    if let Some(i) = get_maybe_prompt(path) {
        format!("{} {}{}{} {}",
                i.revision.white(),
                i.behind.white(),
                i.ahead.white(),
                i.status,
                i.state)
    } else {
        String::new()
    }
}

#[cfg(test)]
mod get_prompt_tests {
    use super::get_prompt;
    use std::env;

    macro_rules! invalid_input_tests {
        ($($name:ident: $value:expr,)*) => {
        $(
            #[test]
            fn $name() {
                let (input, expected) = $value;
                assert_eq!(expected, get_prompt(input));
            }
        ) *
        }
    }

    invalid_input_tests! {
        empty_for_root: ("/", ""),
        empty_for_usr_local_bin: ("/usr/local/bin", ""),
        empty_for_non_existant_path: ("/tmp/i/do/not/exist", ""),
    }

    #[test]
    fn non_empty_for_pwd() {
        let path = env::var("PWD")
            .unwrap_or(String::new());
        assert_ne!(get_prompt(&path), "");
    }
}

fn get_maybe_prompt(path: &str) -> Option<PromptInfo> {
    let repo = Repository::discover(path).ok()?;

    Some(PromptInfo{
        revision: get_branch(&repo)
            .unwrap_or(get_sha(&repo)[..8].to_string()),
        behind: behind_count(&repo)?,
        ahead: ahead_count(&repo)?,
        status: status(&repo).unwrap_or(String::new()),
        state: state(&repo),
    })
}

struct PromptInfo {
    revision: String,
    behind: String,
    ahead: String,
    status: String,
    state: String,
}

fn get_branch(repo: &git2::Repository) -> MaybePrompt {
    let shorthand = repo.head()
        .ok()
        .as_ref()
        .and_then(|h| h.shorthand().map(String::from));

    if Some(String::from("HEAD")) == shorthand {
        None
    } else {
        shorthand
    }
}

fn get_sha(repo: &git2::Repository) -> Prompt {
    repo.head()
        .ok()
        .map(|h| h.target().map(|t| t.to_string()))
        .unwrap_or(None)
        .unwrap_or(String::new())
}

fn ahead_count(repo: &git2::Repository) -> MaybePrompt {
    let rev = format!("origin/{}", get_branch(repo)?);
    Some(non_zero("↑", count_between(repo, &rev, "HEAD")))
}

fn behind_count(repo: &git2::Repository) -> MaybePrompt {
    let rev = format!("origin/{}", get_branch(repo)?);
    Some(non_zero("↓", count_between(repo, "HEAD", &rev)))
}

fn count_between(repo: &git2::Repository, from: &str, to: &str) -> usize {
    if let Ok(mut revwalk) = repo.revwalk() {
        if revwalk.push_range(&format!("{}..{}", from, to)).is_ok() {
            return revwalk.count();
        }
    }
    0
}

fn status(repo: &git2::Repository) -> MaybePrompt {
    let mut opts = git2::StatusOptions::new();
    opts.include_untracked(false);
    opts.exclude_submodules(true);

    let statuses = repo.statuses(Some(&mut opts)).ok()?;
    let mut changes: usize = 0;
    let mut staged: usize = 0;
    let mut conflicts: usize = 0;
    for entry in statuses.iter().filter(|e| e.status() != git2::Status::CURRENT) {
        let s = entry.status();
        // NOTE: a single entry might belong to multiple categories
        if is_index_change(&s)    {staged += 1}
        if is_worktree_change(&s) {changes += 1}
        if s.is_conflicted()      {conflicts += 1}
    }

    if changes == 0 && staged == 0 && conflicts == 0 {
         Some(format!("{}", "✔".green()))
    } else {
        Some(format!("{}{}{}",
             non_zero("•", staged).green(),
             non_zero("✖", conflicts).red(),
             non_zero("+", changes).yellow()))
    }
}

fn is_index_change(s: &git2::Status) -> bool {
    s.is_index_deleted() ||
        s.is_index_modified() ||
        s.is_index_new() ||
        s.is_index_renamed() ||
        s.is_index_typechange()
}

fn is_worktree_change(s: &git2::Status) -> bool {
    s.is_wt_deleted() ||
        s.is_wt_modified() ||
        s.is_wt_new() ||
        s.is_wt_renamed() ||
        s.is_wt_typechange()
}

fn non_zero(prefix: &str, number: usize) -> String {
    if number == 0 {
        String::new()
    } else {
        format!("{}{}", prefix, number)
    }
}

// https://docs.rs/git2/0.7.5/git2/enum.RepositoryState.html
fn state(repo: &git2::Repository) -> Prompt {
    match repo.state() {
        git2::RepositoryState::Merge => format!("{}", "merge".red()),
        git2::RepositoryState::Rebase => format!("{}", "rebase".yellow()),
        git2::RepositoryState::RebaseInteractive => format!("{}", "rebase".yellow()),
        _ => String::new()
    }
}
<|endoftext|>


<commit_before>use std::path::Path;

use aes::Aes128;
use aes::BLOCK_SIZE;
use aes::MODE;

use xor::XOR;

use serialize::from_base64_file;
use serialize::from_hex;

use mac::hmac_sha1;
use mac::mac_sha1;

use hmac_client;
use hmac_server;

use rand;
use rand::Rng;

use set2::prefix_length;
use set2::random_block;

use errors::*;

use prefix_suffix_oracles::{Oracle, Oracle26};

// The following imports are only required for challenge 29
//
// use std::mem;
// use byteorder::{ByteOrder, BigEndian};
// use sha1::Sha1;

struct Encrypter25 {
    cleartext: Vec<u8>,
    key: Vec<u8>,
    ciphertext: Vec<u8>,
}

impl Encrypter25 {
    pub fn new() -> Result<Self, Error> {
        let cleartext = from_base64_file(Path::new("data/25.txt"))?;
        let key = random_block();
        let ciphertext = cleartext.encrypt(&key, None, MODE::CTR)?;
        Ok(Encrypter25 {
            cleartext,
            key,
            ciphertext,
        })
    }

    pub fn get_ciphertext(&self) -> &[u8] {
        &self.ciphertext
    }

    pub fn edit(&self, offset: usize, newtext: Vec<u8>) -> Result<Vec<u8>, Error> {
        let mut cleartext = self.cleartext.clone();
        let end = offset + newtext.len();
        if end > cleartext.len() {
            bail!("input out of bounds")
        }
        cleartext[offset..end].copy_from_slice(&newtext);
        cleartext.encrypt(&self.key, None, MODE::CTR)
    }

    pub fn verify_solution(&self, candidate_cleartext: &[u8]) -> Result<(), Error> {
        compare_eq(&self.cleartext[..], candidate_cleartext)
    }
}

fn challenge_25() -> Result<(), Error> {
    // This exercise is trivial: In CTR mode, if we know the underlying plaintext at some location,
    // we can trivially recover the used keystream by xor'ing the ciphertext with the
    // known plaintext. We simply use the edit function to set the entire cleartext to 0 so that
    // the ciphertext is even equal to the keystream.

    let encrypter = Encrypter25::new()?;
    let ciphertext = encrypter.get_ciphertext();
    let keystream = encrypter.edit(0, vec![0; ciphertext.len()])?;
    encrypter.verify_solution(&ciphertext.xor(&keystream))
}

fn challenge_26() -> Result<(), Error> {
    // This exercise is trivial: In CTR mode, if we know the underlying plaintext at some location,
    // we can inject any plaintext at the same location by xor'ing the ciphertext with
    // known_plaintext ^ target_plaintext.

    let oracle = Oracle26::new()?;
    let prefix_len = prefix_length(&oracle)?;
    let target_cleartext = b";admin=true";
    let mut ciphertext = oracle.encrypt(&vec![0; target_cleartext.len()])?;
    ciphertext.truncate(prefix_len + target_cleartext.len());
    ciphertext[prefix_len..].xor_inplace(target_cleartext);
    oracle.verify_solution(&ciphertext)
}

struct Sender27 {
    key: Vec<u8>,
}

struct Receiver27 {
    key: Vec<u8>,
}

impl Sender27 {
    pub fn encrypt(&self, input: &[u8]) -> Result<Vec<u8>, Error> {
        // Exclude ';' and '='
        if input
            .iter()
            .any(|&c| !c.is_ascii() || c == b';' || c == b'=')
        {
            bail!("invalid character in input");
        }

        let prefix = b"comment1=cooking%20MCs;userdata=";
        let suffix = b";comment2=%20like%20a%20pound%20of%20bacon";
        let mut cleartext = Vec::with_capacity(prefix.len() + input.len() + suffix.len());
        cleartext.extend_from_slice(prefix);
        cleartext.extend_from_slice(input);
        cleartext.extend_from_slice(suffix);
        cleartext.encrypt(&self.key, Some(&self.key), MODE::CBC)
    }
}

impl Receiver27 {
    pub fn try_decrypt(&self, ciphertext: &[u8]) -> Result<(), Error> {
        let cleartext = ciphertext.decrypt(&self.key, Some(&self.key), MODE::CBC)?;
        if !cleartext.is_ascii() {
            Err(NonAscii(cleartext).into())
        } else {
            Ok(())
        }
    }

    pub fn verify_solution(&self, candidate_key: &[u8]) -> Result<(), Error> {
        compare_eq(&self.key[..], candidate_key)
    }
}

fn get_sender_and_receiver_with_shared_key() -> (Sender27, Receiver27) {
    let secret_key = random_block();
    let sender = Sender27 {
        key: secret_key.clone(),
    };
    let receiver = Receiver27 {
        key: secret_key.clone(),
    };
    (sender, receiver)
}

#[derive(Debug, Fail)]
#[fail(display = "invalid input: {:?}", _0)]
struct NonAscii(Vec<u8>);

fn challenge_27() -> Result<(), Error> {
    let (sender, receiver) = get_sender_and_receiver_with_shared_key();

    let ciphertext = sender.encrypt(&[])?;

    let mut attack_ciphertext = Vec::with_capacity(3 * BLOCK_SIZE);
    attack_ciphertext.extend_from_slice(&ciphertext[0..BLOCK_SIZE]);
    attack_ciphertext.extend_from_slice(&[0; BLOCK_SIZE]);
    attack_ciphertext.extend_from_slice(&ciphertext[0..BLOCK_SIZE]);
    //Push last two blocks to preserve valid padding at the end
    attack_ciphertext.extend_from_slice(&ciphertext[ciphertext.len() - 2 * BLOCK_SIZE..]);

    if let Err(err) = receiver.try_decrypt(&attack_ciphertext) {
        if let Ok(NonAscii(attack_cleartext)) = err.downcast::<NonAscii>() {
            return receiver.verify_solution(
                &attack_cleartext[0..BLOCK_SIZE]
                    .xor(&attack_cleartext[2 * BLOCK_SIZE..3 * BLOCK_SIZE]),
            );
        }
    }

    bail!("attack ciphertext did not deceive the receiver");
}

fn challenge_28() -> Result<(), Error> {
    compare_eq(
        from_hex("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")?,
        mac_sha1(b"The quick brown fox ", b"jumps over the lazy dog"),
    )
}

/* The solution to challenge 29 requires a patched version of the sha1 crate.
 * To be precise, you have to clone the rust-sha1 repository and the apply the
 * following patch (tested for version 0.2.0 of rust-sha1):

------------------------------------
diff --git a/src/lib.rs b/src/lib.rs
index 352888b..325b94f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -74,6 +74,18 @@ impl Sha1 {
         }
     }

+    /// Creates a sha1 hash object with a predefined state.
+    pub fn new_with_state_and_length(state: [u32; 5], len: u64) -> Sha1 {
+        Sha1 {
+            state: Sha1State { state: state },
+            len: len,
+            blocks: Blocks {
+                len: 0,
+                block: [0; 64],
+            },
+        }
+    }
+
     /// Resets the hash object to it's initial state.
     pub fn reset(&mut self) {
         self.state = DEFAULT_STATE;
------------------------------------

Finally point Cargo.toml to your patched version of rust-sha1. */

/*
fn setup4_29() -> (Box<Fn(&[u8]) -> Vec<u8>>, Box<Fn(&[u8], &[u8]) -> bool>) {
    let mut rng = rand::thread_rng();
    let key_len = rng.gen_range(1, 200);
    let secret_key: Vec<u8> = rng.gen_iter().take(key_len).collect();

    let key = secret_key.clone();
    let compute_mac = move |message: &[u8]| mac_sha1(&key, message);

    let key = secret_key.clone();
    let verify_mac = move |message: &[u8], mac: &[u8]| &mac_sha1(&key, message)[..] == mac;
    (Box::new(compute_mac), Box::new(verify_mac))
}

fn padding(length: usize) -> Vec<u8> {
    let mut w = Vec::<u8>::with_capacity(137);
    w.extend_from_slice(&[0x80u8]);
    let padding = 64 - ((length + 9) % 64);
    for _ in 0..padding {
        w.push(0u8);
    }
    w.extend_from_slice(unsafe {
        &mem::transmute::<_, [u8; 8]>(
            (((length as u64) % 64 + (length as u64) / 64 * 64) * 8).to_be(),
        )
    });
    w
}

fn challenge_29() -> Result<(), Error> {
    let input = b"comment1=cooking%20MCs;userdata=foo;comment2=%20like%20a%20pound%20of%20bacon";
    let suffix = b";admin=true";

    let (compute_mac, verify_mac) = setup4_29();
    let hash = compute_mac(input);

    let mut state: [u32; 5] = [0; 5];
    for (b, s) in hash.chunks(4).zip(state.iter_mut()) {
        //Transmute four u8 values to u32

        *s = <BigEndian as ByteOrder>::read_u32(b)
    }

    for key_len in 0..200 {
        let secret_len = input.len() + key_len;
        let padding = padding(secret_len);
        let mut m2 = Sha1::new_with_state_and_length(state, (secret_len + padding.len()) as u64);
        m2.update(suffix);
        let mac = m2.digest().bytes();

        let mut message = Vec::new();
        message.extend_from_slice(input);
        message.extend_from_slice(&padding);
        message.extend_from_slice(suffix);
        if verify_mac(&message, &mac) {
            return Ok(());
        }
    }
    bail!("No matching message found.");
}
*/

fn challenge_29() -> Result<(), Error> {
    Err(ChallengeError::Skipped(
        "Requires a patched version of the sha1 crate. \
         See the source code for detailed instructions.",
    ).into())
}

fn challenge_30() -> Result<(), Error> {
    /* Skipping/postponing Challenge 30 because
     * 1) SHA1 was much more interesting anyway,
     * 2) no MD4 implementation seems to be available.  */

    Err(ChallengeError::NotImplemented.into())
}

fn challenge_31() -> Result<(), Error> {
    compare_eq(
        from_hex("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9")?,
        hmac_sha1(b"key", b"The quick brown fox jumps over the lazy dog"),
    )
}

fn challenge_32() -> Result<(), Error> {
    let skip_exercise = true;

    if skip_exercise {
        return Err(ChallengeError::Skipped("Runs very long.").into());
    }

    let mut rng = rand::thread_rng();
    let key: Vec<u8> = rng.gen_iter().take(20).collect();

    let mut server = hmac_server::start(key);

    hmac_client::run();

    server.close().context("failed to close connection")?;

    Ok(()) //TODO Add proper error handling
}

pub fn add_challenges(challenges: &mut Vec<fn() -> Result<(), Error>>) {
    challenges.push(challenge_25);
    challenges.push(challenge_26);
    challenges.push(challenge_27);
    challenges.push(challenge_28);
    challenges.push(challenge_29);
    challenges.push(challenge_30);
    challenges.push(challenge_31);
    challenges.push(challenge_32);
}
<commit_msg>Improve solution to challenge 29<commit_after>use std::path::Path;

use aes::Aes128;
use aes::BLOCK_SIZE;
use aes::MODE;

use xor::XOR;

use serialize::from_base64_file;
use serialize::from_hex;

use mac::hmac_sha1;
use mac::mac_sha1;

use hmac_client;
use hmac_server;

use rand;
use rand::Rng;

use set2::prefix_length;
use set2::random_block;

use errors::*;

use prefix_suffix_oracles::{Oracle, Oracle26};

// The following imports are only required for challenge 29
// use byteorder::{ByteOrder, BigEndian, WriteBytesExt};
// use sha1::Sha1;

struct Encrypter25 {
    cleartext: Vec<u8>,
    key: Vec<u8>,
    ciphertext: Vec<u8>,
}

impl Encrypter25 {
    pub fn new() -> Result<Self, Error> {
        let cleartext = from_base64_file(Path::new("data/25.txt"))?;
        let key = random_block();
        let ciphertext = cleartext.encrypt(&key, None, MODE::CTR)?;
        Ok(Encrypter25 {
            cleartext,
            key,
            ciphertext,
        })
    }

    pub fn get_ciphertext(&self) -> &[u8] {
        &self.ciphertext
    }

    pub fn edit(&self, offset: usize, newtext: Vec<u8>) -> Result<Vec<u8>, Error> {
        let mut cleartext = self.cleartext.clone();
        let end = offset + newtext.len();
        if end > cleartext.len() {
            bail!("input out of bounds")
        }
        cleartext[offset..end].copy_from_slice(&newtext);
        cleartext.encrypt(&self.key, None, MODE::CTR)
    }

    pub fn verify_solution(&self, candidate_cleartext: &[u8]) -> Result<(), Error> {
        compare_eq(&self.cleartext[..], candidate_cleartext)
    }
}

fn challenge_25() -> Result<(), Error> {
    // This exercise is trivial: In CTR mode, if we know the underlying plaintext at some location,
    // we can trivially recover the used keystream by xor'ing the ciphertext with the
    // known plaintext. We simply use the edit function to set the entire cleartext to 0 so that
    // the ciphertext is even equal to the keystream.

    let encrypter = Encrypter25::new()?;
    let ciphertext = encrypter.get_ciphertext();
    let keystream = encrypter.edit(0, vec![0; ciphertext.len()])?;
    encrypter.verify_solution(&ciphertext.xor(&keystream))
}

fn challenge_26() -> Result<(), Error> {
    // This exercise is trivial: In CTR mode, if we know the underlying plaintext at some location,
    // we can inject any plaintext at the same location by xor'ing the ciphertext with
    // known_plaintext ^ target_plaintext.

    let oracle = Oracle26::new()?;
    let prefix_len = prefix_length(&oracle)?;
    let target_cleartext = b";admin=true";
    let mut ciphertext = oracle.encrypt(&vec![0; target_cleartext.len()])?;
    ciphertext.truncate(prefix_len + target_cleartext.len());
    ciphertext[prefix_len..].xor_inplace(target_cleartext);
    oracle.verify_solution(&ciphertext)
}

struct Sender27 {
    key: Vec<u8>,
}

struct Receiver27 {
    key: Vec<u8>,
}

impl Sender27 {
    pub fn encrypt(&self, input: &[u8]) -> Result<Vec<u8>, Error> {
        // Exclude ';' and '='
        if input
            .iter()
            .any(|&c| !c.is_ascii() || c == b';' || c == b'=')
        {
            bail!("invalid character in input");
        }

        let prefix = b"comment1=cooking%20MCs;userdata=";
        let suffix = b";comment2=%20like%20a%20pound%20of%20bacon";
        let mut cleartext = Vec::with_capacity(prefix.len() + input.len() + suffix.len());
        cleartext.extend_from_slice(prefix);
        cleartext.extend_from_slice(input);
        cleartext.extend_from_slice(suffix);
        cleartext.encrypt(&self.key, Some(&self.key), MODE::CBC)
    }
}

impl Receiver27 {
    pub fn try_decrypt(&self, ciphertext: &[u8]) -> Result<(), Error> {
        let cleartext = ciphertext.decrypt(&self.key, Some(&self.key), MODE::CBC)?;
        if !cleartext.is_ascii() {
            Err(NonAscii(cleartext).into())
        } else {
            Ok(())
        }
    }

    pub fn verify_solution(&self, candidate_key: &[u8]) -> Result<(), Error> {
        compare_eq(&self.key[..], candidate_key)
    }
}

fn get_sender_and_receiver_with_shared_key() -> (Sender27, Receiver27) {
    let secret_key = random_block();
    let sender = Sender27 {
        key: secret_key.clone(),
    };
    let receiver = Receiver27 {
        key: secret_key.clone(),
    };
    (sender, receiver)
}

#[derive(Debug, Fail)]
#[fail(display = "invalid input: {:?}", _0)]
struct NonAscii(Vec<u8>);

fn challenge_27() -> Result<(), Error> {
    let (sender, receiver) = get_sender_and_receiver_with_shared_key();

    let ciphertext = sender.encrypt(&[])?;

    let mut attack_ciphertext = Vec::with_capacity(3 * BLOCK_SIZE);
    attack_ciphertext.extend_from_slice(&ciphertext[0..BLOCK_SIZE]);
    attack_ciphertext.extend_from_slice(&[0; BLOCK_SIZE]);
    attack_ciphertext.extend_from_slice(&ciphertext[0..BLOCK_SIZE]);
    //Push last two blocks to preserve valid padding at the end
    attack_ciphertext.extend_from_slice(&ciphertext[ciphertext.len() - 2 * BLOCK_SIZE..]);

    if let Err(err) = receiver.try_decrypt(&attack_ciphertext) {
        if let Ok(NonAscii(attack_cleartext)) = err.downcast::<NonAscii>() {
            return receiver.verify_solution(
                &attack_cleartext[0..BLOCK_SIZE]
                    .xor(&attack_cleartext[2 * BLOCK_SIZE..3 * BLOCK_SIZE]),
            );
        }
    }

    bail!("attack ciphertext did not deceive the receiver");
}

fn challenge_28() -> Result<(), Error> {
    compare_eq(
        from_hex("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")?,
        mac_sha1(b"The quick brown fox ", b"jumps over the lazy dog"),
    )
}

/* The solution to challenge 29 requires a patched version of the sha1 crate.
 * To be precise, you have to clone the rust-sha1 repository and the apply the
 * following patch (tested for version 0.2.0 of rust-sha1):

------------------------------------
diff --git a/src/lib.rs b/src/lib.rs
index 352888b..325b94f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -74,6 +74,18 @@ impl Sha1 {
         }
     }

+    /// Creates a sha1 hash object with a predefined state.
+    pub fn new_with_state_and_length(state: [u32; 5], len: u64) -> Sha1 {
+        Sha1 {
+            state: Sha1State { state: state },
+            len: len,
+            blocks: Blocks {
+                len: 0,
+                block: [0; 64],
+            },
+        }
+    }
+
     /// Resets the hash object to it's initial state.
     pub fn reset(&mut self) {
         self.state = DEFAULT_STATE;
------------------------------------

Finally point Cargo.toml to your patched version of rust-sha1. */

/*

struct Server29 {
    key: Vec<u8>
}

impl Server29 {
    pub fn new() -> Self {
        let mut rng = rand::thread_rng();
        let key_len = rng.gen_range(1, 200);
        let key: Vec<u8> = rng.gen_iter().take(key_len).collect();
        Server29 { key }
    }

    pub fn get_message_with_mac(&self) -> (Vec<u8>, Vec<u8>) {
        let message = b"comment1=cooking%20MCs;userdata=foo;comment2=%20like%20a%20pound%20of%20bacon".to_vec();
        let mac = mac_sha1(&self.key, &message);
        (message, mac)
    }

    pub fn verify_mac(&self, message: &[u8], mac: &[u8]) -> bool {
        mac_sha1(&self.key, message) == mac
    }
}

fn padding(length: usize) -> Vec<u8> {
    let mut w = Vec::new();
    w.push(0x80u8);
    let zero_padding_count = 64 - ((length + 9) % 64);
    for _ in 0..zero_padding_count {
        w.push(0u8);
    }
    w.write_u64::<BigEndian>((length as u64) * 8).unwrap();
    w
}

fn challenge_29() -> Result<(), Error> {
    let server = Server29::new();
    let (input, hash) = server.get_message_with_mac();
    let suffix = b";admin=true";

    // Split the 20 bytes of `hash` into chunks of four
    // bytes and transmute each chunk into an u32.
    let mut state: [u32; 5] = [0; 5];
    for (b, s) in hash.chunks(4).zip(state.iter_mut()) {
        *s = BigEndian::read_u32(b)
    }

    for key_len in 0..200 {
        let secret_len = input.len() + key_len;
        let padding = padding(secret_len);
        let mut m2 = Sha1::new_with_state_and_length(state, (secret_len + padding.len()) as u64);
        m2.update(suffix);
        let mac = m2.digest().bytes();

        let mut message = Vec::new();
        message.extend_from_slice(&input);
        message.extend_from_slice(&padding);
        message.extend_from_slice(suffix);
        if server.verify_mac(&message, &mac) {
            return Ok(());
        }
    }
    bail!("No matching message found.");
}
*/

fn challenge_29() -> Result<(), Error> {
    Err(ChallengeError::Skipped(
        "Requires a patched version of the sha1 crate. \
         See the source code for detailed instructions.",
    ).into())
}

fn challenge_30() -> Result<(), Error> {
    /* Skipping/postponing Challenge 30 because
     * 1) SHA1 was much more interesting anyway,
     * 2) no MD4 implementation seems to be available.  */

    Err(ChallengeError::NotImplemented.into())
}

fn challenge_31() -> Result<(), Error> {
    compare_eq(
        from_hex("de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9")?,
        hmac_sha1(b"key", b"The quick brown fox jumps over the lazy dog"),
    )
}

fn challenge_32() -> Result<(), Error> {
    let skip_exercise = true;

    if skip_exercise {
        return Err(ChallengeError::Skipped("Runs very long.").into());
    }

    let mut rng = rand::thread_rng();
    let key: Vec<u8> = rng.gen_iter().take(20).collect();

    let mut server = hmac_server::start(key);

    hmac_client::run();

    server.close().context("failed to close connection")?;

    Ok(()) //TODO Add proper error handling
}

pub fn add_challenges(challenges: &mut Vec<fn() -> Result<(), Error>>) {
    challenges.push(challenge_25);
    challenges.push(challenge_26);
    challenges.push(challenge_27);
    challenges.push(challenge_28);
    challenges.push(challenge_29);
    challenges.push(challenge_30);
    challenges.push(challenge_31);
    challenges.push(challenge_32);
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
<commit_msg>Remove rouge file<commit_after><|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Utilities to throw exceptions from Rust bindings.

use dom::bindings::conversions::ToJSValConvertible;
use dom::bindings::global::GlobalRef;
use dom::domexception::{DOMException, DOMErrorName};

use js::jsapi::{JSContext, JSBool, JSObject};
use js::jsapi::{JS_IsExceptionPending, JS_SetPendingException, JS_ReportPendingException};
use js::jsapi::{JS_ReportErrorNumber, JSErrorFormatString, JSEXN_TYPEERR};
use js::jsapi::{JS_SaveFrameChain, JS_RestoreFrameChain};
use js::glue::{ReportError};
use js::rust::with_compartment;

use libc;
use std::ffi::CString;
use std::ptr;

/// DOM exceptions that can be thrown by a native DOM method.
#[derive(Debug, Clone)]
pub enum Error {
    /// IndexSizeError DOMException
    IndexSize,
    /// NotFoundError DOMException
    NotFound,
    /// HierarchyRequestError DOMException
    HierarchyRequest,
    /// InvalidCharacterError DOMException
    InvalidCharacter,
    /// NotSupportedError DOMException
    NotSupported,
    /// InvalidStateError DOMException
    InvalidState,
    /// SyntaxError DOMException
    Syntax,
    /// NamespaceError DOMException
    NamespaceError,
    /// InvalidAccessError DOMException
    InvalidAccess,
    /// SecurityError DOMException
    Security,
    /// NetworkError DOMException
    Network,
    /// AbortError DOMException
    Abort,
    /// TimeoutError DOMException
    Timeout,
    /// DataCloneError DOMException
    DataClone,
    /// NoModificationAllowedError DOMException
    NoModificationAllowedError,
    /// A JavaScript exception is already pending.
    JSFailed,
}

/// The return type for IDL operations that can throw DOM exceptions.
pub type Fallible<T> = Result<T, Error>;

/// The return type for IDL operations that can throw DOM exceptions and
/// return `()`.
pub type ErrorResult = Fallible<()>;

/// Set a pending DOM exception for the given `result` on `cx`.
pub fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef,
                           result: Error) {
    assert!(unsafe { JS_IsExceptionPending(cx) } == 0);
    let code = match result {
        Error::IndexSize => DOMErrorName::IndexSizeError,
        Error::NotFound => DOMErrorName::NotFoundError,
        Error::HierarchyRequest => DOMErrorName::HierarchyRequestError,
        Error::InvalidCharacter => DOMErrorName::InvalidCharacterError,
        Error::NotSupported => DOMErrorName::NotSupportedError,
        Error::InvalidState => DOMErrorName::InvalidStateError,
        Error::Syntax => DOMErrorName::SyntaxError,
        Error::NamespaceError => DOMErrorName::NamespaceError,
        Error::InvalidAccess => DOMErrorName::InvalidAccessError,
        Error::Security => DOMErrorName::SecurityError,
        Error::Network => DOMErrorName::NetworkError,
        Error::Abort => DOMErrorName::AbortError,
        Error::Timeout => DOMErrorName::TimeoutError,
        Error::DataClone => DOMErrorName::DataCloneError,
        Error::NoModificationAllowedError => DOMErrorName::NoModificationAllowedError,
        Error::JSFailed => panic!(),
    };
    let exception = DOMException::new(global, code).root();
    let thrown = exception.to_jsval(cx);
    unsafe {
        JS_SetPendingException(cx, thrown);
    }
}

/// Report a pending exception, thereby clearing it.
pub fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) {
    unsafe {
        if JS_IsExceptionPending(cx) != 0 {
            let saved = JS_SaveFrameChain(cx);
            with_compartment(cx, obj, || {
                JS_ReportPendingException(cx);
            });
            if saved != 0 {
                JS_RestoreFrameChain(cx);
            }
        }
    }
}

/// Throw an exception to signal that a `JSVal` can not be converted to any of
/// the types in an IDL union type.
pub fn throw_not_in_union(cx: *mut JSContext, names: &'static str) -> JSBool {
    assert!(unsafe { JS_IsExceptionPending(cx) } == 0);
    let message = format!("argument could not be converted to any of: {}", names);
    let string = CString::from_slice(message.as_bytes());
    unsafe { ReportError(cx, string.as_ptr()) };
    return 0;
}

/// Format string used to throw `TypeError`s.
static ERROR_FORMAT_STRING_STRING: [libc::c_char; 4] = [
    '{' as libc::c_char,
    '0' as libc::c_char,
    '}' as libc::c_char,
    0 as libc::c_char,
];

/// Format string struct used to throw `TypeError`s.
static mut ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString {
    format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char,
    argCount: 1,
    exnType: JSEXN_TYPEERR as i16,
};

/// Callback used to throw `TypeError`s.
unsafe extern fn get_error_message(_user_ref: *mut libc::c_void,
                            _locale: *const libc::c_char,
                            error_number: libc::c_uint) -> *const JSErrorFormatString
{
    assert_eq!(error_number, 0);
    &ERROR_FORMAT_STRING as *const JSErrorFormatString
}

/// Throw a `TypeError` with the given message.
pub fn throw_type_error(cx: *mut JSContext, error: &str) {
    let error = CString::from_slice(error.as_bytes());
    unsafe {
        JS_ReportErrorNumber(cx,
            Some(get_error_message as
                unsafe extern "C" fn(*mut libc::c_void, *const libc::c_char,
                                     libc::c_uint) -> *const JSErrorFormatString),
            ptr::null_mut(), 0, error.as_ptr());
    }
}
<commit_msg>Add support for throwing TypeErrors from DOM implementations.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Utilities to throw exceptions from Rust bindings.

use dom::bindings::conversions::ToJSValConvertible;
use dom::bindings::global::GlobalRef;
use dom::domexception::{DOMException, DOMErrorName};

use util::str::DOMString;

use js::jsapi::{JSContext, JSBool, JSObject};
use js::jsapi::{JS_IsExceptionPending, JS_SetPendingException, JS_ReportPendingException};
use js::jsapi::{JS_ReportErrorNumber, JSErrorFormatString, JSEXN_TYPEERR};
use js::jsapi::{JS_SaveFrameChain, JS_RestoreFrameChain};
use js::glue::{ReportError};
use js::rust::with_compartment;

use libc;
use std::ffi::CString;
use std::ptr;

/// DOM exceptions that can be thrown by a native DOM method.
#[derive(Debug, Clone)]
pub enum Error {
    /// IndexSizeError DOMException
    IndexSize,
    /// NotFoundError DOMException
    NotFound,
    /// HierarchyRequestError DOMException
    HierarchyRequest,
    /// InvalidCharacterError DOMException
    InvalidCharacter,
    /// NotSupportedError DOMException
    NotSupported,
    /// InvalidStateError DOMException
    InvalidState,
    /// SyntaxError DOMException
    Syntax,
    /// NamespaceError DOMException
    NamespaceError,
    /// InvalidAccessError DOMException
    InvalidAccess,
    /// SecurityError DOMException
    Security,
    /// NetworkError DOMException
    Network,
    /// AbortError DOMException
    Abort,
    /// TimeoutError DOMException
    Timeout,
    /// DataCloneError DOMException
    DataClone,
    /// NoModificationAllowedError DOMException
    NoModificationAllowedError,

    /// TypeError JavaScript Error
    TypeError(DOMString),

    /// A JavaScript exception is already pending.
    JSFailed,
}

/// The return type for IDL operations that can throw DOM exceptions.
pub type Fallible<T> = Result<T, Error>;

/// The return type for IDL operations that can throw DOM exceptions and
/// return `()`.
pub type ErrorResult = Fallible<()>;

/// Set a pending DOM exception for the given `result` on `cx`.
pub fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef,
                           result: Error) {
    assert!(unsafe { JS_IsExceptionPending(cx) } == 0);
    let code = match result {
        Error::IndexSize => DOMErrorName::IndexSizeError,
        Error::NotFound => DOMErrorName::NotFoundError,
        Error::HierarchyRequest => DOMErrorName::HierarchyRequestError,
        Error::InvalidCharacter => DOMErrorName::InvalidCharacterError,
        Error::NotSupported => DOMErrorName::NotSupportedError,
        Error::InvalidState => DOMErrorName::InvalidStateError,
        Error::Syntax => DOMErrorName::SyntaxError,
        Error::NamespaceError => DOMErrorName::NamespaceError,
        Error::InvalidAccess => DOMErrorName::InvalidAccessError,
        Error::Security => DOMErrorName::SecurityError,
        Error::Network => DOMErrorName::NetworkError,
        Error::Abort => DOMErrorName::AbortError,
        Error::Timeout => DOMErrorName::TimeoutError,
        Error::DataClone => DOMErrorName::DataCloneError,
        Error::NoModificationAllowedError => DOMErrorName::NoModificationAllowedError,
        Error::TypeError(message) => {
            throw_type_error(cx, &message);
            return;
        }
        Error::JSFailed => panic!(),
    };
    let exception = DOMException::new(global, code).root();
    let thrown = exception.to_jsval(cx);
    unsafe {
        JS_SetPendingException(cx, thrown);
    }
}

/// Report a pending exception, thereby clearing it.
pub fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) {
    unsafe {
        if JS_IsExceptionPending(cx) != 0 {
            let saved = JS_SaveFrameChain(cx);
            with_compartment(cx, obj, || {
                JS_ReportPendingException(cx);
            });
            if saved != 0 {
                JS_RestoreFrameChain(cx);
            }
        }
    }
}

/// Throw an exception to signal that a `JSVal` can not be converted to any of
/// the types in an IDL union type.
pub fn throw_not_in_union(cx: *mut JSContext, names: &'static str) -> JSBool {
    assert!(unsafe { JS_IsExceptionPending(cx) } == 0);
    let message = format!("argument could not be converted to any of: {}", names);
    let string = CString::from_slice(message.as_bytes());
    unsafe { ReportError(cx, string.as_ptr()) };
    return 0;
}

/// Format string used to throw `TypeError`s.
static ERROR_FORMAT_STRING_STRING: [libc::c_char; 4] = [
    '{' as libc::c_char,
    '0' as libc::c_char,
    '}' as libc::c_char,
    0 as libc::c_char,
];

/// Format string struct used to throw `TypeError`s.
static mut ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString {
    format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char,
    argCount: 1,
    exnType: JSEXN_TYPEERR as i16,
};

/// Callback used to throw `TypeError`s.
unsafe extern fn get_error_message(_user_ref: *mut libc::c_void,
                            _locale: *const libc::c_char,
                            error_number: libc::c_uint) -> *const JSErrorFormatString
{
    assert_eq!(error_number, 0);
    &ERROR_FORMAT_STRING as *const JSErrorFormatString
}

/// Throw a `TypeError` with the given message.
pub fn throw_type_error(cx: *mut JSContext, error: &str) {
    let error = CString::from_slice(error.as_bytes());
    unsafe {
        JS_ReportErrorNumber(cx,
            Some(get_error_message as
                unsafe extern "C" fn(*mut libc::c_void, *const libc::c_char,
                                     libc::c_uint) -> *const JSErrorFormatString),
            ptr::null_mut(), 0, error.as_ptr());
    }
}
<|endoftext|>