<commit_before>use std::borrow::Cow;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
	let kernel_src = if has_feature("with_submodule") {
		KernelSrc::from_submodule()
	} else {
		KernelSrc::download()
	};

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn from_submodule() -> Self {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		Self { src_dir }
	}

	fn download() -> Self {
		let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
		let src_dir = out_dir.join("rusty-hermit");

		if !src_dir.exists() {
			let status = Command::new("cargo")
				.current_dir(out_dir)
				.arg("download")
				.arg("--output")
				.arg(&src_dir)
				.arg("--extract")
				.arg("rusty-hermit")
				.status()
				.expect("failed to start kernel download");
			assert!(
				status.success(),
				"Unable to download rusty-hermit. Is cargo-download installed?"
			);
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = {
			let mut target_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
			target_dir.push("target");
			target_dir
		};
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
		let profile = env::var("PROFILE").expect("PROFILE was not set");
		let mut cmd = Command::new("cargo");

		let kernel_triple = match target_arch.as_str() {
			"x86_64" => "x86_64-unknown-none-hermitkernel",
			"aarch64" => "aarch64-unknown-none-hermitkernel",
			_ => panic!("Unsupported target arch: {}", target_arch),
		};

		cmd.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg(kernel_triple)
			.arg("--manifest-path")
			.arg("Cargo.toml");

		cmd.current_dir(&self.src_dir);

		cmd.env_remove("RUSTUP_TOOLCHAIN");
		if option_env!("RUSTC_WORKSPACE_WRAPPER")
			.unwrap_or_default()
			.ends_with("clippy-driver")
		{
			cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
		}

		cmd.env("CARGO_TERM_COLOR", "always");

		cmd.arg("--target-dir").arg(&target_dir);

		if profile == "release" {
			cmd.arg("--release");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			["acpi", "fsgsbase", "pci", "smp", "vga"].into_iter(),
		);

		let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
		let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

		if has_feature("instrument") {
			rustflags.push("-Zinstrument-mcount".to_string());
			// Add outer rustflags to command
			rustflags.push(outer_rustflags);
		} else {
			// If the `instrument` feature feature is not enabled,
			// filter it from outer rustflags before adding them to the command.
			if !outer_rustflags.is_empty() {
				let flags = outer_rustflags
					.split('\x1f')
					.filter(|&flag| !flag.contains("instrument-mcount"))
					.map(String::from);
				rustflags.extend(flags);
			}
		}

		cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(kernel_triple)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("Lib location: {}", lib_location.display());

		let lib = lib_location.join("libhermit.a");

		let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

		if target_arch == "aarch64" {
			symbols.extend(include_str!("aarch64-duplicate-symbols").lines());
		} else if target_arch == "x86_64" {
			symbols.extend(include_str!("x86_64-duplicate-symbols").lines());
		}

		// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
		// Consequently, we rename the functions in the libos to avoid collisions.
		rename_symbols(symbols.iter(), &lib);

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.arg("--features");
		cmd.arg(features.join(" "));
	}
}

fn rename_symbols(symbols: impl Iterator<Item = impl AsRef<OsStr>>, lib: &Path) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib)
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}
<commit_msg>build.rs: Support no-op build for non-hermit OS'<commit_after>use std::borrow::Cow;
use std::env;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
	// TODO: Replace with is_some_with once stabilized
	// https://github.com/rust-lang/rust/issues/93050
	let targets_hermit =
		matches!(env::var_os("CARGO_CFG_TARGET_OS"), Some(os) if os == OsStr::new("hermit"));
	if !targets_hermit {
		return;
	}

	let kernel_src = if has_feature("with_submodule") {
		KernelSrc::from_submodule()
	} else {
		KernelSrc::download()
	};

	kernel_src.build();
}

struct KernelSrc {
	src_dir: PathBuf,
}

impl KernelSrc {
	fn from_submodule() -> Self {
		let mut src_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
		src_dir.set_file_name("libhermit-rs");
		Self { src_dir }
	}

	fn download() -> Self {
		let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
		let src_dir = out_dir.join("rusty-hermit");

		if !src_dir.exists() {
			let status = Command::new("cargo")
				.current_dir(out_dir)
				.arg("download")
				.arg("--output")
				.arg(&src_dir)
				.arg("--extract")
				.arg("rusty-hermit")
				.status()
				.expect("failed to start kernel download");
			assert!(
				status.success(),
				"Unable to download rusty-hermit. Is cargo-download installed?"
			);
		}

		Self { src_dir }
	}

	fn build(self) {
		let target_dir = {
			let mut target_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
			target_dir.push("target");
			target_dir
		};
		let manifest_path = self.src_dir.join("Cargo.toml");
		assert!(
			manifest_path.exists(),
			"kernel manifest path `{}` does not exist",
			manifest_path.display()
		);
		let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
		let profile = env::var("PROFILE").expect("PROFILE was not set");
		let mut cmd = Command::new("cargo");

		let kernel_triple = match target_arch.as_str() {
			"x86_64" => "x86_64-unknown-none-hermitkernel",
			"aarch64" => "aarch64-unknown-none-hermitkernel",
			_ => panic!("Unsupported target arch: {}", target_arch),
		};

		cmd.arg("build")
			.arg("-Z")
			.arg("build-std=core,alloc")
			.arg("--target")
			.arg(kernel_triple)
			.arg("--manifest-path")
			.arg("Cargo.toml");

		cmd.current_dir(&self.src_dir);

		cmd.env_remove("RUSTUP_TOOLCHAIN");
		if option_env!("RUSTC_WORKSPACE_WRAPPER")
			.unwrap_or_default()
			.ends_with("clippy-driver")
		{
			cmd.env("RUSTC_WORKSPACE_WRAPPER", "clippy-driver");
		}

		cmd.env("CARGO_TERM_COLOR", "always");

		cmd.arg("--target-dir").arg(&target_dir);

		if profile == "release" {
			cmd.arg("--release");
		}

		// Control enabled features via this crate's features
		cmd.arg("--no-default-features");
		forward_features(
			&mut cmd,
			["acpi", "fsgsbase", "pci", "smp", "vga"].into_iter(),
		);

		let mut rustflags = vec!["-Zmutable-noalias=no".to_string()];
		let outer_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap();

		if has_feature("instrument") {
			rustflags.push("-Zinstrument-mcount".to_string());
			// Add outer rustflags to command
			rustflags.push(outer_rustflags);
		} else {
			// If the `instrument` feature feature is not enabled,
			// filter it from outer rustflags before adding them to the command.
			if !outer_rustflags.is_empty() {
				let flags = outer_rustflags
					.split('\x1f')
					.filter(|&flag| !flag.contains("instrument-mcount"))
					.map(String::from);
				rustflags.extend(flags);
			}
		}

		cmd.env("CARGO_ENCODED_RUSTFLAGS", rustflags.join("\x1f"));

		let status = cmd.status().expect("failed to start kernel build");
		assert!(status.success());

		let lib_location = target_dir
			.join(kernel_triple)
			.join(&profile)
			.canonicalize()
			.unwrap();

		println!("Lib location: {}", lib_location.display());

		let lib = lib_location.join("libhermit.a");

		let mut symbols = vec!["rust_begin_unwind", "rust_oom"];

		if target_arch == "aarch64" {
			symbols.extend(include_str!("aarch64-duplicate-symbols").lines());
		} else if target_arch == "x86_64" {
			symbols.extend(include_str!("x86_64-duplicate-symbols").lines());
		}

		// Kernel and user space has its own versions of panic handler, oom handler, memcpy, memset, etc,
		// Consequently, we rename the functions in the libos to avoid collisions.
		rename_symbols(symbols.iter(), &lib);

		println!("cargo:rustc-link-search=native={}", lib_location.display());
		println!("cargo:rustc-link-lib=static=hermit");

		println!("cargo:rerun-if-changed={}", self.src_dir.display());
		// HERMIT_LOG_LEVEL_FILTER sets the log level filter at compile time
		println!("cargo:rerun-if-env-changed=HERMIT_LOG_LEVEL_FILTER");
	}
}

fn has_feature(feature: &str) -> bool {
	let mut var = "CARGO_FEATURE_".to_string();

	var.extend(feature.chars().map(|c| match c {
		'-' => '_',
		c => c.to_ascii_uppercase(),
	}));

	env::var_os(&var).is_some()
}

fn forward_features<'a>(cmd: &mut Command, features: impl Iterator<Item = &'a str>) {
	let features = features.filter(|f| has_feature(f)).collect::<Vec<_>>();
	if !features.is_empty() {
		cmd.arg("--features");
		cmd.arg(features.join(" "));
	}
}

fn rename_symbols(symbols: impl Iterator<Item = impl AsRef<OsStr>>, lib: &Path) {
	let args = symbols.into_iter().flat_map(|symbol| {
		let option = OsStr::new("--redefine-sym");
		let arg = [symbol.as_ref(), "=kernel-".as_ref(), symbol.as_ref()]
			.into_iter()
			.collect::<OsString>();
		[Cow::Borrowed(option), Cow::Owned(arg)]
	});

	let status = Command::new("rust-objcopy")
		.args(args)
		.arg(lib)
		.status()
		.expect("Failed to execute rust-objcopy. Is cargo-binutils installed?");
	assert!(status.success(), "rust-objcopy was not successful");
}
<|endoftext|>


<commit_before>// Unstable features coming in next Epoch
#![feature(match_default_bindings, nll, universal_impl_trait, conservative_impl_trait, dyn_trait)]
// Unstable features needed for light_arena
#![feature(placement_in_syntax, integer_atomics, vec_resize_default)]
// Lints
#![deny(unused_qualifications, unused_must_use)]
#![allow(non_snake_case)]

#[macro_use]
extern crate bitflags;
extern crate byteorder;
#[macro_use]
extern crate combine;
extern crate crossbeam;
#[macro_use]
extern crate failure;
extern crate image as img;
extern crate indicatif;
extern crate itertools as it;
#[macro_use]
extern crate lazy_static;
extern crate light_arena;
#[macro_use]
extern crate log;
extern crate ndarray;
extern crate ndarray_parallel;
extern crate num;
extern crate num_cpus;
#[cfg(feature = "exr")]
extern crate openexr;
extern crate parking_lot;
extern crate ply_rs;
extern crate rayon;
extern crate state;

#[cfg(test)]
#[macro_use]
extern crate approx;
#[cfg(test)]
#[macro_use]
extern crate quickcheck;

use std::f32;
use std::ops::{Add, Mul, Sub};

use num::{Num, One, Signed};

// stats needs to be declared first so its macros can be used by the following modules
#[macro_use]
mod stats;
mod api;
mod blockedarray;
pub mod bounds;
mod bsdf;
pub mod bvh;
pub mod camera;
mod cie;
pub mod display;
pub mod efloat;
mod fileutil;
pub mod film;
pub mod filter;
mod floatfile;
mod geometry;
pub mod integrator;
mod interaction;
mod interpolation;
pub mod imageio;
pub mod light;
pub mod lightdistrib;
pub mod material;
pub mod mipmap;
mod noise;
mod paramset;
pub mod pbrt;
pub mod primitive;
pub mod ray;
pub mod renderer;
pub mod rng;
pub mod sampling;
pub mod sampler;
pub mod scene;
pub mod shapes;
pub mod spectrum;
pub mod texture;
pub mod transform;

pub fn init_stats() {
    // This one needs to be called first
    stats::init_stats();
    api::init_stats();
    bvh::init_stats();
    film::init_stats();
    integrator::init_stats();
    lightdistrib::init_stats();
    mipmap::init_stats();
    renderer::init_stats();
    scene::init_stats();
    shapes::init_stats();
}

use geometry::{Normal3, Point2, Point3, Vector2, Vector3};
use spectrum::Spectrum;

pub type Vector2f = Vector2<f32>;
pub type Vector3f = Vector3<f32>;
pub type Point2f = Point2<f32>;
pub type Point2i = Point2<i32>;
pub type Point3f = Point3<f32>;
pub type Point3i = Point3<i32>;
pub type Normal3f = Normal3<f32>;

pub use transform::Transform;

pub const INV_2_PI: f32 = 0.15915494309189533577;
pub const MACHINE_EPSILON: f32 = f32::EPSILON * 0.5;
pub fn gamma(n: u32) -> f32 {
    (n as f32 * MACHINE_EPSILON) / (1.0 - n as f32 * MACHINE_EPSILON)
}

/// Smallest representable float strictly less than 1
pub const ONE_MINUS_EPSILON: f32 = 0.99999994f32;

#[derive(Debug, Copy, Clone, Default)]
pub struct PbrtOptions {
    pub num_threads: u8,
    pub quick_render: bool,
}

/// Linear interpolation between 2 values.
///
/// This version should be generic enough to linearly interpolate between 2 Spectrums using an f32
/// parameter.
pub fn lerp<S, T>(t: S, a: T, b: T) -> T
where
    S: One,
    S: Sub<S, Output = S>,
    S: Copy,
    T: Add<T, Output = T>,
    T: Mul<S, Output = T>,
{
    let one: S = num::one();
    a * (one - t) + b * t
}

/// Return the dimension index (0, 1 or 2) that contains the largest component.
pub fn max_dimension<T>(v: &Vector3<T>) -> usize
where
    T: Num + PartialOrd,
{
    if v.x > v.y {
        if v.x > v.z {
            0
        } else {
            2
        }
    } else if v.y > v.z {
        1
    } else {
        2
    }
}

pub fn max_component(v: &Vector3f) -> f32 {
    f32::max(v.x, f32::max(v.y, v.z))
}

/// Permute the components of this vector based on the given indices for x, y and z.
pub fn permute_v<T>(v: &Vector3<T>, x: usize, y: usize, z: usize) -> Vector3<T>
where
    T: Num + Copy,
{
    Vector3::new(v[x], v[y], v[z])
}

/// Permute the components of this point based on the given indices for x, y and z.
pub fn permute_p<T>(v: &Point3<T>, x: usize, y: usize, z: usize) -> Point3<T>
where
    T: Num + Signed + Copy,
{
    Point3::new(v[x], v[y], v[z])
}

/// Create an orthogonal coordinate system from a single vector.
pub fn coordinate_system(v1: &Vector3f) -> (Vector3f, Vector3f) {
    let v2 = if v1.x.abs() > v1.y.abs() {
        Vector3::new(-v1.z, 0.0, v1.x) / (v1.x * v1.x + v1.z * v1.z).sqrt()
    } else {
        Vector3::new(0.0, v1.z, -v1.y) / (v1.y * v1.y + v1.z * v1.z).sqrt()
    };

    let v3 = v1.cross(&v2);

    (v2, v3)
}

// TODO does this exist in std?
pub fn find_interval<P>(size: usize, pred: P) -> usize
where
    P: Fn(usize) -> bool,
{
    let mut first = 0;
    let mut len = size;
    while len > 0 {
        let half = len >> 1;
        let middle = first + half;
        // Bisect range based on value of _pred_ at _middle_
        if pred(middle as usize) {
            first = middle + 1;
            len -= half + 1;
        } else {
            len = half;
        }
    }
    clamp(first as isize - 1, 0, size as isize - 2) as usize
}

/// Version of min() that works on `PartialOrd`, so it works for both u32 and f32.
pub fn min<T: PartialOrd + Copy>(a: T, b: T) -> T {
    if a.lt(&b) {
        a
    } else {
        b
    }
}

/// Version of max() that works on `PartialOrd`, so it works for both u32 and f32.
pub fn max<T: PartialOrd + Copy>(a: T, b: T) -> T {
    if a.gt(&b) {
        a
    } else {
        b
    }
}

#[inline]
pub fn is_power_of_2(v: i32) -> bool {
    (v != 0) && (v & (v - 1)) == 0
}

#[inline]
pub fn round_up_pow_2(v: i32) -> i32 {
    let mut v = v;
    v -= 1;
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v + 1
}

#[inline]
pub fn next_float_up(v: f32) -> f32 {
    let mut v = v;
    if v.is_infinite() && v > 0.0 {
        return v;
    }

    if v == -0.0 {
        v = 0.0;
    }
    let mut ui = v.to_bits();
    if v >= 0.0 {
        ui += 1;
    } else {
        ui -= 1;
    }
    f32::from_bits(ui)
}

#[inline]
pub fn next_float_down(v: f32) -> f32 {
    let mut v = v;
    if v.is_infinite() && v < 0.0 {
        return v;
    }

    if v == 0.0 {
        v = -0.0;
    }
    let mut ui = v.to_bits();
    if v > 0.0 {
        ui -= 1;
    } else {
        ui += 1;
    }
    f32::from_bits(ui)
}

pub fn clamp<T>(val: T, low: T, high: T) -> T
where
    T: PartialOrd + Copy,
{
    if val < low {
        low
    } else if val > high {
        high
    } else {
        val
    }
}

pub trait Clampable {
    fn clamp(self, min: f32, max: f32) -> Self;
}

impl Clampable for f32 {
    fn clamp(self, min: f32, max: f32) -> f32 {
        clamp(self, min, max)
    }
}

impl Clampable for Spectrum {
    fn clamp(self, min: f32, max: f32) -> Spectrum {
        Spectrum::rgb(
            self.r.clamp(min, max),
            self.g.clamp(min, max),
            self.b.clamp(min, max),
        )
    }
}

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

    #[test]
    fn test_find_interval() {
        let a = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

        // check clamping for out of range
        assert_eq!(0, find_interval(a.len(), |index| a[index] as isize <= -1));
        assert_eq!(a.len() - 2, find_interval(a.len(), |index| a[index] <= 100));

        for i in 0..a.len() - 1 {
            assert_eq!(i, find_interval(a.len(), |index| a[index] <= i));
            assert_eq!(
                i,
                find_interval(a.len(), |index| a[index] as f32 <= i as f32 + 0.5)
            );
            if i > 0 {
                assert_eq!(
                    i - 1,
                    find_interval(a.len(), |index| a[index] as f32 <= i as f32 - 0.5)
                );
            }
        }
    }

    #[test]
    fn test_gamma() {
        let g5 = gamma(5);
        let p = Point3f::new(-0.4, 0.9, 0.2);
        let v = g5 * p.abs();
        println!("gamma(5) = {}, p={:?}, v={:?}", gamma(5), p, v);
    }

    #[test]
    fn test_is_power_of_2() {
        assert!(is_power_of_2(4));
        assert!(is_power_of_2(8));
        assert!(is_power_of_2(1024));
        assert!(!is_power_of_2(3));
        assert!(!is_power_of_2(7));
    }

    #[test]
    fn test_round_up_pow_2() {
        assert_eq!(round_up_pow_2(1023), 1024);
        assert_eq!(round_up_pow_2(1024), 1024);
    }
}
<commit_msg>Disable nll feature as it's still quite unstable<commit_after>// Unstable features coming in next Epoch
#![feature(match_default_bindings, universal_impl_trait, conservative_impl_trait, dyn_trait)]
// Unstable features needed for light_arena
#![feature(placement_in_syntax, integer_atomics, vec_resize_default)]
// Lints
#![deny(unused_qualifications, unused_must_use)]
#![allow(non_snake_case)]

#[macro_use]
extern crate bitflags;
extern crate byteorder;
#[macro_use]
extern crate combine;
extern crate crossbeam;
#[macro_use]
extern crate failure;
extern crate image as img;
extern crate indicatif;
extern crate itertools as it;
#[macro_use]
extern crate lazy_static;
extern crate light_arena;
#[macro_use]
extern crate log;
extern crate ndarray;
extern crate ndarray_parallel;
extern crate num;
extern crate num_cpus;
#[cfg(feature = "exr")]
extern crate openexr;
extern crate parking_lot;
extern crate ply_rs;
extern crate rayon;
extern crate state;

#[cfg(test)]
#[macro_use]
extern crate approx;
#[cfg(test)]
#[macro_use]
extern crate quickcheck;

use std::f32;
use std::ops::{Add, Mul, Sub};

use num::{Num, One, Signed};

// stats needs to be declared first so its macros can be used by the following modules
#[macro_use]
mod stats;
mod api;
mod blockedarray;
pub mod bounds;
mod bsdf;
pub mod bvh;
pub mod camera;
mod cie;
pub mod display;
pub mod efloat;
mod fileutil;
pub mod film;
pub mod filter;
mod floatfile;
mod geometry;
pub mod integrator;
mod interaction;
mod interpolation;
pub mod imageio;
pub mod light;
pub mod lightdistrib;
pub mod material;
pub mod mipmap;
mod noise;
mod paramset;
pub mod pbrt;
pub mod primitive;
pub mod ray;
pub mod renderer;
pub mod rng;
pub mod sampling;
pub mod sampler;
pub mod scene;
pub mod shapes;
pub mod spectrum;
pub mod texture;
pub mod transform;

pub fn init_stats() {
    // This one needs to be called first
    stats::init_stats();
    api::init_stats();
    bvh::init_stats();
    film::init_stats();
    integrator::init_stats();
    lightdistrib::init_stats();
    mipmap::init_stats();
    renderer::init_stats();
    scene::init_stats();
    shapes::init_stats();
}

use geometry::{Normal3, Point2, Point3, Vector2, Vector3};
use spectrum::Spectrum;

pub type Vector2f = Vector2<f32>;
pub type Vector3f = Vector3<f32>;
pub type Point2f = Point2<f32>;
pub type Point2i = Point2<i32>;
pub type Point3f = Point3<f32>;
pub type Point3i = Point3<i32>;
pub type Normal3f = Normal3<f32>;

pub use transform::Transform;

pub const INV_2_PI: f32 = 0.15915494309189533577;
pub const MACHINE_EPSILON: f32 = f32::EPSILON * 0.5;
pub fn gamma(n: u32) -> f32 {
    (n as f32 * MACHINE_EPSILON) / (1.0 - n as f32 * MACHINE_EPSILON)
}

/// Smallest representable float strictly less than 1
pub const ONE_MINUS_EPSILON: f32 = 0.99999994f32;

#[derive(Debug, Copy, Clone, Default)]
pub struct PbrtOptions {
    pub num_threads: u8,
    pub quick_render: bool,
}

/// Linear interpolation between 2 values.
///
/// This version should be generic enough to linearly interpolate between 2 Spectrums using an f32
/// parameter.
pub fn lerp<S, T>(t: S, a: T, b: T) -> T
where
    S: One,
    S: Sub<S, Output = S>,
    S: Copy,
    T: Add<T, Output = T>,
    T: Mul<S, Output = T>,
{
    let one: S = num::one();
    a * (one - t) + b * t
}

/// Return the dimension index (0, 1 or 2) that contains the largest component.
pub fn max_dimension<T>(v: &Vector3<T>) -> usize
where
    T: Num + PartialOrd,
{
    if v.x > v.y {
        if v.x > v.z {
            0
        } else {
            2
        }
    } else if v.y > v.z {
        1
    } else {
        2
    }
}

pub fn max_component(v: &Vector3f) -> f32 {
    f32::max(v.x, f32::max(v.y, v.z))
}

/// Permute the components of this vector based on the given indices for x, y and z.
pub fn permute_v<T>(v: &Vector3<T>, x: usize, y: usize, z: usize) -> Vector3<T>
where
    T: Num + Copy,
{
    Vector3::new(v[x], v[y], v[z])
}

/// Permute the components of this point based on the given indices for x, y and z.
pub fn permute_p<T>(v: &Point3<T>, x: usize, y: usize, z: usize) -> Point3<T>
where
    T: Num + Signed + Copy,
{
    Point3::new(v[x], v[y], v[z])
}

/// Create an orthogonal coordinate system from a single vector.
pub fn coordinate_system(v1: &Vector3f) -> (Vector3f, Vector3f) {
    let v2 = if v1.x.abs() > v1.y.abs() {
        Vector3::new(-v1.z, 0.0, v1.x) / (v1.x * v1.x + v1.z * v1.z).sqrt()
    } else {
        Vector3::new(0.0, v1.z, -v1.y) / (v1.y * v1.y + v1.z * v1.z).sqrt()
    };

    let v3 = v1.cross(&v2);

    (v2, v3)
}

// TODO does this exist in std?
pub fn find_interval<P>(size: usize, pred: P) -> usize
where
    P: Fn(usize) -> bool,
{
    let mut first = 0;
    let mut len = size;
    while len > 0 {
        let half = len >> 1;
        let middle = first + half;
        // Bisect range based on value of _pred_ at _middle_
        if pred(middle as usize) {
            first = middle + 1;
            len -= half + 1;
        } else {
            len = half;
        }
    }
    clamp(first as isize - 1, 0, size as isize - 2) as usize
}

/// Version of min() that works on `PartialOrd`, so it works for both u32 and f32.
pub fn min<T: PartialOrd + Copy>(a: T, b: T) -> T {
    if a.lt(&b) {
        a
    } else {
        b
    }
}

/// Version of max() that works on `PartialOrd`, so it works for both u32 and f32.
pub fn max<T: PartialOrd + Copy>(a: T, b: T) -> T {
    if a.gt(&b) {
        a
    } else {
        b
    }
}

#[inline]
pub fn is_power_of_2(v: i32) -> bool {
    (v != 0) && (v & (v - 1)) == 0
}

#[inline]
pub fn round_up_pow_2(v: i32) -> i32 {
    let mut v = v;
    v -= 1;
    v |= v >> 1;
    v |= v >> 2;
    v |= v >> 4;
    v |= v >> 8;
    v |= v >> 16;
    v + 1
}

#[inline]
pub fn next_float_up(v: f32) -> f32 {
    let mut v = v;
    if v.is_infinite() && v > 0.0 {
        return v;
    }

    if v == -0.0 {
        v = 0.0;
    }
    let mut ui = v.to_bits();
    if v >= 0.0 {
        ui += 1;
    } else {
        ui -= 1;
    }
    f32::from_bits(ui)
}

#[inline]
pub fn next_float_down(v: f32) -> f32 {
    let mut v = v;
    if v.is_infinite() && v < 0.0 {
        return v;
    }

    if v == 0.0 {
        v = -0.0;
    }
    let mut ui = v.to_bits();
    if v > 0.0 {
        ui -= 1;
    } else {
        ui += 1;
    }
    f32::from_bits(ui)
}

pub fn clamp<T>(val: T, low: T, high: T) -> T
where
    T: PartialOrd + Copy,
{
    if val < low {
        low
    } else if val > high {
        high
    } else {
        val
    }
}

pub trait Clampable {
    fn clamp(self, min: f32, max: f32) -> Self;
}

impl Clampable for f32 {
    fn clamp(self, min: f32, max: f32) -> f32 {
        clamp(self, min, max)
    }
}

impl Clampable for Spectrum {
    fn clamp(self, min: f32, max: f32) -> Spectrum {
        Spectrum::rgb(
            self.r.clamp(min, max),
            self.g.clamp(min, max),
            self.b.clamp(min, max),
        )
    }
}

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

    #[test]
    fn test_find_interval() {
        let a = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

        // check clamping for out of range
        assert_eq!(0, find_interval(a.len(), |index| a[index] as isize <= -1));
        assert_eq!(a.len() - 2, find_interval(a.len(), |index| a[index] <= 100));

        for i in 0..a.len() - 1 {
            assert_eq!(i, find_interval(a.len(), |index| a[index] <= i));
            assert_eq!(
                i,
                find_interval(a.len(), |index| a[index] as f32 <= i as f32 + 0.5)
            );
            if i > 0 {
                assert_eq!(
                    i - 1,
                    find_interval(a.len(), |index| a[index] as f32 <= i as f32 - 0.5)
                );
            }
        }
    }

    #[test]
    fn test_gamma() {
        let g5 = gamma(5);
        let p = Point3f::new(-0.4, 0.9, 0.2);
        let v = g5 * p.abs();
        println!("gamma(5) = {}, p={:?}, v={:?}", gamma(5), p, v);
    }

    #[test]
    fn test_is_power_of_2() {
        assert!(is_power_of_2(4));
        assert!(is_power_of_2(8));
        assert!(is_power_of_2(1024));
        assert!(!is_power_of_2(3));
        assert!(!is_power_of_2(7));
    }

    #[test]
    fn test_round_up_pow_2() {
        assert_eq!(round_up_pow_2(1023), 1024);
        assert_eq!(round_up_pow_2(1024), 1024);
    }
}
<|endoftext|>


<commit_before>
use prelude::*;
use tyhandlers::TypeSystem;
use syn::*;
use syn::synom::Parser;

use error::MacroError;
use super::*;

use ast_converters::*;

pub fn parse_attr_tokens(
    attr_name: &str,
    attr_tokens: &str,
) -> Result< Attribute, MacroError >
{
    let attr_rendered = format!( "#[{}({})]", attr_name, attr_tokens );
    if let Ok( tt ) = attr_rendered.parse() {
        if let Ok( t ) = Attribute::parse_outer.parse2( tt ) {
            return Ok( t )
        }
    }

    Err( MacroError {
        msg: format!( "Could not parse [{}] attribute", attr_name )
    } )
}

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum InterfaceType { Trait, Struct }

pub type InterfaceData<'a> = (
    Ident,
    Vec< &'a MethodSig >,
    InterfaceType,
    Option<Token!(unsafe)>,
);

pub fn get_ident_and_fns(
    item : &Item
) -> Option< InterfaceData >
{
    match *item {
        Item::Impl( ItemImpl {
                ref unsafety,
                ref trait_,
                ref self_ty,
                ref items,
                .. } )
            => {
                let ( _, struct_ident, items ) =
                        get_impl_data_raw( trait_, self_ty, items );
                Some( (
                        struct_ident,
                        items,
                        InterfaceType::Struct,
                        *unsafety ) )
            },
        Item::Trait( ItemTrait {
                ref ident,
                unsafety,
                ref items,
                .. } )
            => {

            let methods : Option< Vec< &MethodSig > > = items
                    .into_iter()
                    .map( |i| get_trait_method( i ) )
                    .collect();

            match methods {
                Some( m ) => Some( (
                        ident.clone(),
                        m,
                        InterfaceType::Trait,
                        unsafety,
                    ) ),
                None => None
            }
        },
        _ => None
    }
}

pub type ImplData<'a> = (
    Option<Ident>,
    Ident,
    Vec< &'a MethodSig >
);

pub fn get_impl_data(
    item : &Item
) -> Option< ImplData >
{
    if let Item::Impl( ItemImpl { ref trait_, ref self_ty, ref items, .. } ) = *item {
        return Some( get_impl_data_raw( trait_, self_ty, items ) );
    }
    None
}

fn get_impl_data_raw<'a>(
    trait_ref : &'a Option<( Option<Token!(!)>, Path, Token!(for) )>,
    struct_ty : &'a Type,
    items : &'a [ImplItem]
) -> ImplData<'a>
{

    let struct_ident = struct_ty.get_ident().unwrap();
    let trait_ident = match *trait_ref {
        Some( ( _, ref path, _ ) ) => path.get_ident().ok(),
        None => None
    };

    let methods_opt : Option< Vec< &MethodSig > > = items
            .into_iter()
            .map( |i| get_impl_method( i ) )
            .collect();
    let methods = methods_opt.unwrap_or_else( || vec![] );

    ( trait_ident, struct_ident, methods )
}

#[derive(PartialEq, Eq, Debug)]
pub enum AttrParam {
    Literal( Lit ),
    Word( Ident ),
}

pub fn iter_parameters(
    attr : &syn::Attribute
) -> Box< Iterator<Item = AttrParam > >
{
    match attr.interpret_meta() {

        Some( Meta::List( MetaList { ref nested, .. } ) ) =>
            Box::new( nested.to_owned().into_iter().map( |i| {

                match i {

                    NestedMeta::Meta( meta ) =>
                        AttrParam::Word( match meta {
                            Meta::Word( i ) => i,
                            Meta::List( l ) => l.ident,
                            Meta::NameValue( nv ) => nv.ident,
                        } ),

                    syn::NestedMeta::Literal( l ) =>
                            AttrParam::Literal( l ),
                }
            } ) ),

        // Attributes without parameter lists don't have params.
        None
            | Some( Meta::Word(..) )
            | Some( Meta::NameValue(..) ) => Box::new( std::iter::empty() ),

    }
}

pub fn get_impl_method(
    i : &ImplItem
) -> Option< &MethodSig >
{
    match *i {
        ImplItem::Method( ref itm ) => Some( &itm.sig ),
        _ => None
    }
}

pub fn get_trait_method(
    i : &TraitItem
) -> Option< &MethodSig >
{
    match *i {
        TraitItem::Method( ref tim ) => Some( &tim.sig ),
        _ => None
    }
}

const AUTO_GUID_BASE : guid::GUID = guid::GUID {
    data1: 0x4449_494C,
    data2: 0xDE1F,
    data3: 0x4525,
    data4: [ 0xB9, 0x57, 0x89, 0xD6, 0x0C, 0xE9, 0x34, 0x77 ]
};

/*
pub fn parameter_to_guid(
    p : &AttrParam,
    crate_name : &str,
    item_name : &str,
    item_type : &str,
) -> Result< Option< guid::GUID >, String >
{
    if let AttrParam::Word( ref i ) = *p {
        return Ok( match i.to_string().as_ref() {
            "AUTO_GUID" =>
                Some( generate_guid( crate_name, item_name, item_type ) ),
            "NO_GUID" =>
                None,
            _ => return Err( format!( "Invalid GUID: {:?}", i ) ),
        } );
    }

    if let AttrParam::Literal( Lit::Str( ref s ) ) = *p {
        return Ok( Some( guid::GUID::parse( &s.value() )? ) );
    }

    Err( "GUID parameter must be literal string".to_owned() )
}
*/

pub fn generate_iid(
    crate_name : &str,
    item_name : &str,
    type_system : TypeSystem,
) -> guid::GUID
{
    generate_guid( &[
            "IID",
            crate_name,
            item_name,
            match type_system {
                TypeSystem::Automation => "automation",
                TypeSystem::Raw => "raw",
                TypeSystem::Invariant => "invariant",
            }
        ].join( ":" ) )
}

pub fn generate_libid(
    crate_name : &str,
) -> guid::GUID
{
    generate_guid( &[
            "LIBID",
            crate_name,
        ].join( ":" ) )
}

pub fn generate_clsid(
    crate_name : &str,
    item_name : &str,
) -> guid::GUID
{
    generate_guid( &[
            "CLSID",
            crate_name,
            item_name,
        ].join( ":" ) )
}

pub fn generate_guid(
    key : &str,
) -> guid::GUID
{
    // Hash the name. The name will be hashed in a form similar to:
    // AUTO_GUID_BASE + "CLSID:random_rust_crate:FooBar"
    let mut hash = sha1::Sha1::new();
    hash.update( AUTO_GUID_BASE.as_bytes() );
    hash.update( key.as_bytes() );

    let digest = hash.digest();
    let bytes = digest.bytes();

    // Set the GUID bytes according to RFC-4122, section 4.3.
    let time_low : u32
        = ( u32::from( bytes[0] ) << 24 )
        + ( u32::from( bytes[1] ) << 16 )
        + ( u32::from( bytes[2] ) << 8 )
        + u32::from( bytes[3] );
    let time_mid : u16
        = ( u16::from( bytes[4] ) << 8 )
        + ( u16::from( bytes[5] ) );
    let time_hi_and_version : u16
        = (
            (
                ( u16::from( bytes[6] ) << 8 ) + u16::from( bytes[7] )
            ) & 0x0fff
        ) | 0x3000;
    let clk_seq_hi_res : u8
        = ( bytes[8] & 0b0011_1111 ) | 0b0100_0000;
    let clk_seq_low : u8 = bytes[9];

    guid::GUID {
        data1: time_low,
        data2: time_mid,
        data3: time_hi_and_version,
        data4: [
            clk_seq_hi_res, clk_seq_low,
            bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15] ]
    }
}

pub fn ty_to_string( ty : &syn::Type ) -> String
{
    quote!( #ty ).to_string().replace( " ", "" ).replace( ",", ", " )
}

pub fn is_unit(
    tk : &Type
) -> bool
{
    if let Type::Tuple( ref t ) = *tk {
        t.elems.is_empty()
    } else {
        false
    }
}

pub fn unit_ty() -> Type
{
    parse_quote!( () )
}

pub fn get_guid_tokens(
    g : &guid::GUID
) -> TokenStream
{
    let d1 = g.data1;
    let d2 = g.data2;
    let d3 = g.data3;
    let d4_0 = g.data4[ 0 ];
    let d4_1 = g.data4[ 1 ];
    let d4_2 = g.data4[ 2 ];
    let d4_3 = g.data4[ 3 ];
    let d4_4 = g.data4[ 4 ];
    let d4_5 = g.data4[ 5 ];
    let d4_6 = g.data4[ 6 ];
    let d4_7 = g.data4[ 7 ];
    quote!(
        ::intercom::GUID {
            data1: #d1, data2: #d2, data3: #d3,
            data4: [ #d4_0, #d4_1, #d4_2, #d4_3, #d4_4, #d4_5, #d4_6, #d4_7 ]
        }
    )
}

/// Convert the Rust identifier from `snake_case` to `PascalCase`
pub fn pascal_case<T: AsRef<str>>( input : T ) -> String {
    let input = input.as_ref();

    // Allocate the output string. We'll never increase the amount of
    // characters so we can reserve string buffer using the input string length.
    let mut output = String::new();
    output.reserve( input.len() );

    // Process each character from the input.
    let mut capitalize = true;
    for c in input.chars() {

        // Check the capitalization requirement.
        if c == '_' {

            // Skip '_' but capitalize the following character.
            capitalize = true;

        } else if capitalize {

            // Capitalize. Add the uppercase characters.
            for c_up in c.to_uppercase() {
                output.push( c_up )
            }

            // No need to capitalize any more.
            capitalize = false;

        } else {

            // No need to capitalize. Just add the character as is.
            output.push( c );
        }

    }
    output
}

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

    /// Tests the `ty_to_string` by converting parameter to Type and back to
    /// String to ensure they equal.
    fn test_ty( ty_str : &str ) {
        let ty = parse_str( ty_str ).unwrap();
        let as_string = ty_to_string( &ty );
        assert_eq!( ty_str, as_string );
    }

    #[test] fn path_to_test() { test_ty( "::path::Foo" ) }
    #[test] fn generics_to_test() { test_ty( "Result<Foo, Bar>" ) }
    #[test] fn unit_to_test() { test_ty( "()" ) }
}
<commit_msg>Remove deprecated param to guid function<commit_after>
use prelude::*;
use tyhandlers::TypeSystem;
use syn::*;
use syn::synom::Parser;

use error::MacroError;
use super::*;

use ast_converters::*;

pub fn parse_attr_tokens(
    attr_name: &str,
    attr_tokens: &str,
) -> Result< Attribute, MacroError >
{
    let attr_rendered = format!( "#[{}({})]", attr_name, attr_tokens );
    if let Ok( tt ) = attr_rendered.parse() {
        if let Ok( t ) = Attribute::parse_outer.parse2( tt ) {
            return Ok( t )
        }
    }

    Err( MacroError {
        msg: format!( "Could not parse [{}] attribute", attr_name )
    } )
}

#[derive(PartialEq, Debug, Clone, Copy)]
pub enum InterfaceType { Trait, Struct }

pub type InterfaceData<'a> = (
    Ident,
    Vec< &'a MethodSig >,
    InterfaceType,
    Option<Token!(unsafe)>,
);

pub fn get_ident_and_fns(
    item : &Item
) -> Option< InterfaceData >
{
    match *item {
        Item::Impl( ItemImpl {
                ref unsafety,
                ref trait_,
                ref self_ty,
                ref items,
                .. } )
            => {
                let ( _, struct_ident, items ) =
                        get_impl_data_raw( trait_, self_ty, items );
                Some( (
                        struct_ident,
                        items,
                        InterfaceType::Struct,
                        *unsafety ) )
            },
        Item::Trait( ItemTrait {
                ref ident,
                unsafety,
                ref items,
                .. } )
            => {

            let methods : Option< Vec< &MethodSig > > = items
                    .into_iter()
                    .map( |i| get_trait_method( i ) )
                    .collect();

            match methods {
                Some( m ) => Some( (
                        ident.clone(),
                        m,
                        InterfaceType::Trait,
                        unsafety,
                    ) ),
                None => None
            }
        },
        _ => None
    }
}

pub type ImplData<'a> = (
    Option<Ident>,
    Ident,
    Vec< &'a MethodSig >
);

pub fn get_impl_data(
    item : &Item
) -> Option< ImplData >
{
    if let Item::Impl( ItemImpl { ref trait_, ref self_ty, ref items, .. } ) = *item {
        return Some( get_impl_data_raw( trait_, self_ty, items ) );
    }
    None
}

fn get_impl_data_raw<'a>(
    trait_ref : &'a Option<( Option<Token!(!)>, Path, Token!(for) )>,
    struct_ty : &'a Type,
    items : &'a [ImplItem]
) -> ImplData<'a>
{

    let struct_ident = struct_ty.get_ident().unwrap();
    let trait_ident = match *trait_ref {
        Some( ( _, ref path, _ ) ) => path.get_ident().ok(),
        None => None
    };

    let methods_opt : Option< Vec< &MethodSig > > = items
            .into_iter()
            .map( |i| get_impl_method( i ) )
            .collect();
    let methods = methods_opt.unwrap_or_else( || vec![] );

    ( trait_ident, struct_ident, methods )
}

#[derive(PartialEq, Eq, Debug)]
pub enum AttrParam {
    Literal( Lit ),
    Word( Ident ),
}

pub fn iter_parameters(
    attr : &syn::Attribute
) -> Box< Iterator<Item = AttrParam > >
{
    match attr.interpret_meta() {

        Some( Meta::List( MetaList { ref nested, .. } ) ) =>
            Box::new( nested.to_owned().into_iter().map( |i| {

                match i {

                    NestedMeta::Meta( meta ) =>
                        AttrParam::Word( match meta {
                            Meta::Word( i ) => i,
                            Meta::List( l ) => l.ident,
                            Meta::NameValue( nv ) => nv.ident,
                        } ),

                    syn::NestedMeta::Literal( l ) =>
                            AttrParam::Literal( l ),
                }
            } ) ),

        // Attributes without parameter lists don't have params.
        None
            | Some( Meta::Word(..) )
            | Some( Meta::NameValue(..) ) => Box::new( std::iter::empty() ),

    }
}

pub fn get_impl_method(
    i : &ImplItem
) -> Option< &MethodSig >
{
    match *i {
        ImplItem::Method( ref itm ) => Some( &itm.sig ),
        _ => None
    }
}

pub fn get_trait_method(
    i : &TraitItem
) -> Option< &MethodSig >
{
    match *i {
        TraitItem::Method( ref tim ) => Some( &tim.sig ),
        _ => None
    }
}

const AUTO_GUID_BASE : guid::GUID = guid::GUID {
    data1: 0x4449_494C,
    data2: 0xDE1F,
    data3: 0x4525,
    data4: [ 0xB9, 0x57, 0x89, 0xD6, 0x0C, 0xE9, 0x34, 0x77 ]
};

pub fn generate_iid(
    crate_name : &str,
    item_name : &str,
    type_system : TypeSystem,
) -> guid::GUID
{
    generate_guid( &[
            "IID",
            crate_name,
            item_name,
            match type_system {
                TypeSystem::Automation => "automation",
                TypeSystem::Raw => "raw",
                TypeSystem::Invariant => "invariant",
            }
        ].join( ":" ) )
}

pub fn generate_libid(
    crate_name : &str,
) -> guid::GUID
{
    generate_guid( &[
            "LIBID",
            crate_name,
        ].join( ":" ) )
}

pub fn generate_clsid(
    crate_name : &str,
    item_name : &str,
) -> guid::GUID
{
    generate_guid( &[
            "CLSID",
            crate_name,
            item_name,
        ].join( ":" ) )
}

pub fn generate_guid(
    key : &str,
) -> guid::GUID
{
    // Hash the name. The name will be hashed in a form similar to:
    // AUTO_GUID_BASE + "CLSID:random_rust_crate:FooBar"
    let mut hash = sha1::Sha1::new();
    hash.update( AUTO_GUID_BASE.as_bytes() );
    hash.update( key.as_bytes() );

    let digest = hash.digest();
    let bytes = digest.bytes();

    // Set the GUID bytes according to RFC-4122, section 4.3.
    let time_low : u32
        = ( u32::from( bytes[0] ) << 24 )
        + ( u32::from( bytes[1] ) << 16 )
        + ( u32::from( bytes[2] ) << 8 )
        + u32::from( bytes[3] );
    let time_mid : u16
        = ( u16::from( bytes[4] ) << 8 )
        + ( u16::from( bytes[5] ) );
    let time_hi_and_version : u16
        = (
            (
                ( u16::from( bytes[6] ) << 8 ) + u16::from( bytes[7] )
            ) & 0x0fff
        ) | 0x3000;
    let clk_seq_hi_res : u8
        = ( bytes[8] & 0b0011_1111 ) | 0b0100_0000;
    let clk_seq_low : u8 = bytes[9];

    guid::GUID {
        data1: time_low,
        data2: time_mid,
        data3: time_hi_and_version,
        data4: [
            clk_seq_hi_res, clk_seq_low,
            bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15] ]
    }
}

pub fn ty_to_string( ty : &syn::Type ) -> String
{
    quote!( #ty ).to_string().replace( " ", "" ).replace( ",", ", " )
}

pub fn is_unit(
    tk : &Type
) -> bool
{
    if let Type::Tuple( ref t ) = *tk {
        t.elems.is_empty()
    } else {
        false
    }
}

pub fn unit_ty() -> Type
{
    parse_quote!( () )
}

pub fn get_guid_tokens(
    g : &guid::GUID
) -> TokenStream
{
    let d1 = g.data1;
    let d2 = g.data2;
    let d3 = g.data3;
    let d4_0 = g.data4[ 0 ];
    let d4_1 = g.data4[ 1 ];
    let d4_2 = g.data4[ 2 ];
    let d4_3 = g.data4[ 3 ];
    let d4_4 = g.data4[ 4 ];
    let d4_5 = g.data4[ 5 ];
    let d4_6 = g.data4[ 6 ];
    let d4_7 = g.data4[ 7 ];
    quote!(
        ::intercom::GUID {
            data1: #d1, data2: #d2, data3: #d3,
            data4: [ #d4_0, #d4_1, #d4_2, #d4_3, #d4_4, #d4_5, #d4_6, #d4_7 ]
        }
    )
}

/// Convert the Rust identifier from `snake_case` to `PascalCase`
pub fn pascal_case<T: AsRef<str>>( input : T ) -> String {
    let input = input.as_ref();

    // Allocate the output string. We'll never increase the amount of
    // characters so we can reserve string buffer using the input string length.
    let mut output = String::new();
    output.reserve( input.len() );

    // Process each character from the input.
    let mut capitalize = true;
    for c in input.chars() {

        // Check the capitalization requirement.
        if c == '_' {

            // Skip '_' but capitalize the following character.
            capitalize = true;

        } else if capitalize {

            // Capitalize. Add the uppercase characters.
            for c_up in c.to_uppercase() {
                output.push( c_up )
            }

            // No need to capitalize any more.
            capitalize = false;

        } else {

            // No need to capitalize. Just add the character as is.
            output.push( c );
        }

    }
    output
}

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

    /// Tests the `ty_to_string` by converting parameter to Type and back to
    /// String to ensure they equal.
    fn test_ty( ty_str : &str ) {
        let ty = parse_str( ty_str ).unwrap();
        let as_string = ty_to_string( &ty );
        assert_eq!( ty_str, as_string );
    }

    #[test] fn path_to_test() { test_ty( "::path::Foo" ) }
    #[test] fn generics_to_test() { test_ty( "Result<Foo, Bar>" ) }
    #[test] fn unit_to_test() { test_ty( "()" ) }
}
<|endoftext|>


<commit_before>
extern crate gitlab_api as gitlab;

use std::env;
#[macro_use]
extern crate log;
extern crate env_logger;

use gitlab::GitLab;
// use gitlab::Pagination;

fn main() {
    env_logger::init().unwrap();
    info!("starting up");

    let hostname = match env::var("GITLAB_HOSTNAME") {
        Ok(val) => val,
        Err(_) => {
            let default = String::from("gitlab.com");
            println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.",
                     default);
            default
        }
    };

    let token = match env::var("GITLAB_TOKEN") {
        Ok(val) => val,
        Err(_) => {
            panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \
                    http://{}/profile/account",
                   hostname);
        }
    };

    // // Projects
    // // List projects
    // let projects = gl.projects().list()
    // let projects = gl.projects().archived(...).visibility(...).order_by(...).sort(...).search(...).simple(...).list()
    // // List all projects (admin)
    // let projects = gl.projects().all().list()
    // let projects = gl.projects().all().archived(...).visibility(...).order_by(...).sort(...).search(...).list()
    // // Single project
    // let project = gl.projects().id(142).list()
    // // Search
    // let projects = gl.projects().search(...).list()
    // // Owned
    // let projects = gl.projects().owned().archived(...).visibility(...).order_by(...).sort(...).search(...).list()

    let gl = GitLab::new(&hostname, &token);

    let projects = gl.projects().list();
    println!("projects: {:?}", projects);

    let projects = gl.projects().archived(false).list();
    println!("projects: {:?}", projects);

    let projects = gl.projects().owned().archived(false).list();
    println!("projects: {:?}", projects);

    let projects = gl.projects().all().order_by(gitlab::projects::ListingOrderBy::Name).list();
    println!("projects: {:?}", projects);

    // // for i in 1..82 {
    // //     gl.set_pagination(Pagination{page: i, per_page: 1});
    // //     println!("projects: {:?}", gl.projects_list().unwrap());
    // // }
    // // gl.set_pagination(Pagination {
    // //     page: 1,
    // //     per_page: 100,
    // // });
    // let projects = gl.projects_list(projects::Listing::new()).unwrap();
    // println!("projects: {:?}", projects);
    // // FIXME: Project's members are private
    // // for project in projects {
    // //     println!("{:?}", project.path_with_namespace);
    // // }
    //
    // let projects = gl.projects_all(projects::all::Listing::new()).unwrap();
    // println!("projects: {:?}", projects);
    //
    // let listing = projects::id::Listing::new(projects::id::ListingId::Id(10));
    // let projects = gl.project_id(listing).unwrap();
    // println!("projects: {:?}", projects);
}
<commit_msg>Cleanup example<commit_after>
extern crate gitlab_api as gitlab;

use std::env;
#[macro_use]
extern crate log;
extern crate env_logger;

use gitlab::GitLab;
// use gitlab::Pagination;

fn main() {
    env_logger::init().unwrap();
    info!("starting up");

    let hostname = match env::var("GITLAB_HOSTNAME") {
        Ok(val) => val,
        Err(_) => {
            let default = String::from("gitlab.com");
            println!("Please set environment variable 'GITLAB_HOSTNAME'. Using default '{}'.",
                     default);
            default
        }
    };

    let token = match env::var("GITLAB_TOKEN") {
        Ok(val) => val,
        Err(_) => {
            panic!("Please set environment variable 'GITLAB_TOKEN'. Take it from \
                    http://{}/profile/account",
                   hostname);
        }
    };

    let gl = GitLab::new(&hostname, &token);

    let projects = gl.projects().list();
    println!("projects: {:?}", projects);

    let projects = gl.projects().archived(false).list();
    println!("projects: {:?}", projects);

    let projects = gl.projects().owned().archived(false).list();
    println!("projects: {:?}", projects);

    let projects = gl.projects().all().order_by(gitlab::projects::ListingOrderBy::Name).list();
    println!("projects: {:?}", projects);

}
<|endoftext|>


<commit_before>use std::{
    sync::{
        atomic::{AtomicBool, AtomicUsize, Ordering},
        Arc,
    },
    thread,
};

use docopt::Docopt;
use rand::{thread_rng, Rng};
use serde::Deserialize;

#[cfg_attr(
    // only enable jemalloc on linux and macos by default
    all(
        any(target_os = "linux", target_os = "macos"),
        not(feature = "no_jemalloc"),
    ),
    global_allocator
)]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

static TOTAL: AtomicUsize = AtomicUsize::new(0);
static SEQ: AtomicUsize = AtomicUsize::new(0);

const USAGE: &str = "
Usage: stress [--threads=<#>] [--burn-in] [--duration=<s>] \
    [--key-len=<l>] [--val-len=<l>] \
    [--get-prop=<p>] \
    [--set-prop=<p>] \
    [--del-prop=<p>] \
    [--cas-prop=<p>] \
    [--scan-prop=<p>] \
    [--merge-prop=<p>] \
    [--entries=<n>] \
    [--sequential] \
    [--total-ops=<n>]

Options:
    --threads=<#>      Number of threads [default: 4].
    --burn-in          Don't halt until we receive a signal.
    --duration=<s>     Seconds to run for [default: 10].
    --key-len=<l>      The length of keys [default: 10].
    --val-len=<l>      The length of values [default: 100].
    --get-prop=<p>     The relative proportion of get requests [default: 94].
    --set-prop=<p>     The relative proportion of set requests [default: 2].
    --del-prop=<p>     The relative proportion of del requests [default: 1].
    --cas-prop=<p>     The relative proportion of cas requests [default: 1].
    --scan-prop=<p>    The relative proportion of scan requests [default: 1].
    --merge-prop=<p>   The relative proportion of merge requests [default: 1].
    --entries=<n>      The total keyspace [default: 100000].
    --sequential       Run the test in sequential mode instead of random.
    --total-ops=<n>    Stop test after executing a total number of operations.
";

#[derive(Deserialize, Clone)]
struct Args {
    flag_threads: usize,
    flag_burn_in: bool,
    flag_duration: u64,
    flag_key_len: usize,
    flag_val_len: usize,
    flag_get_prop: usize,
    flag_set_prop: usize,
    flag_del_prop: usize,
    flag_cas_prop: usize,
    flag_scan_prop: usize,
    flag_merge_prop: usize,
    flag_entries: usize,
    flag_sequential: bool,
    flag_total_ops: Option<usize>,
}

// defaults will be applied later based on USAGE above
static mut ARGS: Args = Args {
    flag_threads: 0,
    flag_burn_in: false,
    flag_duration: 0,
    flag_key_len: 0,
    flag_val_len: 0,
    flag_get_prop: 0,
    flag_set_prop: 0,
    flag_del_prop: 0,
    flag_cas_prop: 0,
    flag_scan_prop: 0,
    flag_merge_prop: 0,
    flag_entries: 0,
    flag_sequential: false,
    flag_total_ops: None,
};

fn report(shutdown: Arc<AtomicBool>) {
    let mut last = 0;
    while !shutdown.load(Ordering::Relaxed) {
        thread::sleep(std::time::Duration::from_secs(1));
        let total = TOTAL.load(Ordering::Acquire);

        println!("did {} ops, {}mb RSS", total - last, rss() / (1024 * 1024));

        last = total;
    }
}

fn concatenate_merge(
    _key: &[u8],              // the key being merged
    old_value: Option<&[u8]>, // the previous value, if one existed
    merged_bytes: &[u8],      // the new bytes being merged in
) -> Option<Vec<u8>> {
    // set the new value, return None to delete
    let mut ret = old_value.map(|ov| ov.to_vec()).unwrap_or_else(|| vec![]);

    ret.extend_from_slice(merged_bytes);

    Some(ret)
}

fn run(tree: Arc<sled::Db>, shutdown: Arc<AtomicBool>) {
    let args = unsafe { ARGS.clone() };

    let get_max = args.flag_get_prop;
    let set_max = get_max + args.flag_set_prop;
    let del_max = set_max + args.flag_del_prop;
    let cas_max = del_max + args.flag_cas_prop;
    let merge_max = cas_max + args.flag_merge_prop;
    let scan_max = merge_max + args.flag_scan_prop;

    let bytes = |len| -> Vec<u8> {
        let i = if args.flag_sequential {
            SEQ.fetch_add(1, Ordering::Relaxed)
        } else {
            thread_rng().gen::<usize>()
        } % args.flag_entries;

        let i_bytes: [u8; std::mem::size_of::<usize>()] =
            unsafe { std::mem::transmute(i) };

        i_bytes.iter().cycle().take(len).copied().collect()
    };
    let mut rng = thread_rng();

    while !shutdown.load(Ordering::Relaxed) {
        let op = TOTAL.fetch_add(1, Ordering::Release);
        let key = bytes(args.flag_key_len);
        let choice = rng.gen_range(0, scan_max + 1);

        match choice {
            v if v <= get_max => {
                tree.get(&key).unwrap();
            }
            v if v > get_max && v <= set_max => {
                tree.insert(&key, bytes(args.flag_val_len)).unwrap();
            }
            v if v > set_max && v <= del_max => {
                tree.remove(&key).unwrap();
            }
            v if v > del_max && v <= cas_max => {
                let old_k = bytes(args.flag_val_len);

                let old = if rng.gen::<bool>() {
                    Some(old_k.as_slice())
                } else {
                    None
                };

                let new = if rng.gen::<bool>() {
                    Some(bytes(args.flag_val_len))
                } else {
                    None
                };

                if let Err(e) = tree.compare_and_swap(&key, old, new) {
                    panic!("operational error: {:?}", e);
                }
            }
            v if v > cas_max && v <= merge_max => {
                tree.merge(&key, bytes(args.flag_val_len)).unwrap();
            }
            _ => {
                let iter = tree.range(key..).map(|res| res.unwrap());

                if op % 2 == 0 {
                    let _ = iter.take(rng.gen_range(0, 15)).collect::<Vec<_>>();
                } else {
                    let _ = iter
                        .rev()
                        .take(rng.gen_range(0, 15))
                        .collect::<Vec<_>>();
                }
            }
        }
    }
}

fn rss() -> usize {
    #[cfg(target_os = "linux")]
    {
        use std::io::prelude::*;
        use std::io::BufReader;

        let mut buf = String::new();
        let mut f =
            BufReader::new(std::fs::File::open("/proc/self/statm").unwrap());
        f.read_line(&mut buf).unwrap();
        let mut parts = buf.split_whitespace();
        let rss_pages = parts.nth(1).unwrap().parse::<usize>().unwrap();
        rss_pages * 4096
    }
    #[cfg(not(target_os = "linux"))]
    {
        0
    }
}

fn main() {
    #[cfg(feature = "logging")]
    setup_logger();

    let args = unsafe {
        ARGS = Docopt::new(USAGE)
            .and_then(|d| d.argv(std::env::args()).deserialize())
            .unwrap_or_else(|e| e.exit());
        ARGS.clone()
    };

    let shutdown = Arc::new(AtomicBool::new(false));

    let config = sled::Config::new()
        .cache_capacity(256 * 1024 * 1024)
        .flush_every_ms(Some(200))
        .snapshot_after_ops(100_000_000_000)
        .print_profile_on_drop(true);

    let tree = Arc::new(config.open().unwrap());
    tree.set_merge_operator(concatenate_merge);

    let mut threads = vec![];

    let now = std::time::Instant::now();

    let n_threads = args.flag_threads;

    for i in 0..=n_threads {
        let tree = tree.clone();
        let shutdown = shutdown.clone();

        let t = if i == 0 {
            thread::Builder::new()
                .name("reporter".into())
                .spawn(move || report(shutdown))
                .unwrap()
        } else {
            thread::spawn(move || run(tree, shutdown))
        };

        threads.push(t);
    }

    if let Some(ops) = args.flag_total_ops {
        assert!(!args.flag_burn_in, "don't set both --burn-in and --total-ops");
        while TOTAL.load(Ordering::Relaxed) < ops {
            thread::sleep(std::time::Duration::from_millis(50));
        }
        shutdown.store(true, Ordering::SeqCst);
    } else if !args.flag_burn_in {
        thread::sleep(std::time::Duration::from_secs(unsafe {
            ARGS.flag_duration
        }));
        shutdown.store(true, Ordering::SeqCst);
    }

    for t in threads.into_iter() {
        t.join().unwrap();
    }

    let ops = TOTAL.load(Ordering::SeqCst);
    let time = now.elapsed().as_secs() as usize;

    println!(
        "did {} total ops in {} seconds. {} ops/s",
        ops,
        time,
        (ops * 1_000) / (time * 1_000)
    );
}

#[cfg(feature = "logging")]
pub fn setup_logger() {
    use std::io::Write;

    color_backtrace::install();

    fn tn() -> String {
        std::thread::current().name().unwrap_or("unknown").to_owned()
    }

    let mut builder = env_logger::Builder::new();
    builder
        .format(|buf, record| {
            writeln!(
                buf,
                "{:05} {:25} {:10} {}",
                record.level(),
                tn(),
                record.module_path().unwrap().split("::").last().unwrap(),
                record.args()
            )
        })
        .filter(None, log::LevelFilter::Info);

    if std::env::var("RUST_LOG").is_ok() {
        builder.parse(&std::env::var("RUST_LOG").unwrap());
    }

    let _r = builder.try_init();
}
<commit_msg>Use to_le_bytes instead of mem::transmute in data generation for stress2<commit_after>use std::{
    sync::{
        atomic::{AtomicBool, AtomicUsize, Ordering},
        Arc,
    },
    thread,
};

use docopt::Docopt;
use rand::{thread_rng, Rng};
use serde::Deserialize;

#[cfg_attr(
    // only enable jemalloc on linux and macos by default
    all(
        any(target_os = "linux", target_os = "macos"),
        not(feature = "no_jemalloc"),
    ),
    global_allocator
)]
static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;

static TOTAL: AtomicUsize = AtomicUsize::new(0);
static SEQ: AtomicUsize = AtomicUsize::new(0);

const USAGE: &str = "
Usage: stress [--threads=<#>] [--burn-in] [--duration=<s>] \
    [--key-len=<l>] [--val-len=<l>] \
    [--get-prop=<p>] \
    [--set-prop=<p>] \
    [--del-prop=<p>] \
    [--cas-prop=<p>] \
    [--scan-prop=<p>] \
    [--merge-prop=<p>] \
    [--entries=<n>] \
    [--sequential] \
    [--total-ops=<n>]

Options:
    --threads=<#>      Number of threads [default: 4].
    --burn-in          Don't halt until we receive a signal.
    --duration=<s>     Seconds to run for [default: 10].
    --key-len=<l>      The length of keys [default: 10].
    --val-len=<l>      The length of values [default: 100].
    --get-prop=<p>     The relative proportion of get requests [default: 94].
    --set-prop=<p>     The relative proportion of set requests [default: 2].
    --del-prop=<p>     The relative proportion of del requests [default: 1].
    --cas-prop=<p>     The relative proportion of cas requests [default: 1].
    --scan-prop=<p>    The relative proportion of scan requests [default: 1].
    --merge-prop=<p>   The relative proportion of merge requests [default: 1].
    --entries=<n>      The total keyspace [default: 100000].
    --sequential       Run the test in sequential mode instead of random.
    --total-ops=<n>    Stop test after executing a total number of operations.
";

#[derive(Deserialize, Clone)]
struct Args {
    flag_threads: usize,
    flag_burn_in: bool,
    flag_duration: u64,
    flag_key_len: usize,
    flag_val_len: usize,
    flag_get_prop: usize,
    flag_set_prop: usize,
    flag_del_prop: usize,
    flag_cas_prop: usize,
    flag_scan_prop: usize,
    flag_merge_prop: usize,
    flag_entries: usize,
    flag_sequential: bool,
    flag_total_ops: Option<usize>,
}

// defaults will be applied later based on USAGE above
static mut ARGS: Args = Args {
    flag_threads: 0,
    flag_burn_in: false,
    flag_duration: 0,
    flag_key_len: 0,
    flag_val_len: 0,
    flag_get_prop: 0,
    flag_set_prop: 0,
    flag_del_prop: 0,
    flag_cas_prop: 0,
    flag_scan_prop: 0,
    flag_merge_prop: 0,
    flag_entries: 0,
    flag_sequential: false,
    flag_total_ops: None,
};

fn report(shutdown: Arc<AtomicBool>) {
    let mut last = 0;
    while !shutdown.load(Ordering::Relaxed) {
        thread::sleep(std::time::Duration::from_secs(1));
        let total = TOTAL.load(Ordering::Acquire);

        println!("did {} ops, {}mb RSS", total - last, rss() / (1024 * 1024));

        last = total;
    }
}

fn concatenate_merge(
    _key: &[u8],              // the key being merged
    old_value: Option<&[u8]>, // the previous value, if one existed
    merged_bytes: &[u8],      // the new bytes being merged in
) -> Option<Vec<u8>> {
    // set the new value, return None to delete
    let mut ret = old_value.map(|ov| ov.to_vec()).unwrap_or_else(|| vec![]);

    ret.extend_from_slice(merged_bytes);

    Some(ret)
}

fn run(tree: Arc<sled::Db>, shutdown: Arc<AtomicBool>) {
    let args = unsafe { ARGS.clone() };

    let get_max = args.flag_get_prop;
    let set_max = get_max + args.flag_set_prop;
    let del_max = set_max + args.flag_del_prop;
    let cas_max = del_max + args.flag_cas_prop;
    let merge_max = cas_max + args.flag_merge_prop;
    let scan_max = merge_max + args.flag_scan_prop;

    let bytes = |len| -> Vec<u8> {
        let i = if args.flag_sequential {
            SEQ.fetch_add(1, Ordering::Relaxed)
        } else {
            thread_rng().gen::<usize>()
        } % args.flag_entries;

        let i_bytes = i.to_le_bytes();

        i_bytes.iter().cycle().take(len).copied().collect()
    };
    let mut rng = thread_rng();

    while !shutdown.load(Ordering::Relaxed) {
        let op = TOTAL.fetch_add(1, Ordering::Release);
        let key = bytes(args.flag_key_len);
        let choice = rng.gen_range(0, scan_max + 1);

        match choice {
            v if v <= get_max => {
                tree.get(&key).unwrap();
            }
            v if v > get_max && v <= set_max => {
                tree.insert(&key, bytes(args.flag_val_len)).unwrap();
            }
            v if v > set_max && v <= del_max => {
                tree.remove(&key).unwrap();
            }
            v if v > del_max && v <= cas_max => {
                let old_k = bytes(args.flag_val_len);

                let old = if rng.gen::<bool>() {
                    Some(old_k.as_slice())
                } else {
                    None
                };

                let new = if rng.gen::<bool>() {
                    Some(bytes(args.flag_val_len))
                } else {
                    None
                };

                if let Err(e) = tree.compare_and_swap(&key, old, new) {
                    panic!("operational error: {:?}", e);
                }
            }
            v if v > cas_max && v <= merge_max => {
                tree.merge(&key, bytes(args.flag_val_len)).unwrap();
            }
            _ => {
                let iter = tree.range(key..).map(|res| res.unwrap());

                if op % 2 == 0 {
                    let _ = iter.take(rng.gen_range(0, 15)).collect::<Vec<_>>();
                } else {
                    let _ = iter
                        .rev()
                        .take(rng.gen_range(0, 15))
                        .collect::<Vec<_>>();
                }
            }
        }
    }
}

fn rss() -> usize {
    #[cfg(target_os = "linux")]
    {
        use std::io::prelude::*;
        use std::io::BufReader;

        let mut buf = String::new();
        let mut f =
            BufReader::new(std::fs::File::open("/proc/self/statm").unwrap());
        f.read_line(&mut buf).unwrap();
        let mut parts = buf.split_whitespace();
        let rss_pages = parts.nth(1).unwrap().parse::<usize>().unwrap();
        rss_pages * 4096
    }
    #[cfg(not(target_os = "linux"))]
    {
        0
    }
}

fn main() {
    #[cfg(feature = "logging")]
    setup_logger();

    let args = unsafe {
        ARGS = Docopt::new(USAGE)
            .and_then(|d| d.argv(std::env::args()).deserialize())
            .unwrap_or_else(|e| e.exit());
        ARGS.clone()
    };

    let shutdown = Arc::new(AtomicBool::new(false));

    let config = sled::Config::new()
        .cache_capacity(256 * 1024 * 1024)
        .flush_every_ms(Some(200))
        .snapshot_after_ops(100_000_000_000)
        .print_profile_on_drop(true);

    let tree = Arc::new(config.open().unwrap());
    tree.set_merge_operator(concatenate_merge);

    let mut threads = vec![];

    let now = std::time::Instant::now();

    let n_threads = args.flag_threads;

    for i in 0..=n_threads {
        let tree = tree.clone();
        let shutdown = shutdown.clone();

        let t = if i == 0 {
            thread::Builder::new()
                .name("reporter".into())
                .spawn(move || report(shutdown))
                .unwrap()
        } else {
            thread::spawn(move || run(tree, shutdown))
        };

        threads.push(t);
    }

    if let Some(ops) = args.flag_total_ops {
        assert!(!args.flag_burn_in, "don't set both --burn-in and --total-ops");
        while TOTAL.load(Ordering::Relaxed) < ops {
            thread::sleep(std::time::Duration::from_millis(50));
        }
        shutdown.store(true, Ordering::SeqCst);
    } else if !args.flag_burn_in {
        thread::sleep(std::time::Duration::from_secs(unsafe {
            ARGS.flag_duration
        }));
        shutdown.store(true, Ordering::SeqCst);
    }

    for t in threads.into_iter() {
        t.join().unwrap();
    }

    let ops = TOTAL.load(Ordering::SeqCst);
    let time = now.elapsed().as_secs() as usize;

    println!(
        "did {} total ops in {} seconds. {} ops/s",
        ops,
        time,
        (ops * 1_000) / (time * 1_000)
    );
}

#[cfg(feature = "logging")]
pub fn setup_logger() {
    use std::io::Write;

    color_backtrace::install();

    fn tn() -> String {
        std::thread::current().name().unwrap_or("unknown").to_owned()
    }

    let mut builder = env_logger::Builder::new();
    builder
        .format(|buf, record| {
            writeln!(
                buf,
                "{:05} {:25} {:10} {}",
                record.level(),
                tn(),
                record.module_path().unwrap().split("::").last().unwrap(),
                record.args()
            )
        })
        .filter(None, log::LevelFilter::Info);

    if std::env::var("RUST_LOG").is_ok() {
        builder.parse(&std::env::var("RUST_LOG").unwrap());
    }

    let _r = builder.try_init();
}
<|endoftext|>


<commit_before>extern crate bytecount;
#[macro_use]
extern crate quickcheck;
extern crate rand;

use std::iter;
use bytecount::{
    count, naive_count,
    num_chars, naive_num_chars,
};
use rand::RngCore;

fn random_bytes(len: usize) -> Vec<u8> {
    let mut result = vec![0; len];
    rand::thread_rng().fill_bytes(&mut result);
    result
}

quickcheck! {
    fn check_count_correct(x: (Vec<u8>, u8)) -> bool {
        let (haystack, needle) = x;
        count(&haystack, needle) == naive_count(&haystack, needle)
    }
}

#[test]
fn check_count_large() {
    let haystack = vec![0u8; if cfg!(miri) { 2_000 } else { 10_000_000 }];
    assert_eq!(naive_count(&haystack, 0), count(&haystack, 0));
    assert_eq!(naive_count(&haystack, 1), count(&haystack, 1));
}

#[test]
fn check_count_large_rand() {
    let haystack = random_bytes(if cfg!(miri) { 200 } else { 100_000 });
    for i in 0..=255 {
        assert_eq!(naive_count(&haystack, i), count(&haystack, i));
    }
}

#[test]
fn check_count_some() {
    let haystack = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68];
    let needle = 68;
    assert_eq!(count(&haystack, needle), naive_count(&haystack, needle));
}

#[test]
fn check_count_overflow() {
    let haystack = vec![0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    let needle = 2;
    assert_eq!(count(&haystack, needle), naive_count(&haystack, needle));
}

quickcheck! {
    fn check_num_chars_correct(haystack: Vec<u8>) -> bool {
        num_chars(&haystack) == naive_num_chars(&haystack)
    }
}

#[test]
fn check_num_chars_large() {
    let haystack = vec![0u8; if cfg!(miri) { 2_000 } else { 10_000_000 }];
    assert_eq!(naive_num_chars(&haystack), num_chars(&haystack));
    assert_eq!(naive_num_chars(&haystack), num_chars(&haystack));
}

#[test]
fn check_num_chars_some() {
    let haystack = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68];
    assert_eq!(num_chars(&haystack), naive_num_chars(&haystack));
}

#[test]
fn check_num_chars_overflow() {
    let haystack = vec![0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    assert_eq!(num_chars(&haystack), naive_num_chars(&haystack));
}
<commit_msg>Remove unused std::iter import<commit_after>extern crate bytecount;
#[macro_use]
extern crate quickcheck;
extern crate rand;

use bytecount::{
    count, naive_count,
    num_chars, naive_num_chars,
};
use rand::RngCore;

fn random_bytes(len: usize) -> Vec<u8> {
    let mut result = vec![0; len];
    rand::thread_rng().fill_bytes(&mut result);
    result
}

quickcheck! {
    fn check_count_correct(x: (Vec<u8>, u8)) -> bool {
        let (haystack, needle) = x;
        count(&haystack, needle) == naive_count(&haystack, needle)
    }
}

#[test]
fn check_count_large() {
    let haystack = vec![0u8; if cfg!(miri) { 2_000 } else { 10_000_000 }];
    assert_eq!(naive_count(&haystack, 0), count(&haystack, 0));
    assert_eq!(naive_count(&haystack, 1), count(&haystack, 1));
}

#[test]
fn check_count_large_rand() {
    let haystack = random_bytes(if cfg!(miri) { 200 } else { 100_000 });
    for i in 0..=255 {
        assert_eq!(naive_count(&haystack, i), count(&haystack, i));
    }
}

#[test]
fn check_count_some() {
    let haystack = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68];
    let needle = 68;
    assert_eq!(count(&haystack, needle), naive_count(&haystack, needle));
}

#[test]
fn check_count_overflow() {
    let haystack = vec![0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    let needle = 2;
    assert_eq!(count(&haystack, needle), naive_count(&haystack, needle));
}

quickcheck! {
    fn check_num_chars_correct(haystack: Vec<u8>) -> bool {
        num_chars(&haystack) == naive_num_chars(&haystack)
    }
}

#[test]
fn check_num_chars_large() {
    let haystack = vec![0u8; if cfg!(miri) { 2_000 } else { 10_000_000 }];
    assert_eq!(naive_num_chars(&haystack), num_chars(&haystack));
    assert_eq!(naive_num_chars(&haystack), num_chars(&haystack));
}

#[test]
fn check_num_chars_some() {
    let haystack = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 68];
    assert_eq!(num_chars(&haystack), naive_num_chars(&haystack));
}

#[test]
fn check_num_chars_overflow() {
    let haystack = vec![0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    assert_eq!(num_chars(&haystack), naive_num_chars(&haystack));
}
<|endoftext|>


<commit_before>use self::ItemType::*;
use std::io;
use std::io::Write;
use str::GopherStr;
use tokio_core::io::EasyBuf;

pub enum Void {}

/// A client-to-server message.
#[derive(Clone, Debug)]
pub struct GopherRequest {
    /// Identifier of the resource to fetch. May be an empty string.
    pub selector: GopherStr,
    /// Search string for a full-text search transaction.
    pub query: Option<GopherStr>
}

impl GopherRequest {
    /// Read a Gopher request from a buffer containing a line *without* the trailing CRLF.
    pub fn decode(mut line: EasyBuf) -> Self {
        // Split on TAB if present.
        let query = match line.as_slice().iter().position(|b| *b == b'\t') {
            Some(i) => {
                let mut query = line.split_off(i);
                query.drain_to(1); // Consume the TAB.
                Some(GopherStr::new(query))
            }
            None => None
        };

        GopherRequest {
            selector: GopherStr::new(line),
            query: query,
        }
    }
}

/// A server-to-client message.
#[derive(Clone, Debug)]
pub enum GopherResponse {
    /// A list of resources.
    Menu(Vec<DirEntity>),
    /// A text document.
    TextFile(EasyBuf),
    /// A binary file download.
    BinaryFile(EasyBuf),
}

impl GopherResponse {
    /// Construct a menu with a single error line.
    pub fn error(text: GopherStr) -> Self {
        GopherResponse::Menu(vec![
            DirEntity {
                item_type: Error,
                name: text,
                selector: GopherStr::from_latin1(b"error"),
                host: GopherStr::from_latin1(b"error.host"),
                port: 0,
            }
        ])
    }

    /// Encode the response into bytes for sending over the wire.
    pub fn encode<W>(&self, mut buf: W) -> io::Result<()>
        where W: Write
    {
        match *self {
            GopherResponse::BinaryFile(ref file) => {
                buf.write_all(file.as_slice())?;
            }
            GopherResponse::TextFile(ref file) => {
                // TODO: Escape lines beginning with periods by adding an extra period.
                buf.write_all(file.as_slice())?;
                buf.write_all(b"\r\n.\r\n")?;
            }
            GopherResponse::Menu(ref entities) => {
                for entity in entities {
                    buf.write_all(&[entity.item_type.encode()])?;
                    buf.write_all(&entity.name)?;
                    buf.write_all(b"\t")?;
                    buf.write_all(&entity.selector)?;
                    buf.write_all(b"\t")?;
                    buf.write_all(&entity.host)?;
                    write!(buf, "\t{}\r\n", entity.port)?;
                }
                buf.write_all(b".\r\n")?;
            }
        }
        Ok(())
    }
}

/// A list of Gopher resources.
pub struct Menu {
    pub entities: Vec<DirEntity>,
}

/// An menu item in a directory of Gopher resources.
#[derive(Clone, Debug)]
pub struct DirEntity {
    /// The type of the resource
    pub item_type: ItemType,
    /// String to display to the user.
    pub name: GopherStr,
    /// Path or identifier used for requesting this resource.
    pub selector: GopherStr,
    /// The hostname of the server hosting this resource.
    pub host: GopherStr,
    /// The TCP port of the server hosting this resource.
    pub port: u16,
}

/// The type of a resource in a Gopher directory.
///
/// For more details, see: https://tools.ietf.org/html/rfc1436
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
pub enum ItemType {
    /// Item is a file
    File,
    /// Item is a directory
    Dir,
    /// Item is a CSO phone-book server
    CsoServer,
    /// Error
    Error,
    /// Item is a BinHexed Macintosh file.
    BinHex,
    /// Item is DOS binary archive of some sort.
    ///
    /// Client must read until the TCP connection closes.  Beware.
    Dos,
    /// Item is a UNIX uuencoded file.
    Uuencoded,
    /// Item is an Index-Search server.
    IndexServer,
    /// Item points to a text-based telnet session.
    Telnet,
    /// Item is a binary file! Client must read until the TCP connection closes.  Beware
    Binary,
    /// Item is a redundant server
    RedundantServer,
    /// Item points to a text-based tn3270 session.
    Tn3270,
    /// Item is a GIF format graphics file.
    Gif,
    /// Item is some kind of image file.  Client decides how to display.
    Image,
    /// Item is a non-standard type
    Other(u8),
}

impl ItemType {
    pub fn decode(b: u8) -> Self {
        match b {
            b'0' => File,
            b'1' => Dir,
            b'2' => CsoServer,
            b'3' => Error,
            b'4' => BinHex,
            b'5' => Dos,
            b'6' => Uuencoded,
            b'7' => IndexServer,
            b'8' => Telnet,
            b'9' => Binary,
            b'+' => RedundantServer,
            b'T' => Tn3270,
            b'g' => Gif,
            b'I' => Image,
            byte => Other(byte)
        }
    }

    pub fn encode(self) -> u8 {
        match self {
            File            => b'0',
            Dir             => b'1',
            CsoServer       => b'2',
            Error           => b'3',
            BinHex          => b'4',
            Dos             => b'5',
            Uuencoded       => b'6',
            IndexServer     => b'7',
            Telnet          => b'8',
            Binary          => b'9',
            RedundantServer => b'+',
            Tn3270          => b'T',
            Gif             => b'g',
            Image           => b'I',
            Other(byte)     => byte,
        }
    }
}
<commit_msg>Don't consume the writer<commit_after>use self::ItemType::*;
use std::io;
use std::io::Write;
use str::GopherStr;
use tokio_core::io::EasyBuf;

pub enum Void {}

/// A client-to-server message.
#[derive(Clone, Debug)]
pub struct GopherRequest {
    /// Identifier of the resource to fetch. May be an empty string.
    pub selector: GopherStr,
    /// Search string for a full-text search transaction.
    pub query: Option<GopherStr>
}

impl GopherRequest {
    /// Read a Gopher request from a buffer containing a line *without* the trailing CRLF.
    pub fn decode(mut line: EasyBuf) -> Self {
        // Split on TAB if present.
        let query = match line.as_slice().iter().position(|b| *b == b'\t') {
            Some(i) => {
                let mut query = line.split_off(i);
                query.drain_to(1); // Consume the TAB.
                Some(GopherStr::new(query))
            }
            None => None
        };

        GopherRequest {
            selector: GopherStr::new(line),
            query: query,
        }
    }
}

/// A server-to-client message.
#[derive(Clone, Debug)]
pub enum GopherResponse {
    /// A list of resources.
    Menu(Vec<DirEntity>),
    /// A text document.
    TextFile(EasyBuf),
    /// A binary file download.
    BinaryFile(EasyBuf),
}

impl GopherResponse {
    /// Construct a menu with a single error line.
    pub fn error(text: GopherStr) -> Self {
        GopherResponse::Menu(vec![
            DirEntity {
                item_type: Error,
                name: text,
                selector: GopherStr::from_latin1(b"error"),
                host: GopherStr::from_latin1(b"error.host"),
                port: 0,
            }
        ])
    }

    /// Encode the response into bytes for sending over the wire.
    pub fn encode<W>(&self, mut buf: &mut W) -> io::Result<()>
        where W: Write
    {
        match *self {
            GopherResponse::BinaryFile(ref file) => {
                buf.write_all(file.as_slice())?;
            }
            GopherResponse::TextFile(ref file) => {
                // TODO: Escape lines beginning with periods by adding an extra period.
                buf.write_all(file.as_slice())?;
                buf.write_all(b"\r\n.\r\n")?;
            }
            GopherResponse::Menu(ref entities) => {
                for entity in entities {
                    buf.write_all(&[entity.item_type.encode()])?;
                    buf.write_all(&entity.name)?;
                    buf.write_all(b"\t")?;
                    buf.write_all(&entity.selector)?;
                    buf.write_all(b"\t")?;
                    buf.write_all(&entity.host)?;
                    write!(buf, "\t{}\r\n", entity.port)?;
                }
                buf.write_all(b".\r\n")?;
            }
        }
        Ok(())
    }
}

/// A list of Gopher resources.
pub struct Menu {
    pub entities: Vec<DirEntity>,
}

/// An menu item in a directory of Gopher resources.
#[derive(Clone, Debug)]
pub struct DirEntity {
    /// The type of the resource
    pub item_type: ItemType,
    /// String to display to the user.
    pub name: GopherStr,
    /// Path or identifier used for requesting this resource.
    pub selector: GopherStr,
    /// The hostname of the server hosting this resource.
    pub host: GopherStr,
    /// The TCP port of the server hosting this resource.
    pub port: u16,
}

/// The type of a resource in a Gopher directory.
///
/// For more details, see: https://tools.ietf.org/html/rfc1436
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
pub enum ItemType {
    /// Item is a file
    File,
    /// Item is a directory
    Dir,
    /// Item is a CSO phone-book server
    CsoServer,
    /// Error
    Error,
    /// Item is a BinHexed Macintosh file.
    BinHex,
    /// Item is DOS binary archive of some sort.
    ///
    /// Client must read until the TCP connection closes.  Beware.
    Dos,
    /// Item is a UNIX uuencoded file.
    Uuencoded,
    /// Item is an Index-Search server.
    IndexServer,
    /// Item points to a text-based telnet session.
    Telnet,
    /// Item is a binary file! Client must read until the TCP connection closes.  Beware
    Binary,
    /// Item is a redundant server
    RedundantServer,
    /// Item points to a text-based tn3270 session.
    Tn3270,
    /// Item is a GIF format graphics file.
    Gif,
    /// Item is some kind of image file.  Client decides how to display.
    Image,
    /// Item is a non-standard type
    Other(u8),
}

impl ItemType {
    pub fn decode(b: u8) -> Self {
        match b {
            b'0' => File,
            b'1' => Dir,
            b'2' => CsoServer,
            b'3' => Error,
            b'4' => BinHex,
            b'5' => Dos,
            b'6' => Uuencoded,
            b'7' => IndexServer,
            b'8' => Telnet,
            b'9' => Binary,
            b'+' => RedundantServer,
            b'T' => Tn3270,
            b'g' => Gif,
            b'I' => Image,
            byte => Other(byte)
        }
    }

    pub fn encode(self) -> u8 {
        match self {
            File            => b'0',
            Dir             => b'1',
            CsoServer       => b'2',
            Error           => b'3',
            BinHex          => b'4',
            Dos             => b'5',
            Uuencoded       => b'6',
            IndexServer     => b'7',
            Telnet          => b'8',
            Binary          => b'9',
            RedundantServer => b'+',
            Tn3270          => b'T',
            Gif             => b'g',
            Image           => b'I',
            Other(byte)     => byte,
        }
    }
}
<|endoftext|>