<commit_before>use std::collections::HashMap;
use std::env;
use std::fmt;
use std::io;
use std::io::Write;
use std::sync::Mutex;

extern crate syn;
use syn::*;

mod rust_lib;

#[derive(Debug)]
struct ConvertedType {
    /// The type converted to C (e.g. `uint32_t`)
    prefix: String,
    /// Stuff that might need to go after the identifier (e.g. `[3]` for an array).
    postfix: String,
}

impl ConvertedType {
    fn new<T: Into<String>>(pre: T, post: String) -> Self {
        ConvertedType {
            prefix: pre.into(),
            postfix: post
        }
    }

    fn format_with_ident(&self, ident: &Ident) -> String {
        format!("{} {}{}", &self.prefix, &ident.to_string(), &self.postfix)
    }
}

impl Into<String> for ConvertedType {
    fn into(self) -> String {
        let mut str = String::from(self.prefix);
        str.push_str(&self.postfix);
        str
    }
}

impl From<String> for ConvertedType {
    fn from(str: String) -> ConvertedType {
        ConvertedType {
            prefix: str,
            postfix: String::new()
        }
    }
}

impl fmt::Display for ConvertedType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", &self.prefix, &self.postfix)
    }
}

fn has_attribute(target: MetaItem, attrs: &Vec<Attribute>) -> bool {
    return attrs
               .iter()
               .any(|ref attr| attr.style == AttrStyle::Outer && attr.value == target);
}

fn has_no_mangle(attrs: &Vec<Attribute>) -> bool {
    has_attribute(MetaItem::Word(Ident::new("no_mangle")), attrs)
}

fn wr_func_body(attrs: &Vec<Attribute>) -> String {
    if has_attribute(MetaItem::Word(Ident::new("destructor_safe")), attrs) {
        String::from("WR_DESTRUCTOR_SAFE_FUNC")
    } else {
        String::from("WR_FUNC")
    }
}

fn is_repr_c(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("C")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_repr_u32(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("u32")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_c_abi(abi: &Option<Abi>) -> bool {
    abi == &Some(Abi::Named(String::from("C")))
}

fn map_path(p: &Path) -> String {
    let l = p.segments[0].ident.to_string();
    let mut c = match l.as_ref() {
        "usize" => "size_t".to_string(),
        "u8" => "uint8_t".to_string(),
        "u32" => "uint32_t".to_string(),
        "u64" => "uint64_t".to_string(),
        "i8" => "int8_t".to_string(),
        "i32" => "int32_t".to_string(),
        "i64" => "int64_t".to_string(),
        "f32" => "float".to_string(),
        "c_void" => "void".to_string(),
        _ => l,
    };
    if let PathParameters::AngleBracketed(ref d) = p.segments[0].parameters {
        let template_args = d.types
            .iter()
            .map(|ty| map_ty(ty).into())
            .collect::<Vec<String>>()
            .join(", ");
        if !template_args.is_empty() {
            c.push('<');
            c.push_str(&template_args);
            c.push('>');
        }
    }
    c
}

fn map_mut_ty(mut_ty: &MutTy) -> ConvertedType {
    map_ty(&mut_ty.ty)
}

fn map_ty(ty: &Ty) -> ConvertedType {
    match ty {
        &Ty::Path(_, ref p) => ConvertedType::from(map_path(p)),
        &Ty::Ptr(ref p) => ConvertedType::from(format!("{}*", map_ty(&p.ty))),
        &Ty::Rptr(_, ref mut_ty) => ConvertedType::from(format!("{}*", map_mut_ty(mut_ty))),
        &Ty::Array(ref p, ConstExpr::Lit(Lit::Int(sz, _))) => ConvertedType::new(map_ty(&p), format!("[{}]", sz)),
        _ => ConvertedType::from(format!("unknown {:?}", ty)),
    }
}

fn map_return_type(ret: &FunctionRetTy) -> ConvertedType {
    match ret {
        &FunctionRetTy::Default => ConvertedType::from("void".to_string()),
        &FunctionRetTy::Ty(ref ty) => map_ty(ty),
    }
}

fn map_arg(f: &FnArg) -> String {
    match f {
        &FnArg::Captured(Pat::Ident(_, ref ident, _), ref ty) => map_ty(ty).format_with_ident(ident),
        _ => "unknown".to_string(),
    }
}

fn map_field(f: &Field) -> String {
    let mut ret = String::from("  ");
    let converted = map_ty(&f.ty);
    ret.push_str(&converted.format_with_ident(f.ident.as_ref().expect("Struct fields must have idents")));
    ret.push_str(";\n");
    ret
}

fn map_generic_param(t: &TyParam) -> String {
    let mut ret = String::from("typename ");
    ret.push_str(&t.ident.to_string());
    ret
}

fn fold_enum_variants(accum: (String, i32), v: &Variant) -> (String, i32) {
    // `accum` contains the combined string of converted enum variants so far, to which
    // we will append the converted version of `v`. The other thing in `accum` is the
    // value of the previous variant. If `v` has an explicit value we keep that, otherwise
    // we increment the value of the previous variant to get the new one. This is all
    // so that we properly support enums with explicitly-specified and discontinuous
    // values.
    let mut ret = accum.0;
    ret.push_str("  ");
    ret.push_str(&v.ident.to_string());
    ret.push_str(" = ");
    let new_value = match &v.discriminant {
        &None => accum.1 + 1,
        &Some(ConstExpr::Lit(Lit::Int(ref specified_value, _))) => *specified_value as i32,
        &Some(_) => {
            // we don't handle this yet, so just put in something that will fail C compilation
            writeln!(io::stderr(), "warning, unsupported enum discriminant").unwrap();
            ret.push_str("???");
            accum.1 + 1
        }
    };
    ret.push_str(&new_value.to_string());
    ret.push_str(",\n");
    (ret, new_value)
}

/// A structure to store all the results of converting Rust stuff to C
/// stuff, so that we can pick and choose what we output.
struct ConversionResults {
    /// This holds the C function signatures of no_mangle rust functions,
    /// in the order that they were encountered in Rust.
    funcs: Vec<String>,
    /// This holds the C conversions of repr(C) structs and repr(u32) enums
    /// from Rust. The 'ds' in the name stands for 'data structures'. The
    /// key of the map is the name of the type, and the value is the C code.
    c_ds: HashMap<String, String>,
    /// This holds the dependency tree. If a struct contains another struct
    /// or an enum it will be in this map. The key is the dependent type,
    /// the list of values are all the things it depends on.
    _dep_tree: HashMap<String, Vec<String>>,
}

fn main() {
    let p = env::args().nth(1).unwrap();

    let results = Mutex::new(ConversionResults {
        funcs: Vec::new(),
        c_ds: HashMap::new(),
        _dep_tree: HashMap::new(),
    });

    rust_lib::parse(p, &|mod_name, items| {
        for item in items {
            match item.node {
                ItemKind::Fn(ref decl,
                             ref _unsafe,
                             ref _const,
                             ref abi,
                             ref _generic,
                             ref _block) => {
                    writeln!(io::stderr(), "processing function {}::{}", mod_name, &item.ident).unwrap();
                    if has_no_mangle(&item.attrs) && is_c_abi(&abi) {
                        results.lock().unwrap().funcs.push(
                            format!("WR_INLINE {}\n{}({})\n{};\n",
                                    map_return_type(&decl.output),
                                    item.ident,
                                    decl.inputs
                                        .iter()
                                        .map(map_arg)
                                        .collect::<Vec<_>>()
                                        .join(", "),
                                    wr_func_body(&item.attrs)));
                    }
                }
                ItemKind::Struct(ref variant,
                                 ref generics) => {
                    writeln!(io::stderr(), "processing struct {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_c(&item.attrs) {
                        if let &VariantData::Struct(ref fields) = variant {
                            let mut c_struct = String::new();
                            if !generics.ty_params.is_empty() {
                                c_struct.push_str(&(
                                    format!("template<{}>",
                                            generics.ty_params
                                                    .iter()
                                                    .map(map_generic_param)
                                                    .collect::<Vec<_>>()
                                                    .join(", "))));
                            }
                            c_struct.push_str(&(
                                format!("struct {} {{\n{}}};\n",
                                        item.ident,
                                        fields.iter()
                                              .map(map_field)
                                              .collect::<String>())));
                            results.lock().unwrap().c_ds.insert(
                                item.ident.to_string(),
                                c_struct);
                        }
                    }
                }
                ItemKind::Enum(ref variants, ref _generics) => {
                    writeln!(io::stderr(), "processing enum {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_u32(&item.attrs) {
                        let c_enum = format!("enum class {}: uint32_t {{\n{}\n  Sentinel /* this must be last for serialization purposes. */\n}};\n",
                                             item.ident,
                                             variants.iter()
                                                     .fold((String::new(), -1), fold_enum_variants)
                                                     .0);
                        results.lock().unwrap().c_ds.insert(
                            item.ident.to_string(),
                            c_enum);
                    }
                }
                _ => {}
            }
        }
    });
    for (_, c_stuff) in &results.lock().unwrap().c_ds {
        println!("{}", c_stuff);
    }
    for func in &results.lock().unwrap().funcs {
        println!("{}", func);
    }
}
<commit_msg>Make map_path return a ConvertedType<commit_after>use std::collections::HashMap;
use std::env;
use std::fmt;
use std::io;
use std::io::Write;
use std::sync::Mutex;

extern crate syn;
use syn::*;

mod rust_lib;

#[derive(Debug)]
struct ConvertedType {
    /// The type converted to C (e.g. `uint32_t`)
    prefix: String,
    /// Stuff that might need to go after the identifier (e.g. `[3]` for an array).
    postfix: String,
}

impl ConvertedType {
    fn new<T: Into<String>>(pre: T, post: String) -> Self {
        ConvertedType {
            prefix: pre.into(),
            postfix: post
        }
    }

    fn format_with_ident(&self, ident: &Ident) -> String {
        format!("{} {}{}", &self.prefix, &ident.to_string(), &self.postfix)
    }
}

impl Into<String> for ConvertedType {
    fn into(self) -> String {
        let mut str = String::from(self.prefix);
        str.push_str(&self.postfix);
        str
    }
}

impl From<String> for ConvertedType {
    fn from(str: String) -> ConvertedType {
        ConvertedType {
            prefix: str,
            postfix: String::new()
        }
    }
}

impl fmt::Display for ConvertedType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", &self.prefix, &self.postfix)
    }
}

fn has_attribute(target: MetaItem, attrs: &Vec<Attribute>) -> bool {
    return attrs
               .iter()
               .any(|ref attr| attr.style == AttrStyle::Outer && attr.value == target);
}

fn has_no_mangle(attrs: &Vec<Attribute>) -> bool {
    has_attribute(MetaItem::Word(Ident::new("no_mangle")), attrs)
}

fn wr_func_body(attrs: &Vec<Attribute>) -> String {
    if has_attribute(MetaItem::Word(Ident::new("destructor_safe")), attrs) {
        String::from("WR_DESTRUCTOR_SAFE_FUNC")
    } else {
        String::from("WR_FUNC")
    }
}

fn is_repr_c(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("C")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_repr_u32(attrs: &Vec<Attribute>) -> bool {
    let repr_args = vec![NestedMetaItem::MetaItem(MetaItem::Word(Ident::new("u32")))];
    has_attribute(MetaItem::List(Ident::new("repr"), repr_args), attrs)
}

fn is_c_abi(abi: &Option<Abi>) -> bool {
    abi == &Some(Abi::Named(String::from("C")))
}

fn map_path(p: &Path) -> ConvertedType {
    let l = p.segments[0].ident.to_string();
    let mut c = match l.as_ref() {
        "usize" => "size_t".to_string(),
        "u8" => "uint8_t".to_string(),
        "u32" => "uint32_t".to_string(),
        "u64" => "uint64_t".to_string(),
        "i8" => "int8_t".to_string(),
        "i32" => "int32_t".to_string(),
        "i64" => "int64_t".to_string(),
        "f32" => "float".to_string(),
        "c_void" => "void".to_string(),
        _ => l,
    };
    if let PathParameters::AngleBracketed(ref d) = p.segments[0].parameters {
        let template_args = d.types
            .iter()
            .map(|ty| map_ty(ty).into())
            .collect::<Vec<String>>()
            .join(", ");
        if !template_args.is_empty() {
            c.push('<');
            c.push_str(&template_args);
            c.push('>');
        }
    }
    ConvertedType::from(c)
}

fn map_mut_ty(mut_ty: &MutTy) -> ConvertedType {
    map_ty(&mut_ty.ty)
}

fn map_ty(ty: &Ty) -> ConvertedType {
    match ty {
        &Ty::Path(_, ref p) => map_path(p),
        &Ty::Ptr(ref p) => ConvertedType::from(format!("{}*", map_ty(&p.ty))),
        &Ty::Rptr(_, ref mut_ty) => ConvertedType::from(format!("{}*", map_mut_ty(mut_ty))),
        &Ty::Array(ref p, ConstExpr::Lit(Lit::Int(sz, _))) => ConvertedType::new(map_ty(&p), format!("[{}]", sz)),
        _ => ConvertedType::from(format!("unknown {:?}", ty)),
    }
}

fn map_return_type(ret: &FunctionRetTy) -> ConvertedType {
    match ret {
        &FunctionRetTy::Default => ConvertedType::from("void".to_string()),
        &FunctionRetTy::Ty(ref ty) => map_ty(ty),
    }
}

fn map_arg(f: &FnArg) -> String {
    match f {
        &FnArg::Captured(Pat::Ident(_, ref ident, _), ref ty) => map_ty(ty).format_with_ident(ident),
        _ => "unknown".to_string(),
    }
}

fn map_field(f: &Field) -> String {
    let mut ret = String::from("  ");
    let converted = map_ty(&f.ty);
    ret.push_str(&converted.format_with_ident(f.ident.as_ref().expect("Struct fields must have idents")));
    ret.push_str(";\n");
    ret
}

fn map_generic_param(t: &TyParam) -> String {
    let mut ret = String::from("typename ");
    ret.push_str(&t.ident.to_string());
    ret
}

fn fold_enum_variants(accum: (String, i32), v: &Variant) -> (String, i32) {
    // `accum` contains the combined string of converted enum variants so far, to which
    // we will append the converted version of `v`. The other thing in `accum` is the
    // value of the previous variant. If `v` has an explicit value we keep that, otherwise
    // we increment the value of the previous variant to get the new one. This is all
    // so that we properly support enums with explicitly-specified and discontinuous
    // values.
    let mut ret = accum.0;
    ret.push_str("  ");
    ret.push_str(&v.ident.to_string());
    ret.push_str(" = ");
    let new_value = match &v.discriminant {
        &None => accum.1 + 1,
        &Some(ConstExpr::Lit(Lit::Int(ref specified_value, _))) => *specified_value as i32,
        &Some(_) => {
            // we don't handle this yet, so just put in something that will fail C compilation
            writeln!(io::stderr(), "warning, unsupported enum discriminant").unwrap();
            ret.push_str("???");
            accum.1 + 1
        }
    };
    ret.push_str(&new_value.to_string());
    ret.push_str(",\n");
    (ret, new_value)
}

/// A structure to store all the results of converting Rust stuff to C
/// stuff, so that we can pick and choose what we output.
struct ConversionResults {
    /// This holds the C function signatures of no_mangle rust functions,
    /// in the order that they were encountered in Rust.
    funcs: Vec<String>,
    /// This holds the C conversions of repr(C) structs and repr(u32) enums
    /// from Rust. The 'ds' in the name stands for 'data structures'. The
    /// key of the map is the name of the type, and the value is the C code.
    c_ds: HashMap<String, String>,
    /// This holds the dependency tree. If a struct contains another struct
    /// or an enum it will be in this map. The key is the dependent type,
    /// the list of values are all the things it depends on.
    _dep_tree: HashMap<String, Vec<String>>,
}

fn main() {
    let p = env::args().nth(1).unwrap();

    let results = Mutex::new(ConversionResults {
        funcs: Vec::new(),
        c_ds: HashMap::new(),
        _dep_tree: HashMap::new(),
    });

    rust_lib::parse(p, &|mod_name, items| {
        for item in items {
            match item.node {
                ItemKind::Fn(ref decl,
                             ref _unsafe,
                             ref _const,
                             ref abi,
                             ref _generic,
                             ref _block) => {
                    writeln!(io::stderr(), "processing function {}::{}", mod_name, &item.ident).unwrap();
                    if has_no_mangle(&item.attrs) && is_c_abi(&abi) {
                        results.lock().unwrap().funcs.push(
                            format!("WR_INLINE {}\n{}({})\n{};\n",
                                    map_return_type(&decl.output),
                                    item.ident,
                                    decl.inputs
                                        .iter()
                                        .map(map_arg)
                                        .collect::<Vec<_>>()
                                        .join(", "),
                                    wr_func_body(&item.attrs)));
                    }
                }
                ItemKind::Struct(ref variant,
                                 ref generics) => {
                    writeln!(io::stderr(), "processing struct {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_c(&item.attrs) {
                        if let &VariantData::Struct(ref fields) = variant {
                            let mut c_struct = String::new();
                            if !generics.ty_params.is_empty() {
                                c_struct.push_str(&(
                                    format!("template<{}>",
                                            generics.ty_params
                                                    .iter()
                                                    .map(map_generic_param)
                                                    .collect::<Vec<_>>()
                                                    .join(", "))));
                            }
                            c_struct.push_str(&(
                                format!("struct {} {{\n{}}};\n",
                                        item.ident,
                                        fields.iter()
                                              .map(map_field)
                                              .collect::<String>())));
                            results.lock().unwrap().c_ds.insert(
                                item.ident.to_string(),
                                c_struct);
                        }
                    }
                }
                ItemKind::Enum(ref variants, ref _generics) => {
                    writeln!(io::stderr(), "processing enum {}::{}", mod_name, &item.ident).unwrap();
                    if is_repr_u32(&item.attrs) {
                        let c_enum = format!("enum class {}: uint32_t {{\n{}\n  Sentinel /* this must be last for serialization purposes. */\n}};\n",
                                             item.ident,
                                             variants.iter()
                                                     .fold((String::new(), -1), fold_enum_variants)
                                                     .0);
                        results.lock().unwrap().c_ds.insert(
                            item.ident.to_string(),
                            c_enum);
                    }
                }
                _ => {}
            }
        }
    });
    for (_, c_stuff) in &results.lock().unwrap().c_ds {
        println!("{}", c_stuff);
    }
    for func in &results.lock().unwrap().funcs {
        println!("{}", func);
    }
}
<|endoftext|>


<commit_before>// Based on smallpt, http://www.kevinbeason.com/smallpt/ which is also licensed under
// the MIT license.
use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::*;
use std::sync::Arc;
use std::sync::mpsc::channel;

extern crate rand;
use rand::{Rng, XorShiftRng, SeedableRng};

extern crate threadpool;
use threadpool::ThreadPool;

extern crate num_cpus;

mod path_tracer;
use path_tracer::*;

enum Material {
    Diffuse,
    Specular,
    Refractive
}

struct Sphere {
    material: Material,
    radius: f64,
    position: Vec3d,
    emission: Vec3d,
    colour: Vec3d
}

impl Sphere {
    fn new(material: Material, radius: f64, position: Vec3d, emission: Vec3d, colour: Vec3d) -> Sphere {
        Sphere {
          material: material,
          radius: radius,
          position: position,
          emission: emission,
          colour: colour
        }
    }
    fn intersect(&self, ray: &Ray) -> Option<f64> {
        let op = self.position - ray.origin;
        let b = op.dot(ray.direction);
        let determinant = b * b - op.dot(op) + self.radius * self.radius;
        if determinant < 0.0 { return None; }
        let determinant = determinant.sqrt();
        let t1 = b - determinant;
        let t2 = b + determinant;
        const EPSILON : f64 = 0.0001;
        if t1 > EPSILON {
            Some(t1)
        } else if t2 > EPSILON {
            Some(t2)
        } else {
            None
        }
    }
}

#[test]
fn intersection() {
    let sphere = Sphere::new(
        Material::Diffuse,
        100.0,
        Vec3d::new(0.0, 0.0, 200.0),
        Vec3d::zero(),
        Vec3d::zero());
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 0.0, 1.0));
    match sphere.intersect(&ray) {
        Some(x) => assert_eq!(x, 100.0),
        None => panic!("unexpected")
    }
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 1.0, 0.0));
    match sphere.intersect(&ray) {
        Some(_) => panic!("unexpected"),
        None => {}
    }
}

struct HitRecord<'a> {
    sphere: &'a Sphere,
    dist: f64
}

fn intersect<'a>(scene: &'a [Sphere], ray: &Ray) -> Option<HitRecord<'a>> {
    let mut result : Option<HitRecord<'a>> = None;
    for sph in scene {
        if let Some(dist) = sph.intersect(&ray) {
            if match result { None => true, Some(ref x) => dist < x.dist } {
                result = Some(HitRecord { sphere: &sph, dist: dist });
            }
        }
    }
    result
}

fn radiance<R: Rng>(scene: &[Sphere], ray: &Ray, depth: i32, rng: &mut R) -> Vec3d {
    if let Some(hit) = intersect(&scene, &ray) {
        let hit_pos = ray.origin + ray.direction * hit.dist;
        let hit_normal = (hit_pos - hit.sphere.position).normalized();
        let n1 = if hit_normal.dot(ray.direction) < 0.0 { hit_normal } else { hit_normal.neg() };
        let mut colour = hit.sphere.colour;
        let max_reflectance = colour.max_component();
        let depth = depth + 1;
        if depth > 5 {
            let rand = rng.gen::<f64>();
            if rand < max_reflectance && depth < 500 { // Rust's stack blows up ~600 on my machine
               colour = colour * (1.0 / max_reflectance);
            } else {
                return hit.sphere.emission;
            }
        }
        match hit.sphere.material {
            Material::Diffuse => {
                // Get a random polar coordinate
                let r1 = rng.gen::<f64>() * 2.0 * std::f64::consts::PI;
                let r2 = rng.gen::<f64>();
                let r2s = r2.sqrt();
                // Create a coordinate system u,v,w local to the point, where the w is the normal
                // pointing out of the sphere and the u and v are orthonormal to w.
                let w = n1;
                // Pick an arbitrary non-zero preferred axis for u
                let u = if n1.x.abs() > 0.1 { Vec3d::new(0.0, 1.0, 0.0) } else { Vec3d::new(1.0, 0.0, 0.0) }.cross(w);
                let v = w.cross(u);
                // construct the new direction
                let new_dir = u * r1.cos() * r2s + v * r1.sin() * r2s + w * (1.0 - r2).sqrt();
                colour = colour * radiance(scene, &Ray::new(hit_pos, new_dir.normalized()), depth, rng);
            },
            Material::Specular => {
                let reflection = ray.direction - hit_normal * 2.0 * hit_normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit_pos, reflection);
                colour = colour * radiance(scene, &reflected_ray, depth, rng);
            },
            Material::Refractive => {
                let reflection = ray.direction - hit_normal * 2.0 * hit_normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit_pos, reflection);
                let into = if hit_normal.dot(n1) > 0.0 { true } else { false };
                let nc = 1.0;
                let nt = 1.5;
                let nnt = if into { nc/nt } else { nt/nc };
                let ddn = ray.direction.dot(n1);
                let cos2t = 1.0 - nnt * nnt * (1.0 - ddn * ddn);
                if cos2t < 0.0 {
                    // Total internal reflection
                    colour = colour * radiance(scene, &reflected_ray, depth, rng);
                } else {
                    let tbd = ddn * nnt + cos2t.sqrt();
                    let tbd = if into { tbd } else { -tbd };
                    let tdir = (ray.direction * nnt - hit_normal * tbd).normalized();
                    let transmitted_ray = Ray::new(hit_pos, tdir);
                    let a = nt - nc;
                    let b = nt + nc;
                    let r0 = (a * a) / (b * b);
                    let c = 1.0 - if into { -ddn } else { tdir.dot(hit_normal) };
                    let re = r0 + (1.0 - r0) * c * c * c * c * c;
                    let tr = 1.0 - re;
                    let p = 0.25 + 0.5 * re;
                    let rp = re / p;
                    let tp = tr / (1.0 - p);
                    colour = colour * if depth > 2 {
                        if rng.gen::<f64>() < p {
                            radiance(scene, &reflected_ray, depth, rng) * rp
                        } else {
                            radiance(scene, &transmitted_ray, depth, rng) * tp
                        }
                    } else {
                        radiance(scene, &reflected_ray, depth, rng) * re +
                            radiance(scene, &transmitted_ray, depth, rng) * tr
                    }
                }
            }
        }
        hit.sphere.emission + colour
    } else {
        Vec3d::zero()
    }
}

fn random_samp<T: Rng>(rng: &mut T) -> f64 {
    let r = 2.0 * rng.gen::<f64>();
    if r < 1.0 { r.sqrt() - 1.0 } else { 1.0 - (2.0 - r).sqrt() }
}

fn to_int(v: f64) -> u8 {
    let ch = (v.powf(1.0/2.2) * 255.0 + 0.5) as i64;
    if ch < 0 { 0 } else if ch > 255 { 255 } else { ch as u8 }
}

fn main() {
    const BLACK : Vec3d = Vec3d { x: 0.0, y: 0.0, z: 0.0 };
    const RED : Vec3d = Vec3d { x: 0.75, y: 0.25, z: 0.25 };
    const BLUE : Vec3d = Vec3d { x: 0.25, y: 0.25, z: 0.75 };
    const GREY : Vec3d = Vec3d { x: 0.75, y: 0.75, z: 0.75 };
    const WHITE : Vec3d = Vec3d { x: 0.999, y: 0.999, z: 0.999 };
    let scene = Arc::new(vec!{
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(1e5+1.0, 40.8, 81.6),
            BLACK, RED),
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(-1e5+99.0, 40.8, 81.6),
            BLACK, BLUE),
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(50.0, 40.8, 1e5),
            BLACK, GREY),
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(50.0, 40.8, -1e5 + 170.0),
            BLACK, BLACK),
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(50.0, 1e5, 81.6),
            BLACK, GREY),
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(50.0, -1e5 + 81.6, 81.6),
            BLACK, RED),
        Sphere::new(Material::Specular, 16.5,
            Vec3d::new(27.0, 16.5, 47.0),
            BLACK, WHITE),
        Sphere::new(Material::Refractive, 16.5,
            Vec3d::new(73.0, 16.5, 78.0),
            BLACK, WHITE),
        Sphere::new(Material::Diffuse, 600.0,
            Vec3d::new(50.0, 681.6 - 0.27, 81.6),
            Vec3d::new(12.0, 12.0, 12.0), BLACK),
    });

    const WIDTH: usize = 1024;
    const HEIGHT: usize = 768;
    let samps = 1;
    let camera_pos = Vec3d::new(50.0, 52.0, 295.6);
    let camera_dir = Vec3d::new(0.0, -0.042612, -1.0);
    let camera_x = Vec3d::new(WIDTH as f64 * 0.5135 / HEIGHT as f64, 0.0, 0.0);
    let camera_y = camera_x.cross(camera_dir).normalized() * 0.5135;

    let num_threads = num_cpus::get();
    println!("Using {} threads", num_threads);
    let pool = ThreadPool::new(num_threads);
    let (tx, rx) = channel();

    for y in 0..HEIGHT {
        let tx = tx.clone();
        let scene = scene.clone();
        pool.execute(move || {
            let mut line = Vec::with_capacity(WIDTH);
            for x in 0..WIDTH {
                let mut rng = XorShiftRng::from_seed([10, 200, 999999, y as u32]);
                let mut sum = Vec3d::zero();
                for sx in 0..2 {
                    for sy in 0..2 {
                        for _samp in 0..samps {
                            let dx = random_samp(&mut rng);
                            let dy = random_samp(&mut rng);
                            let dir = camera_x * (((sx as f64 + 0.5 + dx)/2.0 + x as f64) / WIDTH as f64 - 0.5) +
            camera_y * (((sy as f64 + 0.5 + dy)/2.0 + (HEIGHT - y - 1) as f64) / HEIGHT as f64  - 0.5) + camera_dir;
                            let jittered_ray = Ray::new(camera_pos + dir * 140.0, dir.normalized());
                            let sample = radiance(&scene, &jittered_ray, 0, &mut rng);
                            sum = sum + sample;
                        }
                    }
                }
                line.push(sum / (samps * 4) as f64);
            }
            tx.send((y, line)).unwrap();
        });
    }
    let mut left = HEIGHT;
    let mut screen : Vec<Vec<Vec3d>> = Vec::new();
    for _y in 0..HEIGHT {
        screen.push(Vec::new());
    }
    while left > 0 {
        println!("Rendering ({} spp) {:.4}%...", samps * 4, 100.0 * (HEIGHT - left) as f64 / HEIGHT as f64);
        let (y, line) = rx.recv().unwrap();
        screen[y] = line;
        left -= 1;
    }
    println!("Writing output");
    let output_file = File::create("image.ppm").unwrap();
    let mut writer = BufWriter::new(output_file);
    write!(&mut writer, "P3\n{} {}\n255\n", WIDTH, HEIGHT).unwrap();
    for y in 0..HEIGHT {
        for x in 0..WIDTH {
            let sum = screen[y][x];
            write!(&mut writer, "{} {} {} ", to_int(sum.x), to_int(sum.y), to_int(sum.z)).unwrap();
        }
    }
}
<commit_msg>Fiddlings to try and work out what's going on with all the streaky stuff at low iteration counts<commit_after>// Based on smallpt, http://www.kevinbeason.com/smallpt/ which is also licensed under
// the MIT license.
use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::*;
use std::sync::Arc;
use std::sync::mpsc::channel;

extern crate rand;
use rand::{Rng, XorShiftRng, SeedableRng};

extern crate threadpool;
use threadpool::ThreadPool;

extern crate num_cpus;

mod path_tracer;
use path_tracer::*;

enum Material {
    Diffuse,
    Specular,
    Refractive
}

struct Sphere {
    material: Material,
    radius: f64,
    position: Vec3d,
    emission: Vec3d,
    colour: Vec3d
}

impl Sphere {
    fn new(material: Material, radius: f64, position: Vec3d, emission: Vec3d, colour: Vec3d) -> Sphere {
        Sphere {
          material: material,
          radius: radius,
          position: position,
          emission: emission,
          colour: colour
        }
    }
    fn intersect(&self, ray: &Ray) -> Option<f64> {
        let op = self.position - ray.origin;
        let b = op.dot(ray.direction);
        let determinant = b * b - op.dot(op) + self.radius * self.radius;
        if determinant < 0.0 { return None; }
        let determinant = determinant.sqrt();
        let t1 = b - determinant;
        let t2 = b + determinant;
        const EPSILON : f64 = 0.0001;
        if t1 > EPSILON {
            Some(t1)
        } else if t2 > EPSILON {
            Some(t2)
        } else {
            None
        }
    }
}

#[test]
fn intersection() {
    let sphere = Sphere::new(
        Material::Diffuse,
        100.0,
        Vec3d::new(0.0, 0.0, 200.0),
        Vec3d::zero(),
        Vec3d::zero());
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 0.0, 1.0));
    match sphere.intersect(&ray) {
        Some(x) => assert_eq!(x, 100.0),
        None => panic!("unexpected")
    }
    let ray = Ray::new(Vec3d::zero(), Vec3d::new(0.0, 1.0, 0.0));
    match sphere.intersect(&ray) {
        Some(_) => panic!("unexpected"),
        None => {}
    }
}

struct HitRecord<'a> {
    sphere: &'a Sphere,
    dist: f64
}

fn intersect<'a>(scene: &'a [Sphere], ray: &Ray) -> Option<HitRecord<'a>> {
    let mut result : Option<HitRecord<'a>> = None;
    for sph in scene {
        if let Some(dist) = sph.intersect(&ray) {
            if match result { None => true, Some(ref x) => dist < x.dist } {
                result = Some(HitRecord { sphere: &sph, dist: dist });
            }
        }
    }
    result
}

fn radiance<R: Rng>(scene: &[Sphere], ray: &Ray, depth: i32, rng: &mut R) -> Vec3d {
    if let Some(hit) = intersect(&scene, &ray) {
        let hit_pos = ray.origin + ray.direction * hit.dist;
        let hit_normal = (hit_pos - hit.sphere.position).normalized();
        let n1 = if hit_normal.dot(ray.direction) < 0.0 { hit_normal } else { hit_normal.neg() };
        let mut colour = hit.sphere.colour;
        let max_reflectance = colour.max_component();
        let depth = depth + 1;
        if depth > 5 {
            let rand = rng.gen::<f64>();
            if rand < max_reflectance && depth < 500 { // Rust's stack blows up ~600 on my machine
               colour = colour * (1.0 / max_reflectance);
            } else {
                return hit.sphere.emission;
            }
        }
        match hit.sphere.material {
            Material::Diffuse => {
                // Get a random polar coordinate
                let r1 = rng.gen::<f64>() * 2.0 * std::f64::consts::PI;
                let r2 = rng.gen::<f64>();
                let r2s = r2.sqrt();
                // Create a coordinate system u,v,w local to the point, where the w is the normal
                // pointing out of the sphere and the u and v are orthonormal to w.
                let w = n1;
                // Pick an arbitrary non-zero preferred axis for u
                let u = if n1.x.abs() > 0.1 { Vec3d::new(0.0, 1.0, 0.0) } else { Vec3d::new(1.0, 0.0, 0.0) }.cross(w);
                let v = w.cross(u);
                // construct the new direction
                let new_dir = u * r1.cos() * r2s + v * r1.sin() * r2s + w * (1.0 - r2).sqrt();
                colour = colour * radiance(scene, &Ray::new(hit_pos, new_dir.normalized()), depth, rng);
            },
            Material::Specular => {
                let reflection = ray.direction - hit_normal * 2.0 * hit_normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit_pos, reflection);
                colour = colour * radiance(scene, &reflected_ray, depth, rng);
            },
            Material::Refractive => {
                let reflection = ray.direction - hit_normal * 2.0 * hit_normal.dot(ray.direction);
                let reflected_ray = Ray::new(hit_pos, reflection);
                let into = if hit_normal.dot(n1) > 0.0 { true } else { false };
                let nc = 1.0;
                let nt = 1.5;
                let nnt = if into { nc/nt } else { nt/nc };
                let ddn = ray.direction.dot(n1);
                let cos2t = 1.0 - nnt * nnt * (1.0 - ddn * ddn);
                if cos2t < 0.0 {
                    // Total internal reflection
                    colour = colour * radiance(scene, &reflected_ray, depth, rng);
                } else {
                    let tbd = ddn * nnt + cos2t.sqrt();
                    let tbd = if into { tbd } else { -tbd };
                    let tdir = (ray.direction * nnt - hit_normal * tbd).normalized();
                    let transmitted_ray = Ray::new(hit_pos, tdir);
                    let a = nt - nc;
                    let b = nt + nc;
                    let r0 = (a * a) / (b * b);
                    let c = 1.0 - if into { -ddn } else { tdir.dot(hit_normal) };
                    let re = r0 + (1.0 - r0) * c * c * c * c * c;
                    let tr = 1.0 - re;
                    let p = 0.25 + 0.5 * re;
                    let rp = re / p;
                    let tp = tr / (1.0 - p);
                    colour = colour * if depth > 2 {
                        if rng.gen::<f64>() < p {
                            radiance(scene, &reflected_ray, depth, rng) * rp
                        } else {
                            radiance(scene, &transmitted_ray, depth, rng) * tp
                        }
                    } else {
                        radiance(scene, &reflected_ray, depth, rng) * re +
                            radiance(scene, &transmitted_ray, depth, rng) * tr
                    }
                }
            }
        }
        hit.sphere.emission + colour
    } else {
        Vec3d::zero()
    }
}

fn random_samp<T: Rng>(rng: &mut T) -> f64 {
    let r = 2.0 * rng.gen::<f64>();
    if r < 1.0 { r.sqrt() - 1.0 } else { 1.0 - (2.0 - r).sqrt() }
}

fn to_int(v: f64) -> u8 {
    let ch = (v.powf(1.0/2.2) * 255.0 + 0.5) as i64;
    if ch < 0 { 0 } else if ch > 255 { 255 } else { ch as u8 }
}

fn main() {
    const BLACK : Vec3d = Vec3d { x: 0.0, y: 0.0, z: 0.0 };
    const RED : Vec3d = Vec3d { x: 0.75, y: 0.25, z: 0.25 };
    const BLUE : Vec3d = Vec3d { x: 0.25, y: 0.25, z: 0.75 };
    const GREY : Vec3d = Vec3d { x: 0.75, y: 0.75, z: 0.75 };
    const WHITE : Vec3d = Vec3d { x: 0.999, y: 0.999, z: 0.999 };
    let scene = Arc::new(vec!{
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(1e5+1.0, 40.8, 81.6),
            BLACK, RED),
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(-1e5+99.0, 40.8, 81.6),
            BLACK, BLUE),
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(50.0, 40.8, 1e5),
            BLACK, GREY),
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(50.0, 40.8, -1e5 + 170.0),
            BLACK, BLACK),
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(50.0, 1e5, 81.6),
            BLACK, GREY),
        Sphere::new(Material::Diffuse, 1e5,
            Vec3d::new(50.0, -1e5 + 81.6, 81.6),
            BLACK, RED),
        Sphere::new(Material::Specular, 16.5,
            Vec3d::new(27.0, 16.5, 47.0),
            BLACK, WHITE),
        Sphere::new(Material::Refractive, 16.5,
            Vec3d::new(73.0, 16.5, 78.0),
            BLACK, WHITE),
        Sphere::new(Material::Diffuse, 600.0,
            Vec3d::new(50.0, 681.6 - 0.27, 81.6),
            Vec3d::new(12.0, 12.0, 12.0), BLACK),
    });

    const WIDTH: usize = 1024;
    const HEIGHT: usize = 768;
    let samps = 1;
    let camera_pos = Vec3d::new(50.0, 52.0, 295.6);
    let camera_dir = Vec3d::new(0.0, -0.042612, -1.0);
    let camera_x = Vec3d::new(WIDTH as f64 * 0.5135 / HEIGHT as f64, 0.0, 0.0);
    let camera_y = camera_x.cross(camera_dir).normalized() * 0.5135;

    let num_threads = num_cpus::get();
    println!("Using {} threads", num_threads);
    let pool = ThreadPool::new(num_threads);
    let (tx, rx) = channel();

    for y in 0..HEIGHT {
        let tx = tx.clone();
        let scene = scene.clone();
        pool.execute(move || {
            let mut line = Vec::with_capacity(WIDTH);
            for x in 0..WIDTH {
                let mut rng = XorShiftRng::from_seed([1 + y as u32, 1 + (y * y) as u32, 1 + y as u32, 1 + y as u32]);
                let mut sum = Vec3d::zero();
                for sx in 0..2 {
                    for sy in 0..2 {
                        for _samp in 0..samps {
                            let dx = random_samp(&mut rng);
                            let dy = random_samp(&mut rng);
                            let dir = camera_x * (((sx as f64 + 0.5 + dx)/2.0 + x as f64) / WIDTH as f64 - 0.5) +
            camera_y * (((sy as f64 + 0.5 + dy)/2.0 + (HEIGHT - y - 1) as f64) / HEIGHT as f64  - 0.5) + camera_dir;
                            let jittered_ray = Ray::new(camera_pos + dir * 140.0, dir.normalized());
                            let sample = radiance(&scene, &jittered_ray, 0, &mut rng);
                            sum = sum + sample;
                        }
                    }
                }
                line.push(sum / (samps * 4) as f64);
            }
            tx.send((y, line)).unwrap();
        });
    }
    let mut left = HEIGHT;
    let mut screen : Vec<Vec<Vec3d>> = Vec::new();
    for _y in 0..HEIGHT {
        screen.push(Vec::new());
    }
    while left > 0 {
        print!("Rendering ({} spp) {:.4}%...\r", samps * 4, 100.0 * (HEIGHT - left) as f64 / HEIGHT as f64);
        let (y, line) = rx.recv().unwrap();
        screen[y] = line;
        left -= 1;
    }
    println!("Writing output");
    let output_file = File::create("image.ppm").unwrap();
    let mut writer = BufWriter::new(output_file);
    write!(&mut writer, "P3\n{} {}\n255\n", WIDTH, HEIGHT).unwrap();
    for y in 0..HEIGHT {
        for x in 0..WIDTH {
            let sum = screen[y][x];
            write!(&mut writer, "{} {} {} ", to_int(sum.x), to_int(sum.y), to_int(sum.z)).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>use std::ops::Mul;
use std::fmt;

use numvec::Vec3f;

/// The type of matrix elements.
pub type Scalar = f32;

/// A 4x4 matrix type stored in column-major order for interoperability with
/// OpenGL.
///
/// Supports the creation of isometries and projections in homogenous
/// coordinates.  In terms of operations, only transposition and multiplication
/// are currently supported (and not super-efficiently implemented).
///
/// _Note:_ The 16 elements are stored in place, so copies are not cheap.
#[packed]
#[repr(C)]
pub struct Mat4 {
    data : [Scalar, ..16]
}

impl Mat4 {
    pub fn new(m00 : Scalar, m01 : Scalar, m02 : Scalar, m03 : Scalar,
               m10 : Scalar, m11 : Scalar, m12 : Scalar, m13 : Scalar,
               m20 : Scalar, m21 : Scalar, m22 : Scalar, m23 : Scalar,
               m30 : Scalar, m31 : Scalar, m32 : Scalar, m33 : Scalar) -> Mat4 {
        // In my mind vectors are columns, hence matrices need to be transposed
        // to the OpenGL memory order.
        Mat4 { data: [m00, m10, m20, m30, m01, m11, m21, m31,
                      m02, m12, m22, m32, m03, m13, m23, m33] }
    }

    pub fn new_identity() -> Mat4 {
        Mat4::new(1.0, 0.0, 0.0, 0.0,
                  0.0, 1.0, 0.0, 0.0,
                  0.0, 0.0, 1.0, 0.0,
                  0.0, 0.0, 0.0, 1.0)
    }

    /// Creates a perspective projection matrix from.
    ///
    /// The parameters are:
    /// * `fov_degrees`  - Horizontal field of view.
    /// * `aspect_ratio` - Ratio between width and height of the view.
    /// * `near`, `far`  - The Z coordinate of the near and far planes.
    pub fn new_perspective(fov_degrees: Scalar, aspect_ratio: Scalar,
                           near: Scalar, far: Scalar) -> Mat4 {
        let fov = (3.1415926538 * fov_degrees) / 180.0;
        let f = 1.0 / (fov * 0.5).tan();

        Mat4::new(
            f / aspect_ratio, 0.0, 0.0, 0.0,
            0.0, f, 0.0, 0.0,
            0.0, 0.0, (far + near) / (near - far), 2.0*far*near / (near - far),
            0.0, 0.0, -1.0, 0.0)
    }

    /// Creates a matrix which rotates points by `angle_radians` around `axis`.
    pub fn new_axis_rotation(axis: &Vec3f, angle_radians: Scalar) -> Mat4 {
        let ca = angle_radians.cos();
        let sa = angle_radians.sin();
        let nca = 1.0 - ca;
        let u = axis;
        Mat4::new(
            ca + u.x*u.x*nca, u.x*u.y*nca - u.z*sa, u.x*u.z*nca + u.y*sa, 0.0,
            u.y*u.x*nca + u.z*sa, ca + u.y*u.y*nca, u.y*u.z*nca - u.x*sa, 0.0,
            u.z*u.x*nca - u.y*sa, u.z*u.y*nca + u.x*sa, ca + u.z*u.z*nca, 0.0,
            0.0, 0.0, 0.0, 1.0
        )
    }

    /// Creates a rotation matrix from the three _Euler angles_.
    pub fn new_euler_rotation(yaw: f32, pitch: f32, roll: f32) -> Mat4 {
        let (ca, sa) = (yaw.cos(), yaw.sin());
        let (cb, sb) = (roll.cos(), roll.sin());
        let (cc, sc) = (pitch.cos(), pitch.sin());

        Mat4::new(
             ca*cb, sa*sc - ca*sb*cc,  ca*sb*sc + sa*cc, 0.0,
                sb,          cb * cc,          -cb * sc, 0.0,
            -sa*cb, sa*sb*cc + ca*sc, -sa*sb*sc + ca*cc, 0.0,
               0.0,              0.0,               0.0, 1.0,
        )
    }

    /// Creates a translation matrix which maps points `p` to `p + by`.
    pub fn new_translation(by: Vec3f) -> Mat4 {
        Mat4::new(1.0, 0.0, 0.0, by.x,
                  0.0, 1.0, 0.0, by.y,
                  0.0, 0.0, 1.0, by.z,
                  0.0, 0.0, 0.0,  1.0)
    }

    /// Returns the transpose of the matrix (columns swapped with rows).
    pub fn transposed(&self) -> Mat4 {
        let m = &self.data;
        // m is in column-major order, so calling with new in row-order will
        // transpose it.
        Mat4::new(m[0], m[1], m[2], m[3],
                  m[4], m[5], m[6], m[7],
                  m[8], m[9], m[10], m[11],
                  m[12], m[13], m[14], m[15])
    }

    pub fn get(&self, row: uint, column: uint) -> Scalar {
        self.data[column * 4 + row]
    }

    pub fn as_scalar_ptr(&self) -> *const Scalar {
        self.data.as_ptr()
    }
}

impl fmt::Show for Mat4 {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter,
               "[{:10.3e} {:10.3e} {:10.3e} {:10.3e};\n \
                 {:10.3e} {:10.3e} {:10.3e} {:10.3e};\n \
                 {:10.3e} {:10.3e} {:10.3e} {:10.3e};\n \
                 {:10.3e} {:10.3e} {:10.3e} {:10.3e}]",
               self.get(0, 0), self.get(0, 1), self.get(0, 2), self.get(0, 3),
               self.get(1, 0), self.get(1, 1), self.get(1, 2), self.get(1, 3),
               self.get(2, 0), self.get(2, 1), self.get(2, 2), self.get(2, 3),
               self.get(3, 0), self.get(3, 1), self.get(3, 2), self.get(3, 3))
    }
}

impl Mul<Mat4, Mat4> for Mat4 {
    fn mul(&self, rhs: &Mat4) -> Mat4 {
        let l = &self.data;
        let r = &rhs.data;
        Mat4 { data:[
            l[ 0]*r[ 0] + l[ 4]*r[ 1] + l[ 8]*r[ 2] + l[12]*r[ 3],
            l[ 1]*r[ 0] + l[ 5]*r[ 1] + l[ 9]*r[ 2] + l[13]*r[ 3],
            l[ 2]*r[ 0] + l[ 6]*r[ 1] + l[10]*r[ 2] + l[14]*r[ 3],
            l[ 3]*r[ 0] + l[ 7]*r[ 1] + l[11]*r[ 2] + l[15]*r[ 3],

            l[ 0]*r[ 4] + l[ 4]*r[ 5] + l[ 8]*r[ 6] + l[12]*r[ 7],
            l[ 1]*r[ 4] + l[ 5]*r[ 5] + l[ 9]*r[ 6] + l[13]*r[ 7],
            l[ 2]*r[ 4] + l[ 6]*r[ 5] + l[10]*r[ 6] + l[14]*r[ 7],
            l[ 3]*r[ 4] + l[ 7]*r[ 5] + l[11]*r[ 6] + l[15]*r[ 7],

            l[ 0]*r[ 8] + l[ 4]*r[ 9] + l[ 8]*r[10] + l[12]*r[11],
            l[ 1]*r[ 8] + l[ 5]*r[ 9] + l[ 9]*r[10] + l[13]*r[11],
            l[ 2]*r[ 8] + l[ 6]*r[ 9] + l[10]*r[10] + l[14]*r[11],
            l[ 3]*r[ 8] + l[ 7]*r[ 9] + l[11]*r[10] + l[15]*r[11],

            l[ 0]*r[12] + l[ 4]*r[13] + l[ 8]*r[14] + l[12]*r[15],
            l[ 1]*r[12] + l[ 5]*r[13] + l[ 9]*r[14] + l[13]*r[15],
            l[ 2]*r[12] + l[ 6]*r[13] + l[10]*r[14] + l[14]*r[15],
            l[ 3]*r[12] + l[ 7]*r[13] + l[11]*r[14] + l[15]*r[15],
        ]}
    }
}

mod test {
    #[test]
    fn test_mul() {
        let a = Mat4::new(4.0,    8.0,    1.0,    6.0,
                          9.0,    4.0,    2.0,    1.0,
                          4.0,    3.0,    9.0,    3.0,
                          2.0,    4.0,    9.0,    4.0);

        let b = Mat4::new(8.0,    6.0,    5.0,    7.0,
                          1.0,    7.0,    3.0,    2.0,
                          1.0,    6.0,    7.0,    4.0,
                          2.0,    5.0,    2.0,    6.0);

        let exp_ab = Mat4::new(53.0,   116.0,    63.0,    84.0,
                               80.0,    99.0,    73.0,    85.0,
                               50.0,   114.0,    98.0,    88.0,
                               37.0,   114.0,    93.0,    82.0);
        let ab = a * b;

        for i in range(0, 16) {
            if exp_ab.data[i] != ab.data[i] {
                fail!("They differ at {} ({}):\nexpected=\n{}\n\nactual=\n{}",
                      i, (a.data[i] - b.data[i]).abs(), exp_ab, ab);
            }
        }

    }
}
<commit_msg>Fixed Mat4 tests.<commit_after>use std::ops::Mul;
use std::fmt;

use numvec::Vec3f;

/// The type of matrix elements.
pub type Scalar = f32;

/// A 4x4 matrix type stored in column-major order for interoperability with
/// OpenGL.
///
/// Supports the creation of isometries and projections in homogenous
/// coordinates.  In terms of operations, only transposition and multiplication
/// are currently supported (and not super-efficiently implemented).
///
/// _Note:_ The 16 elements are stored in place, so copies are not cheap.
#[packed]
#[repr(C)]
pub struct Mat4 {
    data : [Scalar, ..16]
}

impl Mat4 {
    pub fn new(m00 : Scalar, m01 : Scalar, m02 : Scalar, m03 : Scalar,
               m10 : Scalar, m11 : Scalar, m12 : Scalar, m13 : Scalar,
               m20 : Scalar, m21 : Scalar, m22 : Scalar, m23 : Scalar,
               m30 : Scalar, m31 : Scalar, m32 : Scalar, m33 : Scalar) -> Mat4 {
        // In my mind vectors are columns, hence matrices need to be transposed
        // to the OpenGL memory order.
        Mat4 { data: [m00, m10, m20, m30, m01, m11, m21, m31,
                      m02, m12, m22, m32, m03, m13, m23, m33] }
    }

    pub fn new_identity() -> Mat4 {
        Mat4::new(1.0, 0.0, 0.0, 0.0,
                  0.0, 1.0, 0.0, 0.0,
                  0.0, 0.0, 1.0, 0.0,
                  0.0, 0.0, 0.0, 1.0)
    }

    /// Creates a perspective projection matrix from.
    ///
    /// The parameters are:
    /// * `fov_degrees`  - Horizontal field of view.
    /// * `aspect_ratio` - Ratio between width and height of the view.
    /// * `near`, `far`  - The Z coordinate of the near and far planes.
    pub fn new_perspective(fov_degrees: Scalar, aspect_ratio: Scalar,
                           near: Scalar, far: Scalar) -> Mat4 {
        let fov = (3.1415926538 * fov_degrees) / 180.0;
        let f = 1.0 / (fov * 0.5).tan();

        Mat4::new(
            f / aspect_ratio, 0.0, 0.0, 0.0,
            0.0, f, 0.0, 0.0,
            0.0, 0.0, (far + near) / (near - far), 2.0*far*near / (near - far),
            0.0, 0.0, -1.0, 0.0)
    }

    /// Creates a matrix which rotates points by `angle_radians` around `axis`.
    pub fn new_axis_rotation(axis: &Vec3f, angle_radians: Scalar) -> Mat4 {
        let ca = angle_radians.cos();
        let sa = angle_radians.sin();
        let nca = 1.0 - ca;
        let u = axis;
        Mat4::new(
            ca + u.x*u.x*nca, u.x*u.y*nca - u.z*sa, u.x*u.z*nca + u.y*sa, 0.0,
            u.y*u.x*nca + u.z*sa, ca + u.y*u.y*nca, u.y*u.z*nca - u.x*sa, 0.0,
            u.z*u.x*nca - u.y*sa, u.z*u.y*nca + u.x*sa, ca + u.z*u.z*nca, 0.0,
            0.0, 0.0, 0.0, 1.0
        )
    }

    /// Creates a rotation matrix from the three _Euler angles_.
    pub fn new_euler_rotation(yaw: f32, pitch: f32, roll: f32) -> Mat4 {
        let (ca, sa) = (yaw.cos(), yaw.sin());
        let (cb, sb) = (roll.cos(), roll.sin());
        let (cc, sc) = (pitch.cos(), pitch.sin());

        Mat4::new(
             ca*cb, sa*sc - ca*sb*cc,  ca*sb*sc + sa*cc, 0.0,
                sb,          cb * cc,          -cb * sc, 0.0,
            -sa*cb, sa*sb*cc + ca*sc, -sa*sb*sc + ca*cc, 0.0,
               0.0,              0.0,               0.0, 1.0,
        )
    }

    /// Creates a translation matrix which maps points `p` to `p + by`.
    pub fn new_translation(by: Vec3f) -> Mat4 {
        Mat4::new(1.0, 0.0, 0.0, by.x,
                  0.0, 1.0, 0.0, by.y,
                  0.0, 0.0, 1.0, by.z,
                  0.0, 0.0, 0.0,  1.0)
    }

    /// Returns the transpose of the matrix (columns swapped with rows).
    pub fn transposed(&self) -> Mat4 {
        let m = &self.data;
        // m is in column-major order, so calling with new in row-order will
        // transpose it.
        Mat4::new(m[0], m[1], m[2], m[3],
                  m[4], m[5], m[6], m[7],
                  m[8], m[9], m[10], m[11],
                  m[12], m[13], m[14], m[15])
    }

    pub fn get(&self, row: uint, column: uint) -> Scalar {
        self.data[column * 4 + row]
    }

    pub fn as_scalar_ptr(&self) -> *const Scalar {
        self.data.as_ptr()
    }
}

impl fmt::Show for Mat4 {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter,
               "[{:10.3e} {:10.3e} {:10.3e} {:10.3e};\n \
                 {:10.3e} {:10.3e} {:10.3e} {:10.3e};\n \
                 {:10.3e} {:10.3e} {:10.3e} {:10.3e};\n \
                 {:10.3e} {:10.3e} {:10.3e} {:10.3e}]",
               self.get(0, 0), self.get(0, 1), self.get(0, 2), self.get(0, 3),
               self.get(1, 0), self.get(1, 1), self.get(1, 2), self.get(1, 3),
               self.get(2, 0), self.get(2, 1), self.get(2, 2), self.get(2, 3),
               self.get(3, 0), self.get(3, 1), self.get(3, 2), self.get(3, 3))
    }
}

impl Mul<Mat4, Mat4> for Mat4 {
    fn mul(&self, rhs: &Mat4) -> Mat4 {
        let l = &self.data;
        let r = &rhs.data;
        Mat4 { data:[
            l[ 0]*r[ 0] + l[ 4]*r[ 1] + l[ 8]*r[ 2] + l[12]*r[ 3],
            l[ 1]*r[ 0] + l[ 5]*r[ 1] + l[ 9]*r[ 2] + l[13]*r[ 3],
            l[ 2]*r[ 0] + l[ 6]*r[ 1] + l[10]*r[ 2] + l[14]*r[ 3],
            l[ 3]*r[ 0] + l[ 7]*r[ 1] + l[11]*r[ 2] + l[15]*r[ 3],

            l[ 0]*r[ 4] + l[ 4]*r[ 5] + l[ 8]*r[ 6] + l[12]*r[ 7],
            l[ 1]*r[ 4] + l[ 5]*r[ 5] + l[ 9]*r[ 6] + l[13]*r[ 7],
            l[ 2]*r[ 4] + l[ 6]*r[ 5] + l[10]*r[ 6] + l[14]*r[ 7],
            l[ 3]*r[ 4] + l[ 7]*r[ 5] + l[11]*r[ 6] + l[15]*r[ 7],

            l[ 0]*r[ 8] + l[ 4]*r[ 9] + l[ 8]*r[10] + l[12]*r[11],
            l[ 1]*r[ 8] + l[ 5]*r[ 9] + l[ 9]*r[10] + l[13]*r[11],
            l[ 2]*r[ 8] + l[ 6]*r[ 9] + l[10]*r[10] + l[14]*r[11],
            l[ 3]*r[ 8] + l[ 7]*r[ 9] + l[11]*r[10] + l[15]*r[11],

            l[ 0]*r[12] + l[ 4]*r[13] + l[ 8]*r[14] + l[12]*r[15],
            l[ 1]*r[12] + l[ 5]*r[13] + l[ 9]*r[14] + l[13]*r[15],
            l[ 2]*r[12] + l[ 6]*r[13] + l[10]*r[14] + l[14]*r[15],
            l[ 3]*r[12] + l[ 7]*r[13] + l[11]*r[14] + l[15]*r[15],
        ]}
    }
}

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

    #[test]
    fn test_mul() {
        let a = Mat4::new(4.0,    8.0,    1.0,    6.0,
                          9.0,    4.0,    2.0,    1.0,
                          4.0,    3.0,    9.0,    3.0,
                          2.0,    4.0,    9.0,    4.0);

        let b = Mat4::new(8.0,    6.0,    5.0,    7.0,
                          1.0,    7.0,    3.0,    2.0,
                          1.0,    6.0,    7.0,    4.0,
                          2.0,    5.0,    2.0,    6.0);

        let exp_ab = Mat4::new(53.0,   116.0,    63.0,    84.0,
                               80.0,    99.0,    73.0,    85.0,
                               50.0,   114.0,    98.0,    88.0,
                               37.0,   114.0,    93.0,    82.0);
        let ab = a * b;

        for i in range(0, 16) {
            if exp_ab.data[i] != ab.data[i] {
                fail!("They differ at {} ({}):\nexpected=\n{}\n\nactual=\n{}",
                      i, (a.data[i] - b.data[i]).abs(), exp_ab, ab);
            }
        }

    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use edge;

pub type Index = usize;

pub struct Node {
    pub identifier: String,
    pub attributes: Option<HashMap<String, String>>,
    pub first_outgoing_edge: Option<edge::Index>,
}
<commit_msg>Add basic total equality matcher for Node<commit_after>use std::collections::HashMap;
use edge;

pub type Index = usize;

pub struct Node {
    pub identifier: String,
    pub attributes: Option<HashMap<String, String>>,
    pub first_outgoing_edge: Option<edge::Index>,
}

impl Node {
    pub fn equal(&self, node: Node) -> bool {
        self.identifier == node.identifier &&
            self.attributes == node.attributes
    }
}

#[cfg(test)]
mod tests {
    use std::collections::HashMap;
    use super::*;
    #[test]
    fn node_equality() {
        let mut attributes = HashMap::new();
        attributes.insert("key".to_string(), "value".to_string());
        let mut attributes2 = HashMap::new();
        attributes.insert("core".to_string(), "worth".to_string());

        let node0 = Node {
            identifier: "nodeid".to_string(),
            attributes: Some(attributes.clone()),
            first_outgoing_edge: None,
        };
        let node1 = Node {
            identifier: "nodeid".to_string(),
            attributes: Some(attributes.clone()),
            first_outgoing_edge: None,
        };
        let node2 = Node {
            identifier: "nodeid".to_string(),
            attributes: Some(attributes2.clone()),
            first_outgoing_edge: None,
        };
        assert!(node0.equal(node1));
        assert_eq!(node0.equal(node2), false);
    }
}
<|endoftext|>


<commit_before>#![crate_id = "rboy"]
#![license = "MIT"]

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

extern crate native;
extern crate getopts;
extern crate time;
extern crate sdl;
#[cfg(test)]
extern crate test;

use cpu::CPU;
use std::sync::{Arc,RWLock};
use std::comm::{DuplexStream,Disconnected,Empty};
use std::task::TaskBuilder;
use native::NativeTaskBuilder;

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;
mod gbmode;
mod util;

static SCALE: uint = 2;
static EXITCODE_INCORRECTOPTIONS: int = 1;
static EXITCODE_CPULOADFAILS: int = 2;

#[cfg(not(test))]
#[start]
fn start(argc: int, argv: **u8) -> int { native::start(argc, argv, main) }

fn main() {
	let args = std::os::args();
	let opts = [ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f); return }
	};

	let filename = if !matches.free.is_empty() {
		matches.free.get(0).clone()
	} else {
		println!("{}", getopts::usage(args.get(0).clone().append(" <filename>").as_slice(), opts));
		std::os::set_exit_status(EXITCODE_INCORRECTOPTIONS);
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE as int, 144*SCALE as int, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdlstream, cpustream) = std::comm::duplex();
	let rawscreen = [0x00u8,.. 160*144*3];
	let arc = Arc::new(RWLock::new(rawscreen));
	let arc2 = arc.clone();

	TaskBuilder::new().native().spawn(proc() cpuloop(&cpustream, arc2, filename.as_slice(), &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdlstream.try_recv() {
			Err(Disconnected) => { break 'main },
			Ok(_) => recalculate_screen(&screen, &arc),
			Err(Empty) => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdlstream.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdlstream.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &Arc<RWLock<[u8,.. 160*144*3]>>) {
	let data = arc.read();
	for y in range(0u, 144) {
		for x in range(0u, 160) {
			screen.fill_rect(
				Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
				sdl::video::RGB(data[y*160*3 + x*3 + 0],
				                data[y*160*3 + x*3 + 1],
				                data[y*160*3 + x*3 + 2])
			);
		}
	}
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(channel: &DuplexStream<uint, GBEvent>, arc: Arc<RWLock<[u8,.. 160*144*3]>>, filename: &str, matches: &getopts::Matches) {
	let opt_c = match matches.opt_present("classic") {
		true => CPU::new(filename),
		false => CPU::new_cgb(filename),
	};
	let mut c = match opt_c
	{
		Some(cpu) => { cpu },
		None => { error!("Could not get a valid gameboy"); std::os::set_exit_status(EXITCODE_CPULOADFAILS); return; },
	};
	c.mmu.serial.tostdout = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);
	let mut limit_speed = true;

	let waitticks = (4194.304 * 4.0) as uint;

	let mut ticks = 0;
	'cpuloop: loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				let mut data = arc.write();
				for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				if channel.send_opt(0).is_err() { break 'cpuloop };
			}
		}
		ticks -= waitticks;
		if limit_speed { periodic.recv(); }

		match channel.try_recv() {
			Ok(event) => match event {
				KeyUp(key) => c.mmu.keypad.keyup(key),
				KeyDown(key) => c.mmu.keypad.keydown(key),
				SpeedUp => limit_speed = false,
				SlowDown => limit_speed = true,
			},
			Err(Empty) => {},
			Err(Disconnected) => { break },
		};
	}
}
<commit_msg>Remove fn start, native is on by default<commit_after>#![crate_id = "rboy"]
#![license = "MIT"]

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

extern crate native;
extern crate getopts;
extern crate time;
extern crate sdl;
#[cfg(test)]
extern crate test;

use cpu::CPU;
use std::sync::{Arc,RWLock};
use std::comm::{DuplexStream,Disconnected,Empty};
use std::task::TaskBuilder;
use native::NativeTaskBuilder;

mod register;
mod mbc;
mod mmu;
mod cpu;
mod serial;
mod timer;
mod keypad;
mod gpu;
mod sound;
mod gbmode;
mod util;

static SCALE: uint = 2;
static EXITCODE_INCORRECTOPTIONS: int = 1;
static EXITCODE_CPULOADFAILS: int = 2;

fn main() {
	let args = std::os::args();
	let opts = [ getopts::optflag("s", "serial", "Output serial to stdout"), getopts::optflag("c", "classic", "Force Classic mode") ];
	let matches = match getopts::getopts(args.tail(), opts) {
		Ok(m) => { m }
		Err(f) => { println!("{}", f); return }
	};

	let filename = if !matches.free.is_empty() {
		matches.free.get(0).clone()
	} else {
		println!("{}", getopts::usage(args.get(0).clone().append(" <filename>").as_slice(), opts));
		std::os::set_exit_status(EXITCODE_INCORRECTOPTIONS);
		return;
	};

	sdl::init([sdl::InitVideo]);
	sdl::wm::set_caption("RBoy - A gameboy in Rust", "rboy");
	let screen = match sdl::video::set_video_mode(160*SCALE as int, 144*SCALE as int, 32, [sdl::video::HWSurface], [sdl::video::DoubleBuf]) {
		Ok(screen) => screen,
		Err(err) => fail!("failed to open screen: {}", err),
	};

	let (sdlstream, cpustream) = std::comm::duplex();
	let rawscreen = [0x00u8,.. 160*144*3];
	let arc = Arc::new(RWLock::new(rawscreen));
	let arc2 = arc.clone();

	TaskBuilder::new().native().spawn(proc() cpuloop(&cpustream, arc2, filename.as_slice(), &matches));

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);

	'main : loop {
		periodic.recv();
		match sdlstream.try_recv() {
			Err(Disconnected) => { break 'main },
			Ok(_) => recalculate_screen(&screen, &arc),
			Err(Empty) => {},
		}
		'event : loop {
			match sdl::event::poll_event() {
				sdl::event::QuitEvent => break 'main,
				sdl::event::NoEvent => break 'event,
				sdl::event::KeyEvent(sdl::event::EscapeKey, _, _, _)
					=> break 'main,
				sdl::event::KeyEvent(sdl::event::LShiftKey, true, _, _)
					=> sdlstream.send(SpeedUp),
				sdl::event::KeyEvent(sdl::event::LShiftKey, false, _, _)
					=> sdlstream.send(SlowDown),
				sdl::event::KeyEvent(sdlkey, true, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyDown(key)),
						None => {},
					}
				},
				sdl::event::KeyEvent(sdlkey, false, _, _) => {
					match sdl_to_keypad(sdlkey) {
						Some(key) => sdlstream.send(KeyUp(key)),
						None => {},
					}
				},
				_ => {}
			}
		}
	}
}

fn sdl_to_keypad(key: sdl::event::Key) -> Option<keypad::KeypadKey> {
	match key {
		sdl::event::ZKey => Some(keypad::A),
		sdl::event::XKey => Some(keypad::B),
		sdl::event::UpKey => Some(keypad::Up),
		sdl::event::DownKey => Some(keypad::Down),
		sdl::event::LeftKey => Some(keypad::Left),
		sdl::event::RightKey => Some(keypad::Right),
		sdl::event::SpaceKey => Some(keypad::Select),
		sdl::event::ReturnKey => Some(keypad::Start),
		_ => None,
	}
}

fn recalculate_screen(screen: &sdl::video::Surface, arc: &Arc<RWLock<[u8,.. 160*144*3]>>) {
	let data = arc.read();
	for y in range(0u, 144) {
		for x in range(0u, 160) {
			screen.fill_rect(
				Some(sdl::Rect { x: (x*SCALE) as i16, y: (y*SCALE) as i16, w: SCALE as u16, h: SCALE as u16 }),
				sdl::video::RGB(data[y*160*3 + x*3 + 0],
				                data[y*160*3 + x*3 + 1],
				                data[y*160*3 + x*3 + 2])
			);
		}
	}
	screen.flip();
}

enum GBEvent {
	KeyUp(keypad::KeypadKey),
	KeyDown(keypad::KeypadKey),
	SpeedUp,
	SlowDown,
}

fn cpuloop(channel: &DuplexStream<uint, GBEvent>, arc: Arc<RWLock<[u8,.. 160*144*3]>>, filename: &str, matches: &getopts::Matches) {
	let opt_c = match matches.opt_present("classic") {
		true => CPU::new(filename),
		false => CPU::new_cgb(filename),
	};
	let mut c = match opt_c
	{
		Some(cpu) => { cpu },
		None => { error!("Could not get a valid gameboy"); std::os::set_exit_status(EXITCODE_CPULOADFAILS); return; },
	};
	c.mmu.serial.tostdout = matches.opt_present("serial");

	let mut timer = std::io::timer::Timer::new().unwrap();
	let periodic = timer.periodic(8);
	let mut limit_speed = true;

	let waitticks = (4194.304 * 4.0) as uint;

	let mut ticks = 0;
	'cpuloop: loop {
		while ticks < waitticks {
			ticks += c.cycle();
			if c.mmu.gpu.updated {
				c.mmu.gpu.updated = false;
				let mut data = arc.write();
				for i in range(0, data.len()) { data[i] = c.mmu.gpu.data[i]; }
				if channel.send_opt(0).is_err() { break 'cpuloop };
			}
		}
		ticks -= waitticks;
		if limit_speed { periodic.recv(); }

		match channel.try_recv() {
			Ok(event) => match event {
				KeyUp(key) => c.mmu.keypad.keyup(key),
				KeyDown(key) => c.mmu.keypad.keydown(key),
				SpeedUp => limit_speed = false,
				SlowDown => limit_speed = true,
			},
			Err(Empty) => {},
			Err(Disconnected) => { break },
		};
	}
}
<|endoftext|>


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

use super::JSContext;
use super::JSManaged;
use super::JSTraceable;

use js::jsapi::JSTracer;

use std::cell::UnsafeCell;
use std::marker::PhantomData;
use std::mem;
use std::ops::Deref;
use std::ops::DerefMut;
use std::os::raw::c_void;
use std::ptr;

/// A stack allocated root containing data of type `T` with lifetime `'a`.
pub struct JSRoot<'a, T: 'a> {
    value: Option<T>,
    pin: JSUntypedPinnedRoot,
    marker: PhantomData<&'a ()>, // NOTE: this is variant in `'a`.
}

impl<'a, T:'a> JSRoot<'a, T> {
    pub fn new<S>(_cx: &mut JSContext<S>) -> JSRoot<'a, T> {
        JSRoot {
            value: None,
            pin: JSUntypedPinnedRoot {
                value: unsafe { mem::zeroed() },
                next: ptr::null_mut(),
                prev: ptr::null_mut(),
            },
            marker: PhantomData,
        }
    }
}

/// A stack allocated root that has been pinned, so the backing store can't move.
pub struct JSPinnedRoot<'a, T: 'a> (&'a mut JSRoot<'a, T>);

/// A doubly linked list with all the pinned roots.
#[derive(Eq, PartialEq)]
pub struct JSPinnedRoots(*mut JSUntypedPinnedRoot);

impl JSPinnedRoots {
    unsafe fn insert(&mut self, root: &mut JSUntypedPinnedRoot) {
        debug!("Adding root {:p}.", root);
        root.next = self.0;
        root.prev = ptr::null_mut();
        if let Some(next) = root.next.as_mut() {
            next.prev = root;
        }
        self.0 = root;
    }

    unsafe fn remove(&mut self, root: &mut JSUntypedPinnedRoot) {
        if let Some(next) = root.next.as_mut() {
            debug!("Removing root.next.prev for {:p}.", root);
            next.prev = root.prev;
        }
        if let Some(prev) = root.prev.as_mut() {
            debug!("Removing root.prev.next for {:p}.", root);
            prev.next = root.next;
        } else if self.0 == root {
            debug!("Removing root {:p} from rootset.", root);
            self.0 = root.next;
        }
        root.value = mem::zeroed();
        root.next = ptr::null_mut();
        root.prev = ptr::null_mut();
    }
}

/// The thread-local list of all roots
thread_local! { static ROOTS: UnsafeCell<JSPinnedRoots> = UnsafeCell::new(JSPinnedRoots(ptr::null_mut())); }

unsafe fn thread_local_roots() -> *mut JSPinnedRoots {
    ROOTS.with(UnsafeCell::get)
}

pub unsafe extern "C" fn trace_thread_local_roots(trc: *mut JSTracer, _: *mut c_void) {
    debug!("Tracing roots.");

    if let Some(roots) = thread_local_roots().as_mut() {
        let mut curr = roots.0;
        while let Some(root) = curr.as_ref() {
            debug!("Tracing root {:p}.", root);
            (&*root.value).trace(trc);
            curr = root.next;
        }
    }

    debug!("Done tracing roots.");
}

/// A stack allocated root that has been pinned, but we don't have a type for the contents
struct JSUntypedPinnedRoot {
    value: *mut JSTraceable,
    next: *mut JSUntypedPinnedRoot,
    prev: *mut JSUntypedPinnedRoot,
}

impl Drop for JSUntypedPinnedRoot {
    fn drop(&mut self) {
        unsafe { (&mut *thread_local_roots()).remove(self); }
    }
}

/// Data which can be rooted.

pub unsafe trait JSLifetime<'a> {
    type Aged;

    unsafe fn change_lifetime(self) -> Self::Aged where Self: Sized {
        let result = mem::transmute_copy(&self);
        mem::forget(self);
        result
    }

    fn contract_lifetime(self) -> Self::Aged where Self: 'a + Sized {
        unsafe { self.change_lifetime() }
    }

    fn in_root(self, root: &'a mut JSRoot<'a, Self::Aged>) -> Self::Aged where
        Self: Sized,
        Self::Aged: Copy + JSTraceable,
    {
        root.set(self)
    }
}

unsafe impl<'a> JSLifetime<'a> for String { type Aged = String; }
unsafe impl<'a> JSLifetime<'a> for usize { type Aged = usize; }
unsafe impl<'a, T> JSLifetime<'a> for Option<T> where T: JSLifetime<'a> { type Aged = Option<T::Aged>; }
unsafe impl<'a, T> JSLifetime<'a> for Vec<T> where T: JSLifetime<'a> { type Aged = Vec<T::Aged>; }

unsafe impl<'a, 'b, C, T> JSLifetime<'a> for JSManaged<'b, C, T> where
    T: JSLifetime<'a>,
{
    type Aged = JSManaged<'a, C, T::Aged>;
}

impl<'a, T> JSRoot<'a, T> {
    // This uses Sgeo's trick to stop the JSRoot being forgotten by `mem::forget`.
    // The pin takes a `&'a mut JSRoot<'a, T>`, so borrows the root for the
    // duration of `'a`, so the type is no longer valid after the pin is dropped.
    pub fn pin<U>(&'a mut self, value: U) -> &'a T where
        T: JSTraceable,
        U: JSLifetime<'a, Aged=T>,
    {
        let roots = unsafe { &mut *thread_local_roots() };
        self.value = Some(unsafe { value.change_lifetime() });
        self.pin.value = self.value.as_mut_ptr();
        unsafe { roots.insert(&mut self.pin) };
        self.value.as_ref().unwrap()
    }

    pub fn set<U>(&'a mut self, value: U) -> T where
        T: Copy + JSTraceable,
        U: JSLifetime<'a, Aged=T>,
    {
        *self.pin(value)
    }

    pub unsafe fn unpin(&mut self) {
        let roots = &mut *thread_local_roots();
        roots.remove(&mut self.pin);
    }
}

impl<'a, T> Deref for JSPinnedRoot<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        self.0.value.as_ref().unwrap()
    }
}

impl<'a, T> DerefMut for JSPinnedRoot<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        self.0.value.as_mut().unwrap()
    }
}

unsafe impl<#[may_dangle] 'a, #[may_dangle] T> Drop for JSRoot<'a, T> {
    fn drop(&mut self) {
        unsafe { self.unpin() }
    }
}
<commit_msg>Use a dummy value rather than zero to initialize JSRoots.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use super::JSContext;
use super::JSManaged;
use super::JSTraceable;

use js::jsapi::JSTracer;

use std::cell::UnsafeCell;
use std::marker::PhantomData;
use std::mem;
use std::ops::Deref;
use std::ops::DerefMut;
use std::os::raw::c_void;
use std::ptr;

/// A stack allocated root containing data of type `T` with lifetime `'a`.
pub struct JSRoot<'a, T: 'a> {
    value: Option<T>,
    pin: JSUntypedPinnedRoot,
    marker: PhantomData<&'a ()>, // NOTE: this is variant in `'a`.
}

const DUMMY: *mut JSTraceable = &() as *const JSTraceable as *mut JSTraceable;

impl<'a, T:'a> JSRoot<'a, T> {
    pub fn new<S>(_cx: &mut JSContext<S>) -> JSRoot<'a, T> {
        JSRoot {
            value: None,
            pin: JSUntypedPinnedRoot {
                value: DUMMY,
                next: ptr::null_mut(),
                prev: ptr::null_mut(),
            },
            marker: PhantomData,
        }
    }
}

/// A stack allocated root that has been pinned, so the backing store can't move.
pub struct JSPinnedRoot<'a, T: 'a> (&'a mut JSRoot<'a, T>);

/// A doubly linked list with all the pinned roots.
#[derive(Eq, PartialEq)]
pub struct JSPinnedRoots(*mut JSUntypedPinnedRoot);

impl JSPinnedRoots {
    unsafe fn insert(&mut self, root: &mut JSUntypedPinnedRoot) {
        debug!("Adding root {:p}.", root);
        root.next = self.0;
        root.prev = ptr::null_mut();
        if let Some(next) = root.next.as_mut() {
            next.prev = root;
        }
        self.0 = root;
    }

    unsafe fn remove(&mut self, root: &mut JSUntypedPinnedRoot) {
        if let Some(next) = root.next.as_mut() {
            debug!("Removing root.next.prev for {:p}.", root);
            next.prev = root.prev;
        }
        if let Some(prev) = root.prev.as_mut() {
            debug!("Removing root.prev.next for {:p}.", root);
            prev.next = root.next;
        } else if self.0 == root {
            debug!("Removing root {:p} from rootset.", root);
            self.0 = root.next;
        }
        root.value = DUMMY;
        root.next = ptr::null_mut();
        root.prev = ptr::null_mut();
    }
}

/// The thread-local list of all roots
thread_local! { static ROOTS: UnsafeCell<JSPinnedRoots> = UnsafeCell::new(JSPinnedRoots(ptr::null_mut())); }

unsafe fn thread_local_roots() -> *mut JSPinnedRoots {
    ROOTS.with(UnsafeCell::get)
}

pub unsafe extern "C" fn trace_thread_local_roots(trc: *mut JSTracer, _: *mut c_void) {
    debug!("Tracing roots.");

    if let Some(roots) = thread_local_roots().as_mut() {
        let mut curr = roots.0;
        while let Some(root) = curr.as_ref() {
            debug!("Tracing root {:p}.", root);
            (&*root.value).trace(trc);
            curr = root.next;
        }
    }

    debug!("Done tracing roots.");
}

/// A stack allocated root that has been pinned, but we don't have a type for the contents
struct JSUntypedPinnedRoot {
    value: *mut JSTraceable,
    next: *mut JSUntypedPinnedRoot,
    prev: *mut JSUntypedPinnedRoot,
}

impl Drop for JSUntypedPinnedRoot {
    fn drop(&mut self) {
        unsafe { (&mut *thread_local_roots()).remove(self); }
    }
}

/// Data which can be rooted.

pub unsafe trait JSLifetime<'a> {
    type Aged;

    unsafe fn change_lifetime(self) -> Self::Aged where Self: Sized {
        let result = mem::transmute_copy(&self);
        mem::forget(self);
        result
    }

    fn contract_lifetime(self) -> Self::Aged where Self: 'a + Sized {
        unsafe { self.change_lifetime() }
    }

    fn in_root(self, root: &'a mut JSRoot<'a, Self::Aged>) -> Self::Aged where
        Self: Sized,
        Self::Aged: Copy + JSTraceable,
    {
        root.set(self)
    }
}

unsafe impl<'a> JSLifetime<'a> for String { type Aged = String; }
unsafe impl<'a> JSLifetime<'a> for usize { type Aged = usize; }
unsafe impl<'a, T> JSLifetime<'a> for Option<T> where T: JSLifetime<'a> { type Aged = Option<T::Aged>; }
unsafe impl<'a, T> JSLifetime<'a> for Vec<T> where T: JSLifetime<'a> { type Aged = Vec<T::Aged>; }

unsafe impl<'a, 'b, C, T> JSLifetime<'a> for JSManaged<'b, C, T> where
    T: JSLifetime<'a>,
{
    type Aged = JSManaged<'a, C, T::Aged>;
}

impl<'a, T> JSRoot<'a, T> {
    // This uses Sgeo's trick to stop the JSRoot being forgotten by `mem::forget`.
    // The pin takes a `&'a mut JSRoot<'a, T>`, so borrows the root for the
    // duration of `'a`, so the type is no longer valid after the pin is dropped.
    pub fn pin<U>(&'a mut self, value: U) -> &'a T where
        T: JSTraceable,
        U: JSLifetime<'a, Aged=T>,
    {
        let roots = unsafe { &mut *thread_local_roots() };
        self.value = Some(unsafe { value.change_lifetime() });
        self.pin.value = self.value.as_mut_ptr();
        unsafe { roots.insert(&mut self.pin) };
        self.value.as_ref().unwrap()
    }

    pub fn set<U>(&'a mut self, value: U) -> T where
        T: Copy + JSTraceable,
        U: JSLifetime<'a, Aged=T>,
    {
        *self.pin(value)
    }

    pub unsafe fn unpin(&mut self) {
        let roots = &mut *thread_local_roots();
        roots.remove(&mut self.pin);
    }
}

impl<'a, T> Deref for JSPinnedRoot<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        self.0.value.as_ref().unwrap()
    }
}

impl<'a, T> DerefMut for JSPinnedRoot<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        self.0.value.as_mut().unwrap()
    }
}

unsafe impl<#[may_dangle] 'a, #[may_dangle] T> Drop for JSRoot<'a, T> {
    fn drop(&mut self) {
        unsafe { self.unpin() }
    }
}
<|endoftext|>


<commit_before>#![ allow (unused_parens) ]

use std::fs;
use std::fs::DirEntry;
use std::io;
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
use std::rc::Rc;

use output::Output;

use arguments::*;
use misc::*;
use storage::*;

fn scan_directory_internal (
	arguments: & Arguments,
	output: & mut Output,
	directory: Rc <PathBuf>,
	root_path: Rc <PathBuf>,
	device_id: u64,
	in_iterator: & mut FileDatabaseIntoIterator,
	out_database: & mut FileDatabase,
	progress: & mut u64,
) -> Result <(), String> {

	let directory =
		directory.as_ref ();

	let entry_results: Vec <io::Result <DirEntry>> =
		try! (

		io_result (
			fs::read_dir (
				directory),
		).map_err (
			|error|

			format! (
				"Error reading directory: {}: {}",
				directory.to_string_lossy (),
				error)

		)

	).collect ();

	let mut entries: Vec <DirEntry> =
		Vec::new ();

	for entry_result in entry_results.into_iter () {

		entries.push (
			try! (

			io_result (
				entry_result,
			).map_err (
				|error|

				format! (
					"Error reading entry: {}",
					error)

			)

		));

	}

	entries.sort_by_key (
		|entry|

		entry.file_name ()

	);

	let mut entry_iterator =
		entries.into_iter ().peekable ();

	loop {

		{

			let entry_next =
				entry_iterator.peek ();

			if entry_next.is_none () {
				break;
			}

		}

		let entry =
			entry_iterator.next ().unwrap ();

		let entry_path =
			Rc::new (
				entry.path ());

		loop {

			let exists = {

				let in_next_option =
					in_iterator.peek ();

				if in_next_option.is_none () {
					break;
				}

				let in_next =
					in_next_option.unwrap ();

				let in_next_path =
					in_next.path.clone ();

				if in_next_path >= entry_path {
					break;
				}

				in_next_path.exists ()

			};

			if exists {

				out_database.insert_direct (
					in_iterator.next ().unwrap ());

			} else {

				in_iterator.next ();

			}

		}

		let metadata =
			try! (

			fs::symlink_metadata (
				entry.path (),
			).map_err (
				|error|

				format! (
					"Error reading metadata for: {}: {}",
					entry.path ().to_string_lossy (),
					error)

			)

		);

		let file_type =
			metadata.file_type ();

		if (

			file_type.is_symlink ()

			|| metadata.dev () != device_id

		) {

			// ignore

		} else if file_type.is_dir () {

			try! (
				scan_directory_internal (
					arguments,
					output,
					entry_path,
					root_path.clone (),
					device_id,
					in_iterator,
					out_database,
					progress));

		} else if file_type.is_file () {

			let exists = {

				let in_next_option =
					in_iterator.peek ();

				if in_next_option.is_some () {

					let in_next =
						in_next_option.unwrap ();

					let in_next_path =
						in_next.path.clone ();

					in_next_path == entry_path

				} else {

					false

				}

			};

			if exists {

				let existing_file_data =
					in_iterator.next ().unwrap ();

				let changed = (

					metadata.len () !=
						existing_file_data.size

				||

					metadata.mtime () !=
						existing_file_data.mtime

				);

				if changed {

					out_database.insert_update_metadata (
						& existing_file_data,
						root_path.clone (),
						& metadata,
					);

				} else {

					out_database.insert_direct (
						existing_file_data)

				}

			} else {

				out_database.insert_new (
					Rc::new (
						entry.path ()),
					root_path.clone (),
					& metadata,
				);

			}

		};

		if * progress % 0x1000 == 0 {

			output.status (
				& format! (
					"Scanning filesystem: {}",
					entry.path ().to_string_lossy ()));

		}

		* progress += 1;

	}

	Ok (())

}

pub fn scan_directories (
	arguments: & Arguments,
	output: & mut Output,
	previous_database: FileDatabase,
) -> Result <FileDatabase, String> {

	let mut new_database: FileDatabase =
		FileDatabase::new ();

	let mut progress: u64 = 0;

	let previous_database_iterator =
		previous_database.into_iter ();

	let mut previous_database_iterator =
		previous_database_iterator.peekable ();

	for root_path in & arguments.root_paths {

		let root_path =
			new_database.get_path (
				root_path.clone ());

		let metadata =
			try! (

			fs::symlink_metadata (
				root_path.as_ref (),
			).map_err (
				|error|

				format! (
					"Error reading metadata for: {}: {}",
					root_path.to_string_lossy (),
					error)

			)

		);

		loop {

			{

				let existing_file_data_option =
					previous_database_iterator.peek ();

				if existing_file_data_option.is_none () {
					break;
				}

				let existing_file_data =
					existing_file_data_option.unwrap ();

				let existing_file_path =
					& existing_file_data.path;

				if * existing_file_path >= root_path {
					break;
				}

			}

			new_database.insert_direct (
				previous_database_iterator.next ().unwrap ());

		}

		try! (
			scan_directory_internal (
				arguments,
				output,
				root_path.clone (),
				root_path.clone (),
				metadata.dev (),
				& mut previous_database_iterator,
				& mut new_database,
				& mut progress));

	}

	for existing_file_data_ref in previous_database_iterator {

		new_database.insert_direct (
			existing_file_data_ref.clone ());

	}

	output.clear_status ();

	output.message (
		& format! (
			"Scanned {} files",
			progress));

	output.message (
		& format! (
			"Total {} files in database",
			new_database.len ()));

	Ok (new_database)

}

// ex: noet ts=4 filetype=rust
<commit_msg>refactor scanner, handle nested roots properly<commit_after>#![ allow (unused_parens) ]

use std::collections::HashSet;
use std::fs;
use std::fs::DirEntry;
use std::io;
use std::os::unix::fs::MetadataExt;
use std::rc::Rc;

use output::Output;

use arguments::*;
use misc::*;
use storage::*;
use types::*;

pub fn scan_directories (
	arguments: & Arguments,
	output: & mut Output,
	previous_database: FileDatabase,
) -> Result <FileDatabase, String> {

	let root_paths: HashSet <PathRef> =
		arguments.root_paths.iter ().map (
			|root_path|

			root_path.clone ()

		).collect ();

	let previous_database_iterator =
		previous_database.into_iter ();

	let previous_database_iterator =
		previous_database_iterator.peekable ();

	let new_database: FileDatabase =
		FileDatabase::new ();

	let directory_scanner =
		DirectoryScanner {

		arguments: arguments,
		output: output,

		in_iterator: previous_database_iterator,
		out_database: new_database,

		root_paths_unordered: root_paths,
		root_paths_scanned: HashSet::new (),

		progress: 0,

	};

	directory_scanner.scan_directories ()

}

struct DirectoryScanner <'a> {

	arguments: & 'a Arguments,
	output: & 'a mut Output,

	in_iterator: FileDatabaseIntoIterator,
	out_database: FileDatabase,

	root_paths_unordered: HashSet <PathRef>,
	root_paths_scanned: HashSet <PathRef>,

	progress: u64,

}

impl <'a> DirectoryScanner <'a> {

	pub fn scan_directories (
		mut self,
	) -> Result <FileDatabase, String> {

		for root_path in & self.arguments.root_paths {

			if self.root_paths_scanned.contains (root_path) {
				continue;
			}

			self.output.message (
				& format! (
					"Scanning {}",
					root_path.to_string_lossy ()));

			let root_path =
				self.out_database.get_path (
					root_path.clone ());

			let metadata =
				try! (

				fs::symlink_metadata (
					root_path.as_ref (),
				).map_err (
					|error|

					format! (
						"Error reading metadata for: {}: {}",
						root_path.to_string_lossy (),
						error)

				)

			);

			loop {

				{

					let existing_file_data_option =
						self.in_iterator.peek ();

					if existing_file_data_option.is_none () {
						break;
					}

					let existing_file_data =
						existing_file_data_option.unwrap ();

					let existing_file_path =
						& existing_file_data.path;

					if * existing_file_path >= root_path {
						break;
					}

				}

				self.out_database.insert_direct (
					self.in_iterator.next ().unwrap ());

			}

			try! (
				self.scan_directory_internal (
					root_path.clone (),
					root_path.clone (),
					metadata.dev ()));

		}

		for existing_file_data_ref
		in self.in_iterator {

			self.out_database.insert_direct (
				existing_file_data_ref.clone ());

		}

		self.output.clear_status ();

		self.output.message (
			& format! (
				"Scanned {} files",
				self.progress));

		self.output.message (
			& format! (
				"Total {} files in database",
				self.out_database.len ()));

		Ok (self.out_database)

	}

	fn scan_directory_internal (
		& mut self,
		directory: PathRef,
		root_path: PathRef,
		device_id: u64,
	) -> Result <(), String> {

		if (
			self.root_paths_unordered.contains (
				& directory)
		) {
			self.root_paths_scanned.insert (
				directory.clone ());
		}

		let directory =
			directory.as_ref ();

		let entry_results: Vec <io::Result <DirEntry>> =
			try! (

			io_result (
				fs::read_dir (
					directory),
			).map_err (
				|error|

				format! (
					"Error reading directory: {}: {}",
					directory.to_string_lossy (),
					error)

			)

		).collect ();

		let mut entries: Vec <DirEntry> =
			Vec::new ();

		for entry_result in entry_results.into_iter () {

			entries.push (
				try! (

				io_result (
					entry_result,
				).map_err (
					|error|

					format! (
						"Error reading entry: {}",
						error)

				)

			));

		}

		entries.sort_by_key (
			|entry|

			entry.file_name ()

		);

		let mut entry_iterator =
			entries.into_iter ().peekable ();

		loop {

			{

				let entry_next =
					entry_iterator.peek ();

				if entry_next.is_none () {
					break;
				}

			}

			let entry =
				entry_iterator.next ().unwrap ();

			let entry_path =
				Rc::new (
					entry.path ());

			loop {

				let exists = {

					let in_next_option =
						self.in_iterator.peek ();

					if in_next_option.is_none () {
						break;
					}

					let in_next =
						in_next_option.unwrap ();

					let in_next_path =
						in_next.path.clone ();

					if in_next_path >= entry_path {
						break;
					}

					in_next_path.exists ()

				};

				if exists {

					self.out_database.insert_direct (
						self.in_iterator.next ().unwrap ());

				} else {

					self.in_iterator.next ();

				}

			}

			let metadata =
				try! (

				fs::symlink_metadata (
					entry.path (),
				).map_err (
					|error|

					format! (
						"Error reading metadata for: {}: {}",
						entry.path ().to_string_lossy (),
						error)

				)

			);

			let file_type =
				metadata.file_type ();

			if (

				file_type.is_symlink ()

				|| metadata.dev () != device_id

			) {

				// ignore

			} else if file_type.is_dir () {

				try! (
					self.scan_directory_internal (
						entry_path,
						root_path.clone (),
						device_id));

			} else if file_type.is_file () {

				let exists = {

					let in_next_option =
						self.in_iterator.peek ();

					if in_next_option.is_some () {

						let in_next =
							in_next_option.unwrap ();

						let in_next_path =
							in_next.path.clone ();

						in_next_path == entry_path

					} else {

						false

					}

				};

				if exists {

					let existing_file_data =
						self.in_iterator.next ().unwrap ();

					let changed = (

						metadata.len () !=
							existing_file_data.size

					||

						metadata.mtime () !=
							existing_file_data.mtime

					);

					if changed {

						self.out_database.insert_update_metadata (
							& existing_file_data,
							root_path.clone (),
							& metadata,
						);

					} else {

						self.out_database.insert_direct (
							existing_file_data)

					}

				} else {

					self.out_database.insert_new (
						Rc::new (
							entry.path ()),
						root_path.clone (),
						& metadata,
					);

				}

			};

			if self.progress % 0x1000 == 0 {

				self.output.status (
					& format! (
						"Scanning filesystem: {}",
						entry.path ().to_string_lossy ()));

			}

			self.progress += 1;

		}

		Ok (())

	}

}

// ex: noet ts=4 filetype=rust
<|endoftext|>


<commit_before>#[derive(Debug, Clone, Copy)]
struct Merp(i32);

impl Merp {
    fn default() -> Merp {
        Merp(1)
    }
}

fn main() {
    let x = vec![vec![Merp::default(); 5]; 5];
    println!("{:?}", x);
    println!("{:?}", x[1][1]);
}<commit_msg>Delete test.rs<commit_after><|endoftext|>


<commit_before>fn main() {
    println!("hello tile!");
}
<commit_msg>load gdal memory driver<commit_after>extern crate sync;

#[allow(dead_code)]
mod gdal;

fn main() {
    let memory_driver = gdal::get_driver("MEM").unwrap();

    println!("hello tile! {}", memory_driver.get_short_name());
}
<|endoftext|>


<commit_before>
use std::collections::HashMap;

pub struct User {
	id: u64,
	display_name: String
}

impl User {
	pub fn from(id: &u64, display: &String) -> User {
		User {
			id: id,
			display_name: display
		}
	}

	pub fn id(&self) -> &u64 {
		&self.id
	}

	pub fn display_name(&self) -> &String {
		&self.display_name
	}
}

pub struct UserState {
	badges: HashMap<String, String>,
	color: String,
	display: String,
	emotes: String,
	channel: String,
	subscriber: bool,
	moderator: bool,
	user_type: UserType
}

impl UserState {
	pub fn from_irc(irc: &String) -> UserState {
		// TODO: determine UserState from IRC message
		unimplemented!()
	}
}

pub enum UserType {
	Viewer,
	Moderator,
	GlobalModerator,
	Broadcaster,
	Admin,
	Staff
}
<commit_msg>converted `from` to consuming function instead of borrowing<commit_after>
use std::collections::HashMap;

pub struct User {
	id: u64,
	display_name: String
}

impl User {
	pub fn from(id: u64, display: String) -> User {
		User {
			id: id,
			display_name: display
		}
	}

	pub fn id(&self) -> &u64 {
		&self.id
	}

	pub fn display_name(&self) -> &String {
		&self.display_name
	}
}

pub struct UserState {
	badges: HashMap<String, String>,
	color: String,
	display: String,
	emotes: String,
	channel: String,
	subscriber: bool,
	moderator: bool,
	user_type: UserType
}

impl UserState {
	pub fn from_irc(irc: &String) -> UserState {
		// TODO: determine UserState from IRC message
		unimplemented!()
	}
}

pub enum UserType {
	Viewer,
	Moderator,
	GlobalModerator,
	Broadcaster,
	Admin,
	Staff
}
<|endoftext|>


<commit_before>extern crate time;

/// Return current time in microseconds since the UNIX epoch.
pub fn now_microseconds() -> u32 {
    let t = time::get_time();
    (t.sec * 1_000_000) as u32 + (t.nsec/1000) as u32
}

pub fn ewma<T: ToPrimitive>(samples: Vec<T>, alpha: f64) -> f64 {
    let (prev_sample, prev_avg) = samples.iter().fold((0.0, 0.0), |acc, ref sample| match acc {
        (0.0, 0.0) => (sample.to_f64().unwrap(), sample.to_f64().unwrap()),
        (prev_sample, prev_avg) => (sample.to_f64().unwrap(), alpha * prev_sample + (1.0 - alpha) * prev_avg)
    });
    alpha * prev_sample + (1.0 - alpha) * prev_avg
}

mod test {
    #[test]
    fn test_exponential_smoothed_moving_average() {
        use super::ewma;
        use std::iter::range_inclusive;

        let input = range_inclusive(1u, 10).collect();
        let alpha = 1.0/3.0;
        let expected = [1.0, 4.0/3.0, 17.0/9.0,
        70.0/27.0, 275.0/81.0, 1036.0/243.0, 3773.0/729.0, 13378.0/2187.0,
        46439.0/6561.0, 158488.0/19683.0];
        assert_eq!(ewma(input, alpha), expected[expected.len() - 1]);
    }
}
<commit_msg>Add more EWMA tests.<commit_after>extern crate time;

/// Return current time in microseconds since the UNIX epoch.
pub fn now_microseconds() -> u32 {
    let t = time::get_time();
    (t.sec * 1_000_000) as u32 + (t.nsec/1000) as u32
}

pub fn ewma<T: ToPrimitive>(samples: Vec<T>, alpha: f64) -> f64 {
    let (prev_sample, prev_avg) = samples.iter().fold((0.0, 0.0), |acc, ref sample| match acc {
        (0.0, 0.0) => (sample.to_f64().unwrap(), sample.to_f64().unwrap()),
        (prev_sample, prev_avg) => (sample.to_f64().unwrap(), alpha * prev_sample + (1.0 - alpha) * prev_avg)
    });
    alpha * prev_sample + (1.0 - alpha) * prev_avg
}

mod test {
    #[test]
    fn test_ewma_empty_vector() {
        use super::ewma;

        let empty: Vec<u32> = vec!();
        let alpha = 1.0/3.0;
        assert_eq!(ewma(empty, alpha), 0.0);
    }

    #[test]
    fn test_ewma_one_element() {
        use super::ewma;

        let input = vec!(1u);
        let alpha = 1.0/3.0;
        assert_eq!(ewma(input, alpha), 1.0);
    }

    #[test]
    fn test_exponential_smoothed_moving_average() {
        use super::ewma;
        use std::iter::range_inclusive;

        let input = range_inclusive(1u, 10).collect();
        let alpha = 1.0/3.0;
        let expected = [1.0, 4.0/3.0, 17.0/9.0,
        70.0/27.0, 275.0/81.0, 1036.0/243.0, 3773.0/729.0, 13378.0/2187.0,
        46439.0/6561.0, 158488.0/19683.0];
        assert_eq!(ewma(input, alpha), expected[expected.len() - 1]);
    }
}
<|endoftext|>