<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 euclid::point::TypedPoint2D;
use gecko_bindings::structs::{nsTimingFunction, nsTimingFunction_Type};
use properties::longhands::transition_timing_function::single_value::computed_value::StartEnd;
use properties::longhands::transition_timing_function::single_value::computed_value::T as TransitionTimingFunction;
use std::mem;

impl From<TransitionTimingFunction> for nsTimingFunction {
    fn from(function: TransitionTimingFunction) -> nsTimingFunction {
        let mut tf: nsTimingFunction = unsafe { mem::zeroed() };

        match function {
            TransitionTimingFunction::Steps(steps, StartEnd::Start) => {
                tf.mType = nsTimingFunction_Type::StepStart;
                unsafe {
                    tf.__bindgen_anon_1.__bindgen_anon_1.as_mut().mSteps = steps;
                }
            },
            TransitionTimingFunction::Steps(steps, StartEnd::End) => {
                tf.mType = nsTimingFunction_Type::StepEnd;
                unsafe {
                    tf.__bindgen_anon_1.__bindgen_anon_1.as_mut().mSteps = steps;
                }
            },
            TransitionTimingFunction::CubicBezier(p1, p2) => {
                tf.mType = nsTimingFunction_Type::CubicBezier;
                let ref mut gecko_cubic_bezier =
                    unsafe { tf.__bindgen_anon_1.mFunc.as_mut() };
                gecko_cubic_bezier.mX1 = p1.x;
                gecko_cubic_bezier.mY1 = p1.y;
                gecko_cubic_bezier.mX2 = p2.x;
                gecko_cubic_bezier.mY2 = p2.y;
            },
            // FIXME: we need to add more types once TransitionTimingFunction
            // has more types.
        }
        tf
    }
}

impl From<nsTimingFunction> for TransitionTimingFunction {
    fn from(function: nsTimingFunction) -> TransitionTimingFunction {
        match function.mType {
            nsTimingFunction_Type::StepStart => {
                TransitionTimingFunction::Steps(unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mSteps },
                                                StartEnd::Start)
            },
            nsTimingFunction_Type::StepEnd => {
                TransitionTimingFunction::Steps(unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mSteps },
                                                StartEnd::End)
            },
            // FIXME: As above, we need to fix here.
            nsTimingFunction_Type::Ease |
            nsTimingFunction_Type::Linear |
            nsTimingFunction_Type::EaseIn |
            nsTimingFunction_Type::EaseOut |
            nsTimingFunction_Type::EaseInOut |
            nsTimingFunction_Type::CubicBezier => {
                TransitionTimingFunction::CubicBezier(
                    TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX1 },
                                      unsafe { function.__bindgen_anon_1.mFunc.as_ref().mY1 }),
                    TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX2 },
                                      unsafe { function.__bindgen_anon_1.mFunc.as_ref().mY2 }))
            },
        }
    }
}
<commit_msg>Bug 1328787 - Part 7: Implement From for specified TransitionTimingFunction. r=heycam<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 euclid::point::{Point2D, TypedPoint2D};
use gecko_bindings::structs::{nsTimingFunction, nsTimingFunction_Type};
use properties::longhands::transition_timing_function::single_value::FunctionKeyword;
use properties::longhands::transition_timing_function::single_value::SpecifiedValue as SpecifiedTimingFunction;
use properties::longhands::transition_timing_function::single_value::computed_value::StartEnd;
use properties::longhands::transition_timing_function::single_value::computed_value::T as ComputedTimingFunction;
use std::mem;

impl nsTimingFunction {
    fn set_as_step(&mut self, function_type: nsTimingFunction_Type, steps: u32) {
        debug_assert!(function_type == nsTimingFunction_Type::StepStart ||
                      function_type == nsTimingFunction_Type::StepEnd,
                      "function_type should be step-start or step-end");
        self.mType = function_type;
        unsafe {
            self.__bindgen_anon_1.__bindgen_anon_1.as_mut().mSteps = steps;
        }
    }

    fn set_as_cubic_bezier(&mut self, p1: Point2D<f32>, p2: Point2D<f32>) {
        self.mType = nsTimingFunction_Type::CubicBezier;
        unsafe {
            let ref mut gecko_cubic_bezier =
                unsafe { self.__bindgen_anon_1.mFunc.as_mut() };
            gecko_cubic_bezier.mX1 = p1.x;
            gecko_cubic_bezier.mY1 = p1.y;
            gecko_cubic_bezier.mX2 = p2.x;
            gecko_cubic_bezier.mY2 = p2.y;
        }
    }
}

impl From<ComputedTimingFunction> for nsTimingFunction {
    fn from(function: ComputedTimingFunction) -> nsTimingFunction {
        let mut tf: nsTimingFunction = unsafe { mem::zeroed() };

        match function {
            ComputedTimingFunction::Steps(steps, StartEnd::Start) => {
                tf.set_as_step(nsTimingFunction_Type::StepStart, steps);
            },
            ComputedTimingFunction::Steps(steps, StartEnd::End) => {
                tf.set_as_step(nsTimingFunction_Type::StepEnd, steps);
            },
            ComputedTimingFunction::CubicBezier(p1, p2) => {
                tf.set_as_cubic_bezier(p1, p2);
            },
        }
        tf
    }
}

impl From<SpecifiedTimingFunction> for nsTimingFunction {
    fn from(function: SpecifiedTimingFunction) -> nsTimingFunction {
        let mut tf: nsTimingFunction = unsafe { mem::zeroed() };

        match function {
            SpecifiedTimingFunction::Steps(steps, StartEnd::Start) => {
                tf.set_as_step(nsTimingFunction_Type::StepStart, steps);
            },
            SpecifiedTimingFunction::Steps(steps, StartEnd::End) => {
                tf.set_as_step(nsTimingFunction_Type::StepEnd, steps);
            },
            SpecifiedTimingFunction::CubicBezier(p1, p2) => {
                tf.set_as_cubic_bezier(p1, p2);
            },
            SpecifiedTimingFunction::Keyword(keyword) => {
                match keyword {
                    FunctionKeyword::Ease => tf.mType = nsTimingFunction_Type::Ease,
                    FunctionKeyword::Linear => tf.mType = nsTimingFunction_Type::Linear,
                    FunctionKeyword::EaseIn => tf.mType = nsTimingFunction_Type::EaseIn,
                    FunctionKeyword::EaseOut => tf.mType = nsTimingFunction_Type::EaseOut,
                    FunctionKeyword::EaseInOut => tf.mType = nsTimingFunction_Type::EaseInOut,
                    FunctionKeyword::StepStart => {
                        tf.set_as_step(nsTimingFunction_Type::StepStart, 1);
                    },
                    FunctionKeyword::StepEnd => {
                        tf.set_as_step(nsTimingFunction_Type::StepEnd, 1);
                    },
                }
            },
        }
        tf
    }
}

impl From<nsTimingFunction> for ComputedTimingFunction {
    fn from(function: nsTimingFunction) -> ComputedTimingFunction {
        match function.mType {
            nsTimingFunction_Type::StepStart => {
                ComputedTimingFunction::Steps(unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mSteps },
                                              StartEnd::Start)
            },
            nsTimingFunction_Type::StepEnd => {
                ComputedTimingFunction::Steps(unsafe { function.__bindgen_anon_1.__bindgen_anon_1.as_ref().mSteps },
                                              StartEnd::End)
            },
            nsTimingFunction_Type::Ease |
            nsTimingFunction_Type::Linear |
            nsTimingFunction_Type::EaseIn |
            nsTimingFunction_Type::EaseOut |
            nsTimingFunction_Type::EaseInOut |
            nsTimingFunction_Type::CubicBezier => {
                ComputedTimingFunction::CubicBezier(
                    TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX1 },
                                      unsafe { function.__bindgen_anon_1.mFunc.as_ref().mY1 }),
                    TypedPoint2D::new(unsafe { function.__bindgen_anon_1.mFunc.as_ref().mX2 },
                                      unsafe { function.__bindgen_anon_1.mFunc.as_ref().mY2 }))
            },
        }
    }
}
<|endoftext|>


<commit_before>macro_rules! commands {
    ($($cmd:ident),* $(,)*) => {
        $(
            mod $cmd;
            pub use self::$cmd::*;
        )*
    }
}

macro_rules! command {
    ( $(#[$attr:meta])* ) => {
        #[derive(Command)]
        $(#[$attr])*
        struct _Dummy;
    }
}

/// Construct a ReQL JSON object from a Rust expression
///
/// # Example
///
/// Objects wrapped with expr can then be manipulated by ReQL API functions.
///
/// ```
/// # #[macro_use] extern crate reql;
/// # use reql::commands::*;
/// # use reql::commands::run::Dummy;
/// # use reql::r;
/// # struct Value;
/// # fn main() {
/// expr!(obj!{a: "b"}).merge(obj!{b: arr![1, 2, 3]}).run::<Value>();
/// # }
/// ```
///
/// # Example
///
/// The `expr` macro can also compile an if else statement down to a ReQL branch command.
///
/// ```
/// # #[macro_use] extern crate reql;
/// # use reql::commands::*;
/// # use reql::commands::run::Dummy;
/// # use reql::r;
/// # struct Value;
/// # fn main() {
/// let hero = r.table("marvel").get("Iron Man");
///
/// r.do_(expr!{
///
///     if hero.get_field("victories") > 100 {
///         hero.get_field("name") + " is a superhero"
///     }
///
///     else if hero.get_field("victories") > 10 {
///         hero.get_field("name") + " is a hero"
///     }
///
///     else {
///         hero.get_field("name") + " is very nice"
///     }
///
/// }).run::<String>();
/// # }
/// ```
#[macro_export]
macro_rules! expr {
    () => {};

    (trace if $($cond:tt)* { $($body:tt)* } $(else if $($elif_cond:tt)* { $($elif_body:tt)* })* else { $($el_body:tt)* } $($tail:tt)*) => {{
        expr!( r.branch(args!($($cond)*, $($body)*, $($($elif_cond)*, $($elif_body)*),* $($el_body)*)) $($tail)*)
    }};

    ( trace $left:tt && $right:tt $($tail:tt)* ) => {{
        expr!( expr!($left).and($right) $($tail)*)
    }};

    ( trace $left:tt || $right:tt $($tail:tt)* ) => {{
        expr!( expr!($left).or($right) $($tail)*)
    }};

    ( trace $left:tt == $right:tt $($tail:tt)* ) => {{
        expr!( expr!($left).eq($right) $($tail)*)
    }};

    ( trace $left:tt != $right:tt $($tail:tt)* ) => {{
        expr!( expr!($left).ne($right) $($tail)*)
    }};

    ( trace $left:tt > $right:tt $($tail:tt)* ) => {{
        expr!( expr!($left).gt($right) $($tail)*)
    }};

    ( trace $left:tt >= $right:tt $($tail:tt)* ) => {{
        expr!( expr!($left).ge($right) $($tail)*)
    }};

    ( trace $left:tt < $right:tt $($tail:tt)* ) => {{
        expr!( expr!($left).lt($right) $($tail)*)
    }};

    ( trace $left:tt <= $right:tt $($tail:tt)* ) => {{
        expr!( expr!($left).le($right) $($tail)*)
    }};

    ( trace ! $cond:tt $($tail:tt)* ) => {{
        expr!( r.not($cond) $($tail)*)
    }};

    ($( $val:expr ),* $(,)*) => {{
        $crate::Command::new(arr!($($val),*))
    }};
}

/// Splice an array of arguments into another term
///
/// `args` is a macro that’s used to splice a number of arguments into another term. This is
/// useful when you want to call a variadic term such as [branch](commands/trait.Branch.html) with a set of arguments produced at
/// runtime.
///
/// *Note* that args evaluates all its arguments before passing them into the parent term, even if
/// the parent term otherwise allows lazy evaluation.
///
/// # Example
///
/// Get Alice and Bob from the table `people`.
///
/// ```
/// # #[macro_use] extern crate reql;
/// # use reql::commands::*;
/// # use reql::commands::run::Dummy;
/// # use reql::r;
/// # fn main() {
/// let x = 10;
/// r.branch(args!(x > 5, "big", "small")).run::<String>();
/// # }
/// ```
///
/// # Related commands
///
/// * [arr](macro.arr.html)
#[macro_export]
macro_rules! args {
    (trace $left:tt && $right:tt, $($tail:tt)* ) => {{
        args!(expr!($left).and($right), $($tail)*)
    }};

    (trace $left:tt || $right:tt, $($tail:tt)* ) => {{
        args!(expr!($left).or($right), $($tail)*)
    }};

    (trace $left:tt == $right:tt, $($tail:tt)* ) => {{
        args!(expr!($left).eq($right), $($tail)*)
    }};

    (trace $left:tt != $right:tt, $($tail:tt)* ) => {{
        args!(expr!($left).ne($right), $($tail)*)
    }};

    (trace $left:tt > $right:tt, $($tail:tt)* ) => {{
        args!(expr!($left).gt($right), $($tail)*)
    }};

    (trace $left:tt >= $right:tt, $($tail:tt)* ) => {{
        args!(expr!($left).ge($right), $($tail)*)
    }};

    (trace $left:tt < $right:tt, $($tail:tt)* ) => {{
        args!(expr!($left).lt($right), $($tail)*)
    }};

    (trace $left:tt <= $right:tt, $($tail:tt)* ) => {{
        args!(expr!($left).le($right), $($tail)*)
    }};

    (trace ! $cond:tt, $($tail:tt)* ) => {{
        args!(r.not($cond), $($tail)*)
    }};

    (trace $left:tt + $right:tt, $($tail:tt)* ) => {{
        args!(expr!($left).add($right), $($tail)*)
    }};

    ($( $val:expr ),* $(,)*) => {{
        arr!($($val),*)
    }};
}

/// Take one or more values as arguments and return an array
#[macro_export]
macro_rules! arr {
    ($( $val:expr ),* $(,)*) => {{
        use $crate::ToArg;
        use $crate::Term;

        let mut term = Term::new();
        $(
            for arg in $val.to_arg() {
                term.mut_args().push(arg);
            }
        )*
        term
    }}
}

/// Create an object from a list of key-value pairs, where the keys must be strings
#[macro_export]
macro_rules! obj {
    ($( $key:ident: $val:expr ),* $(,)*) => {{
        use $crate::ToArg;
        use $crate::Term;
        use $crate::TermPair;

        let mut object = Term::new();
        $(
            let mut term = Term::new();
            for arg in $val.to_arg() {
                term.mut_args().push(arg);
            }
            let key = stringify!($key);
            let mut term_pair = TermPair::new();
            term_pair.set_key(key.into());
            term_pair.set_val(term);
            object.mut_optargs().push(term_pair);
         )*
        object
    }}
}
<commit_msg>Extract macros to separate file<commit_after>macro_rules! commands {
    ($($cmd:ident),* $(,)*) => {
        $(
            mod $cmd;
            pub use self::$cmd::*;
        )*
    }
}

macro_rules! command {
    ( $(#[$attr:meta])* ) => {
        #[derive(Command)]
        $(#[$attr])*
        struct _Dummy;
    }
}

/// Construct a ReQL JSON object from a Rust expression
///
/// # Example
///
/// Objects wrapped with expr can then be manipulated by ReQL API functions.
///
/// ```
/// # #[macro_use] extern crate reql;
/// # use reql::commands::*;
/// # use reql::commands::run::Dummy;
/// # use reql::r;
/// # struct Value;
/// # fn main() {
/// expr!(obj!{a: "b"}).merge(obj!{b: arr![1, 2, 3]}).run::<Value>();
/// # }
/// ```
///
/// # Example
///
/// The `expr` macro can also compile an if else statement down to a ReQL branch command.
///
/// ```text
/// # #[macro_use] extern crate reql;
/// # use reql::commands::*;
/// # use reql::commands::run::Dummy;
/// # use reql::r;
/// # struct Value;
/// # fn main() {
/// let hero = r.table("marvel").get("Iron Man");
///
/// r.do_(expr!{
///
///     if hero.get_field("victories") > 100 {
///         hero.get_field("name") + " is a superhero"
///     }
///
///     else if hero.get_field("victories") > 10 {
///         hero.get_field("name") + " is a hero"
///     }
///
///     else {
///         hero.get_field("name") + " is very nice"
///     }
///
/// }).run::<String>();
/// # }
/// ```
#[macro_export]
macro_rules! expr {
    () => {};

    (trace if $($cond:tt)* { $($body:tt)* } $(else if $($elif_cond:tt)* { $($elif_body:tt)* })* else { $($el_body:tt)* } $($tail:tt)*) => {{
        expr!( r.branch(args!($($cond)*, $($body)*, $($($elif_cond)*, $($elif_body)*),* $($el_body)*)) $($tail)*)
    }};

    ( trace $($left:tt)* && $($right:tt)* $($tail:tt)* ) => {{
        expr!( expr!($($left)*).and($($right)*) $($tail)*)
    }};

    ( trace $($left:tt)* || $($right:tt)* $($tail:tt)* ) => {{
        expr!( expr!($($left)*).or($($right)*) $($tail)*)
    }};

    ( trace $($left:tt)* == $($right:tt)* $($tail:tt)* ) => {{
        expr!( expr!($($left)*).eq($($right)*) $($tail)*)
    }};

    ( trace $($left:tt)* != $($right:tt)* $($tail:tt)* ) => {{
        expr!( expr!($($left)*).ne($($right)*) $($tail)*)
    }};

    ( trace $($left:tt)* > $($right:tt)* $($tail:tt)* ) => {{
        expr!( expr!($($left)*).gt($($right)*) $($tail)*)
    }};

    ( trace $($left:tt)* >= $($right:tt)* $($tail:tt)* ) => {{
        expr!( expr!($($left)*).ge($($right)*) $($tail)*)
    }};

    ( trace $($left:tt)* < $($right:tt)* $($tail:tt)* ) => {{
        expr!( expr!($($left)*).lt($($right)*) $($tail)*)
    }};

    ( trace $($left:tt)* <= $($right:tt)* $($tail:tt)* ) => {{
        expr!( expr!($($left)*).le($($right)*) $($tail)*)
    }};

    ( trace ! $($cond:tt)* $($tail:tt)* ) => {{
        expr!( r.not($($cond)*) $($tail)*)
    }};

    ($( $val:expr ),* $(,)*) => {{
        $crate::Command::new(arr!($($val),*))
    }};
}

/// Splice an array of arguments into another term
///
/// `args` is a macro that’s used to splice a number of arguments into another term. This is
/// useful when you want to call a variadic term such as [branch](commands/trait.Branch.html) with a set of arguments produced at
/// runtime.
///
/// *Note* that args evaluates all its arguments before passing them into the parent term, even if
/// the parent term otherwise allows lazy evaluation.
///
/// # Example
///
/// Get Alice and Bob from the table `people`.
///
/// ```
/// # #[macro_use] extern crate reql;
/// # use reql::commands::*;
/// # use reql::commands::run::Dummy;
/// # use reql::r;
/// # fn main() {
/// let x = 10;
//// r.branch(args!(x > 5, "big", "small")).run::<String>();
/// r.branch(args!(r.table(x) > 5, "big", "small")).run::<String>();
/// # }
/// ```
///
/// # Related commands
///
/// * [arr](macro.arr.html)
#[macro_export]
macro_rules! args {
    (trace $($left:tt)* && $($right:tt)*, $($tail:tt)* ) => {{
        args!(expr!($($left)*).and($($right)*), $($tail)*)
    }};

    (trace $($left:tt)* || $($right:tt)*, $($tail:tt)* ) => {{
        args!(expr!($($left)*).or($($right)*), $($tail)*)
    }};

    (trace $($left:tt)* == $($right:tt)*, $($tail:tt)* ) => {{
        args!(expr!($($left)*).eq($($right)*), $($tail)*)
    }};

    (trace $($left:tt)* != $($right:tt)*, $($tail:tt)* ) => {{
        args!(expr!($($left)*).ne($($right)*), $($tail)*)
    }};

    (trace $left:block > $right:block, $($tail:tt)* ) => {{
        args!(expr!($left).gt($right), $($tail)*)
    }};

    (trace $($left:tt)* >= $($right:tt)*, $($tail:tt)* ) => {{
        args!(expr!($($left)*).ge($($right)*), $($tail)*)
    }};

    (trace $($left:tt)* < $($right:tt)*, $($tail:tt)* ) => {{
        args!(expr!($($left)*).lt($($right)*), $($tail)*)
    }};

    (trace $($left:tt)* <= $($right:tt)*, $($tail:tt)* ) => {{
        args!(expr!($($left)*).le($($right)*), $($tail)*)
    }};

    (trace ! $cond:tt, $($tail:tt)* ) => {{
        args!(r.not($cond), $($tail)*)
    }};

    (trace $($left:tt)* + $($right:tt)*, $($tail:tt)* ) => {{
        args!(expr!($($left)*).add($($right)*), $($tail)*)
    }};

    ($( $val:expr ),* $(,)*) => {{
        arr!($($val),*)
    }};
}

/// Take one or more values as arguments and return an array
#[macro_export]
macro_rules! arr {
    ($( $val:expr ),* $(,)*) => {{
        use $crate::ToArg;
        use $crate::Term;

        let mut term = Term::new();
        $(
            for arg in $val.to_arg() {
                term.mut_args().push(arg);
            }
        )*
        term
    }}
}

/// Create an object from a list of key-value pairs, where the keys must be strings
#[macro_export]
macro_rules! obj {
    ( $( $key:ident: $val:expr ),* $(,)*) => {{
        use $crate::ToArg;
        use $crate::Term;
        use $crate::TermPair;

        let mut object = Term::new();
        $(
            let mut term = Term::new();
            for arg in $val.to_arg() {
                term.mut_args().push(arg);
            }
            let key = stringify!($key);
            let mut term_pair = TermPair::new();
            term_pair.set_key(key.into());
            term_pair.set_val(term);
            object.mut_optargs().push(term_pair);
         )*
        object
    }}
}
<|endoftext|>


<commit_before>use byteorder::{self, ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt};
use rustc::middle::ty;
use std::collections::{BTreeMap, HashMap};
use std::collections::Bound::{Included, Excluded};
use std::mem;
use std::ptr;

use error::{EvalError, EvalResult};
use primval::PrimVal;

pub struct Memory {
    alloc_map: HashMap<u64, Allocation>,
    next_id: u64,
    pub pointer_size: usize,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct AllocId(u64);

#[derive(Debug)]
pub struct Allocation {
    pub bytes: Vec<u8>,
    pub relocations: BTreeMap<usize, AllocId>,
    // TODO(tsion): undef mask
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Pointer {
    pub alloc_id: AllocId,
    pub offset: usize,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FieldRepr {
    pub offset: usize,
    pub size: usize,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Repr {
    /// Representation for a non-aggregate type such as a boolean, integer, character or pointer.
    Primitive {
        size: usize
    },

    /// The representation for aggregate types including structs, enums, and tuples.
    Aggregate {
        /// The size of the discriminant (an integer). Should be between 0 and 8. Always 0 for
        /// structs and tuples.
        discr_size: usize,

        /// The size of the entire aggregate, including the discriminant.
        size: usize,

        /// The representations of the contents of each variant.
        variants: Vec<Vec<FieldRepr>>,
    },

    Array {
        elem_size: usize,

        /// Number of elements.
        length: usize,
    },
}

impl Memory {
    pub fn new() -> Self {
        Memory {
            alloc_map: HashMap::new(),
            next_id: 0,

            // TODO(tsion): Should this be host's or target's usize?
            pointer_size: mem::size_of::<usize>(),
        }
    }

    pub fn allocate(&mut self, size: usize) -> Pointer {
        let id = AllocId(self.next_id);
        let alloc = Allocation { bytes: vec![0; size], relocations: BTreeMap::new() };
        self.alloc_map.insert(self.next_id, alloc);
        self.next_id += 1;
        Pointer {
            alloc_id: id,
            offset: 0,
        }
    }

    pub fn get(&self, id: AllocId) -> EvalResult<&Allocation> {
        self.alloc_map.get(&id.0).ok_or(EvalError::DanglingPointerDeref)
    }

    pub fn get_mut(&mut self, id: AllocId) -> EvalResult<&mut Allocation> {
        self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref)
    }

    fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> {
        let alloc = try!(self.get(ptr.alloc_id));
        try!(alloc.check_no_relocations(ptr.offset, ptr.offset + size));
        Ok(&alloc.bytes[ptr.offset..ptr.offset + size])
    }

    fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> {
        let alloc = try!(self.get_mut(ptr.alloc_id));
        try!(alloc.check_no_relocations(ptr.offset, ptr.offset + size));
        Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size])
    }

    pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> {
        let (src_bytes, relocations) = {
            let alloc = try!(self.get_mut(src.alloc_id));
            try!(alloc.check_relocation_edges(src.offset, src.offset + size));
            let bytes = alloc.bytes[src.offset..src.offset + size].as_mut_ptr();

            let mut relocations: Vec<(usize, AllocId)> = alloc.relocations
                .range(Included(&src.offset), Excluded(&(src.offset + size)))
                .map(|(&k, &v)| (k, v))
                .collect();

            for &mut (ref mut offset, _) in &mut relocations {
                alloc.relocations.remove(offset);
                *offset += dest.offset - src.offset;
            }

            (bytes, relocations)
        };

        let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr();

        // TODO(tsion): Clear the destination range's existing relocations.
        try!(self.get_mut(dest.alloc_id)).relocations.extend(relocations);

        // SAFE: The above indexing would have panicked if there weren't at least `size` bytes
        // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and
        // `dest` could possibly overlap.
        unsafe {
            if src.alloc_id == dest.alloc_id {
                ptr::copy(src_bytes, dest_bytes, size);
            } else {
                ptr::copy_nonoverlapping(src_bytes, dest_bytes, size);
            }
        }

        Ok(())
    }

    pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<Pointer> {
        let alloc = try!(self.get(ptr.alloc_id));
        try!(alloc.check_relocation_edges(ptr.offset, ptr.offset + self.pointer_size));
        let bytes = &alloc.bytes[ptr.offset..ptr.offset + self.pointer_size];
        let offset = byteorder::NativeEndian::read_u64(bytes) as usize;

        match alloc.relocations.get(&ptr.offset) {
            Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }),
            None => Err(EvalError::ReadBytesAsPointer),
        }
    }

    // TODO(tsion): Detect invalid writes here and elsewhere.
    pub fn write_ptr(&mut self, dest: Pointer, ptr_val: Pointer) -> EvalResult<()> {
        {
            let size = self.pointer_size;
            let bytes = try!(self.get_bytes_mut(dest, size));
            byteorder::NativeEndian::write_u64(bytes, ptr_val.offset as u64);
        }
        let alloc = try!(self.get_mut(dest.alloc_id));
        alloc.relocations.insert(dest.offset, ptr_val.alloc_id);
        Ok(())
    }

    pub fn read_primval(&self, ptr: Pointer, ty: ty::Ty) -> EvalResult<PrimVal> {
        use syntax::ast::{IntTy, UintTy};
        match ty.sty {
            ty::TyBool              => self.read_bool(ptr).map(PrimVal::Bool),
            ty::TyInt(IntTy::I8)    => self.read_int(ptr, 1).map(|n| PrimVal::I8(n as i8)),
            ty::TyInt(IntTy::I16)   => self.read_int(ptr, 2).map(|n| PrimVal::I16(n as i16)),
            ty::TyInt(IntTy::I32)   => self.read_int(ptr, 4).map(|n| PrimVal::I32(n as i32)),
            ty::TyInt(IntTy::I64)   => self.read_int(ptr, 8).map(|n| PrimVal::I64(n as i64)),
            ty::TyUint(UintTy::U8)  => self.read_uint(ptr, 1).map(|n| PrimVal::U8(n as u8)),
            ty::TyUint(UintTy::U16) => self.read_uint(ptr, 2).map(|n| PrimVal::U16(n as u16)),
            ty::TyUint(UintTy::U32) => self.read_uint(ptr, 4).map(|n| PrimVal::U32(n as u32)),
            ty::TyUint(UintTy::U64) => self.read_uint(ptr, 8).map(|n| PrimVal::U64(n as u64)),

            // TODO(tsion): Pick the PrimVal dynamically.
            ty::TyInt(IntTy::Is)    => self.read_int(ptr, self.pointer_size).map(PrimVal::I64),
            ty::TyUint(UintTy::Us)  => self.read_uint(ptr, self.pointer_size).map(PrimVal::U64),
            _ => panic!("primitive read of non-primitive type: {:?}", ty),
        }
    }

    pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<()> {
        match val {
            PrimVal::Bool(b) => self.write_bool(ptr, b),
            PrimVal::I8(n)   => self.write_int(ptr, n as i64, 1),
            PrimVal::I16(n)  => self.write_int(ptr, n as i64, 2),
            PrimVal::I32(n)  => self.write_int(ptr, n as i64, 4),
            PrimVal::I64(n)  => self.write_int(ptr, n as i64, 8),
            PrimVal::U8(n)   => self.write_uint(ptr, n as u64, 1),
            PrimVal::U16(n)  => self.write_uint(ptr, n as u64, 2),
            PrimVal::U32(n)  => self.write_uint(ptr, n as u64, 4),
            PrimVal::U64(n)  => self.write_uint(ptr, n as u64, 8),
        }
    }

    pub fn read_bool(&self, ptr: Pointer) -> EvalResult<bool> {
        let bytes = try!(self.get_bytes(ptr, 1));
        match bytes[0] {
            0 => Ok(false),
            1 => Ok(true),
            _ => Err(EvalError::InvalidBool),
        }
    }

    pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<()> {
        self.get_bytes_mut(ptr, 1).map(|bytes| bytes[0] = b as u8)
    }

    pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult<i64> {
        self.get_bytes(ptr, size).map(|mut b| b.read_int::<NativeEndian>(size).unwrap())
    }

    pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<()> {
        self.get_bytes_mut(ptr, size).map(|mut b| b.write_int::<NativeEndian>(n, size).unwrap())
    }

    pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult<u64> {
        self.get_bytes(ptr, size).map(|mut b| b.read_uint::<NativeEndian>(size).unwrap())
    }

    pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<()> {
        self.get_bytes_mut(ptr, size).map(|mut b| b.write_uint::<NativeEndian>(n, size).unwrap())
    }
}

impl Allocation {
    fn check_bounds(&self, start: usize, end: usize) -> EvalResult<()> {
        if start <= self.bytes.len() && end <= self.bytes.len() {
            Ok(())
        } else {
            Err(EvalError::PointerOutOfBounds)
        }
    }

    fn count_overlapping_relocations(&self, start: usize, end: usize) -> usize {
        self.relocations.range(
            // FIXME(tsion): Assuming pointer size is 8. Move this method to Memory.
            Included(&start.saturating_sub(8 - 1)),
            Excluded(&end)
        ).count()
    }

    fn check_relocation_edges(&self, start: usize, end: usize) -> EvalResult<()> {
        try!(self.check_bounds(start, end));
        let n =
            self.count_overlapping_relocations(start, start) +
            self.count_overlapping_relocations(end, end);
        if n == 0 {
            Ok(())
        } else {
            Err(EvalError::InvalidPointerAccess)
        }
    }

    fn check_no_relocations(&self, start: usize, end: usize) -> EvalResult<()> {
        try!(self.check_bounds(start, end));
        if self.count_overlapping_relocations(start, end) == 0 {
            Ok(())
        } else {
            Err(EvalError::InvalidPointerAccess)
        }
    }
}

impl Pointer {
    pub fn offset(self, i: isize) -> Self {
        Pointer { offset: (self.offset as isize + i) as usize, ..self }
    }
}

impl Repr {
    pub fn size(&self) -> usize {
        match *self {
            Repr::Primitive { size } => size,
            Repr::Aggregate { size, .. } => size,
            Repr::Array { elem_size, length } => elem_size * length,
        }
    }
}
<commit_msg>Avoid an integer underflow error.<commit_after>use byteorder::{self, ByteOrder, NativeEndian, ReadBytesExt, WriteBytesExt};
use rustc::middle::ty;
use std::collections::{BTreeMap, HashMap};
use std::collections::Bound::{Included, Excluded};
use std::mem;
use std::ptr;

use error::{EvalError, EvalResult};
use primval::PrimVal;

pub struct Memory {
    alloc_map: HashMap<u64, Allocation>,
    next_id: u64,
    pub pointer_size: usize,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct AllocId(u64);

#[derive(Debug)]
pub struct Allocation {
    pub bytes: Vec<u8>,
    pub relocations: BTreeMap<usize, AllocId>,
    // TODO(tsion): undef mask
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Pointer {
    pub alloc_id: AllocId,
    pub offset: usize,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FieldRepr {
    pub offset: usize,
    pub size: usize,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Repr {
    /// Representation for a non-aggregate type such as a boolean, integer, character or pointer.
    Primitive {
        size: usize
    },

    /// The representation for aggregate types including structs, enums, and tuples.
    Aggregate {
        /// The size of the discriminant (an integer). Should be between 0 and 8. Always 0 for
        /// structs and tuples.
        discr_size: usize,

        /// The size of the entire aggregate, including the discriminant.
        size: usize,

        /// The representations of the contents of each variant.
        variants: Vec<Vec<FieldRepr>>,
    },

    Array {
        elem_size: usize,

        /// Number of elements.
        length: usize,
    },
}

impl Memory {
    pub fn new() -> Self {
        Memory {
            alloc_map: HashMap::new(),
            next_id: 0,

            // TODO(tsion): Should this be host's or target's usize?
            pointer_size: mem::size_of::<usize>(),
        }
    }

    pub fn allocate(&mut self, size: usize) -> Pointer {
        let id = AllocId(self.next_id);
        let alloc = Allocation { bytes: vec![0; size], relocations: BTreeMap::new() };
        self.alloc_map.insert(self.next_id, alloc);
        self.next_id += 1;
        Pointer {
            alloc_id: id,
            offset: 0,
        }
    }

    pub fn get(&self, id: AllocId) -> EvalResult<&Allocation> {
        self.alloc_map.get(&id.0).ok_or(EvalError::DanglingPointerDeref)
    }

    pub fn get_mut(&mut self, id: AllocId) -> EvalResult<&mut Allocation> {
        self.alloc_map.get_mut(&id.0).ok_or(EvalError::DanglingPointerDeref)
    }

    fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<&[u8]> {
        let alloc = try!(self.get(ptr.alloc_id));
        try!(alloc.check_no_relocations(ptr.offset, ptr.offset + size));
        Ok(&alloc.bytes[ptr.offset..ptr.offset + size])
    }

    fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<&mut [u8]> {
        let alloc = try!(self.get_mut(ptr.alloc_id));
        try!(alloc.check_no_relocations(ptr.offset, ptr.offset + size));
        Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size])
    }

    pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<()> {
        let (src_bytes, relocations) = {
            let alloc = try!(self.get_mut(src.alloc_id));
            try!(alloc.check_relocation_edges(src.offset, src.offset + size));
            let bytes = alloc.bytes[src.offset..src.offset + size].as_mut_ptr();

            let mut relocations: Vec<(usize, AllocId)> = alloc.relocations
                .range(Included(&src.offset), Excluded(&(src.offset + size)))
                .map(|(&k, &v)| (k, v))
                .collect();

            for &mut (ref mut offset, _) in &mut relocations {
                alloc.relocations.remove(offset);
                *offset += dest.offset;
                *offset -= src.offset;
            }

            (bytes, relocations)
        };

        let dest_bytes = try!(self.get_bytes_mut(dest, size)).as_mut_ptr();

        // TODO(tsion): Clear the destination range's existing relocations.
        try!(self.get_mut(dest.alloc_id)).relocations.extend(relocations);

        // SAFE: The above indexing would have panicked if there weren't at least `size` bytes
        // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and
        // `dest` could possibly overlap.
        unsafe {
            if src.alloc_id == dest.alloc_id {
                ptr::copy(src_bytes, dest_bytes, size);
            } else {
                ptr::copy_nonoverlapping(src_bytes, dest_bytes, size);
            }
        }

        Ok(())
    }

    pub fn read_ptr(&self, ptr: Pointer) -> EvalResult<Pointer> {
        let alloc = try!(self.get(ptr.alloc_id));
        try!(alloc.check_relocation_edges(ptr.offset, ptr.offset + self.pointer_size));
        let bytes = &alloc.bytes[ptr.offset..ptr.offset + self.pointer_size];
        let offset = byteorder::NativeEndian::read_u64(bytes) as usize;

        match alloc.relocations.get(&ptr.offset) {
            Some(&alloc_id) => Ok(Pointer { alloc_id: alloc_id, offset: offset }),
            None => Err(EvalError::ReadBytesAsPointer),
        }
    }

    // TODO(tsion): Detect invalid writes here and elsewhere.
    pub fn write_ptr(&mut self, dest: Pointer, ptr_val: Pointer) -> EvalResult<()> {
        {
            let size = self.pointer_size;
            let bytes = try!(self.get_bytes_mut(dest, size));
            byteorder::NativeEndian::write_u64(bytes, ptr_val.offset as u64);
        }
        let alloc = try!(self.get_mut(dest.alloc_id));
        alloc.relocations.insert(dest.offset, ptr_val.alloc_id);
        Ok(())
    }

    pub fn read_primval(&self, ptr: Pointer, ty: ty::Ty) -> EvalResult<PrimVal> {
        use syntax::ast::{IntTy, UintTy};
        match ty.sty {
            ty::TyBool              => self.read_bool(ptr).map(PrimVal::Bool),
            ty::TyInt(IntTy::I8)    => self.read_int(ptr, 1).map(|n| PrimVal::I8(n as i8)),
            ty::TyInt(IntTy::I16)   => self.read_int(ptr, 2).map(|n| PrimVal::I16(n as i16)),
            ty::TyInt(IntTy::I32)   => self.read_int(ptr, 4).map(|n| PrimVal::I32(n as i32)),
            ty::TyInt(IntTy::I64)   => self.read_int(ptr, 8).map(|n| PrimVal::I64(n as i64)),
            ty::TyUint(UintTy::U8)  => self.read_uint(ptr, 1).map(|n| PrimVal::U8(n as u8)),
            ty::TyUint(UintTy::U16) => self.read_uint(ptr, 2).map(|n| PrimVal::U16(n as u16)),
            ty::TyUint(UintTy::U32) => self.read_uint(ptr, 4).map(|n| PrimVal::U32(n as u32)),
            ty::TyUint(UintTy::U64) => self.read_uint(ptr, 8).map(|n| PrimVal::U64(n as u64)),

            // TODO(tsion): Pick the PrimVal dynamically.
            ty::TyInt(IntTy::Is)    => self.read_int(ptr, self.pointer_size).map(PrimVal::I64),
            ty::TyUint(UintTy::Us)  => self.read_uint(ptr, self.pointer_size).map(PrimVal::U64),
            _ => panic!("primitive read of non-primitive type: {:?}", ty),
        }
    }

    pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<()> {
        match val {
            PrimVal::Bool(b) => self.write_bool(ptr, b),
            PrimVal::I8(n)   => self.write_int(ptr, n as i64, 1),
            PrimVal::I16(n)  => self.write_int(ptr, n as i64, 2),
            PrimVal::I32(n)  => self.write_int(ptr, n as i64, 4),
            PrimVal::I64(n)  => self.write_int(ptr, n as i64, 8),
            PrimVal::U8(n)   => self.write_uint(ptr, n as u64, 1),
            PrimVal::U16(n)  => self.write_uint(ptr, n as u64, 2),
            PrimVal::U32(n)  => self.write_uint(ptr, n as u64, 4),
            PrimVal::U64(n)  => self.write_uint(ptr, n as u64, 8),
        }
    }

    pub fn read_bool(&self, ptr: Pointer) -> EvalResult<bool> {
        let bytes = try!(self.get_bytes(ptr, 1));
        match bytes[0] {
            0 => Ok(false),
            1 => Ok(true),
            _ => Err(EvalError::InvalidBool),
        }
    }

    pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<()> {
        self.get_bytes_mut(ptr, 1).map(|bytes| bytes[0] = b as u8)
    }

    pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult<i64> {
        self.get_bytes(ptr, size).map(|mut b| b.read_int::<NativeEndian>(size).unwrap())
    }

    pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<()> {
        self.get_bytes_mut(ptr, size).map(|mut b| b.write_int::<NativeEndian>(n, size).unwrap())
    }

    pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult<u64> {
        self.get_bytes(ptr, size).map(|mut b| b.read_uint::<NativeEndian>(size).unwrap())
    }

    pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<()> {
        self.get_bytes_mut(ptr, size).map(|mut b| b.write_uint::<NativeEndian>(n, size).unwrap())
    }
}

impl Allocation {
    fn check_bounds(&self, start: usize, end: usize) -> EvalResult<()> {
        if start <= self.bytes.len() && end <= self.bytes.len() {
            Ok(())
        } else {
            Err(EvalError::PointerOutOfBounds)
        }
    }

    fn count_overlapping_relocations(&self, start: usize, end: usize) -> usize {
        self.relocations.range(
            // FIXME(tsion): Assuming pointer size is 8. Move this method to Memory.
            Included(&start.saturating_sub(8 - 1)),
            Excluded(&end)
        ).count()
    }

    fn check_relocation_edges(&self, start: usize, end: usize) -> EvalResult<()> {
        try!(self.check_bounds(start, end));
        let n =
            self.count_overlapping_relocations(start, start) +
            self.count_overlapping_relocations(end, end);
        if n == 0 {
            Ok(())
        } else {
            Err(EvalError::InvalidPointerAccess)
        }
    }

    fn check_no_relocations(&self, start: usize, end: usize) -> EvalResult<()> {
        try!(self.check_bounds(start, end));
        if self.count_overlapping_relocations(start, end) == 0 {
            Ok(())
        } else {
            Err(EvalError::InvalidPointerAccess)
        }
    }
}

impl Pointer {
    pub fn offset(self, i: isize) -> Self {
        Pointer { offset: (self.offset as isize + i) as usize, ..self }
    }
}

impl Repr {
    pub fn size(&self) -> usize {
        match *self {
            Repr::Primitive { size } => size,
            Repr::Aggregate { size, .. } => size,
            Repr::Array { elem_size, length } => elem_size * length,
        }
    }
}
<|endoftext|>


<commit_before>use super::byteorder::{BigEndian, ByteOrder};

// Size of the memory map of a CHIP-8 interpreter is 4kB.
pub const RAM_SIZE: usize = 4096;

// Memory map constraints for CHIP-8 virtual machines.
pub const START_RESERVED: usize = 0x000;
pub const END_RESERVED: usize = 0x200;
pub const END_PROGRAM_SPACE: usize = 0xFFF;

// Font size constants.
const CHARACTER_SIZE: usize = 5;
const CHARACTER_COUNT: usize = 16;

// Where fonts are stored in interpreter memory.
const FONT_OFFSET: usize = 0;

pub struct Memory {
    // Memory allocated for the running CHIP-8 application.
    ram: Vec<u8>,
}

impl Memory {
    /// Allocate some memory for ram and load a rom into it.
    pub fn new(rom: Vec<u8>) -> Memory {
        // Allocate 4kB of memory as defined in the many specifications.
        let mut ram = vec![0; RAM_SIZE];

        Memory::dump_rom(&mut ram, &rom);
        Memory::dump_fonts(&mut ram);

        Memory {
            ram: ram,
        }
    }

    /// Simply returns an 8-bit word at the specified address.
    #[inline(always)]
    pub fn read(&self, addr: usize) -> u8 {
        self.ram[addr]
    }

    /// Write an 8-bit byte at a specific address. There is no concern over
    /// endianess since this function operates on single bytes.
    #[inline(always)]
    pub fn write(&mut self, addr: usize, byte: u8) {
        self.ram[addr] = byte;
    }

    /// Reads a 16-bit word from ram. This function is used mainly to read and
    /// execute instructions as their word size is 16 bits.
    #[inline(always)]
    pub fn read_word(&self, addr: u16) -> u16 {
        BigEndian::read_u16(&self.ram[addr as usize..])
    }

    /// Find the memory address of the requested character.
    #[inline(always)]
    pub fn get_font(&self, font: u8) -> u16 {
        FONT_OFFSET as u16 + font as u16 * CHARACTER_SIZE as u16
    }

    /// Dumps a passed rom containing executable code into ram starting at
    /// program space (right after reserved space ends).
    fn dump_rom(ram: &mut Vec<u8>, rom: &Vec<u8>) {
        // Dump the rom containing the executable code of the program into ram
        // starting at the start of the program space.
        for i in 0..rom.len() {
            ram[i + END_RESERVED] = rom[i];
        }
    }

    /// Dumps the standard CHIP-8 fonts to ram. The fonts are stored at the
    /// start of reserved memory and this is fine since the fonts are the only
    /// thing being stored in reserved memory.
    fn dump_fonts(ram: &mut Vec<u8>) {
        // The characters 0-F to be stored in ram as a font for chip 8 programs.
        // Nested vectors are used for ease of reading.
        //
        // Hopefully this is optimized by the compiler, but it's no problem
        // since this is run only once.
        let fonts = vec![
            vec![0xF0, 0x90, 0x90, 0x90, 0xF0], // 0
            vec![0x20, 0x60, 0x20, 0x20, 0x70], // 1
            vec![0xF0, 0x10, 0xf0, 0x80, 0xF0], // 2
            vec![0xF0, 0x10, 0xF0, 0x10, 0xF0], // 3
            vec![0x90, 0x90, 0xF0, 0x10, 0x10], // 4
            vec![0xF0, 0x80, 0xF0, 0x10, 0xF0], // 5
            vec![0xF0, 0x80, 0xF0, 0x90, 0xF0], // 6
            vec![0xF0, 0x10, 0x20, 0x40, 0x40], // 7
            vec![0xF0, 0x90, 0xF0, 0x90, 0xF0], // 8
            vec![0xF0, 0x90, 0xF0, 0x10, 0xF0], // 9
            vec![0xF0, 0x90, 0xF0, 0x90, 0x90], // A
            vec![0xE0, 0x90, 0xE0, 0x90, 0xE0], // B
            vec![0xF0, 0x80, 0x80, 0x80, 0xF0], // C
            vec![0xE0, 0x90, 0x90, 0x90, 0xE0], // D
            vec![0xF0, 0x80, 0xF0, 0x80, 0xF0], // E
            vec![0xF0, 0x80, 0xF0, 0x80, 0x80], // F
        ];

        for i in 0..CHARACTER_COUNT {
            // Find where the current character should be stored in memory.
            let start: usize = FONT_OFFSET + i * CHARACTER_SIZE;

            // Copy the current character into the calculated spot in memory.
            for j in 0..CHARACTER_SIZE {
                ram[start + j] = fonts[i][j];
            }
        }
    }
}
<commit_msg>Replaced dead code with some nice documentation<commit_after>use super::byteorder::{BigEndian, ByteOrder};

// Size of the memory map of a CHIP-8 interpreter is 4kB.
pub const RAM_SIZE: usize = 4096;

// Memory map constraints for CHIP-8 virtual machines.
pub const END_RESERVED: usize = 0x200;

// Here are some additional unused constrains for documentation purposes. These
// are defined in the CHIP-8 spec but are not referenced in code for this
// specific implementation.
//
//     pub const START_RESERVED: usize = 0x000;
//     pub const END_PROGRAM_SPACE: usize = 0xFFF;

// Font size constants.
const CHARACTER_SIZE: usize = 5;
const CHARACTER_COUNT: usize = 16;

// Where fonts are stored in interpreter memory.
const FONT_OFFSET: usize = 0;

pub struct Memory {
    // Memory allocated for the running CHIP-8 application.
    ram: Vec<u8>,
}

impl Memory {
    /// Allocate some memory for ram and load a rom into it.
    pub fn new(rom: Vec<u8>) -> Memory {
        // Allocate 4kB of memory as defined in the many specifications.
        let mut ram = vec![0; RAM_SIZE];

        Memory::dump_rom(&mut ram, &rom);
        Memory::dump_fonts(&mut ram);

        Memory {
            ram: ram,
        }
    }

    /// Simply returns an 8-bit word at the specified address.
    #[inline(always)]
    pub fn read(&self, addr: usize) -> u8 {
        self.ram[addr]
    }

    /// Write an 8-bit byte at a specific address. There is no concern over
    /// endianess since this function operates on single bytes.
    #[inline(always)]
    pub fn write(&mut self, addr: usize, byte: u8) {
        self.ram[addr] = byte;
    }

    /// Reads a 16-bit word from ram. This function is used mainly to read and
    /// execute instructions as their word size is 16 bits.
    #[inline(always)]
    pub fn read_word(&self, addr: u16) -> u16 {
        BigEndian::read_u16(&self.ram[addr as usize..])
    }

    /// Find the memory address of the requested character.
    #[inline(always)]
    pub fn get_font(&self, font: u8) -> u16 {
        FONT_OFFSET as u16 + font as u16 * CHARACTER_SIZE as u16
    }

    /// Dumps a passed rom containing executable code into ram starting at
    /// program space (right after reserved space ends).
    fn dump_rom(ram: &mut Vec<u8>, rom: &Vec<u8>) {
        // Dump the rom containing the executable code of the program into ram
        // starting at the start of the program space.
        for i in 0..rom.len() {
            ram[i + END_RESERVED] = rom[i];
        }
    }

    /// Dumps the standard CHIP-8 fonts to ram. The fonts are stored at the
    /// start of reserved memory and this is fine since the fonts are the only
    /// thing being stored in reserved memory.
    fn dump_fonts(ram: &mut Vec<u8>) {
        // The characters 0-F to be stored in ram as a font for chip 8 programs.
        // Nested vectors are used for ease of reading.
        //
        // Hopefully this is optimized by the compiler, but it's no problem
        // since this is run only once.
        let fonts = vec![
            vec![0xF0, 0x90, 0x90, 0x90, 0xF0], // 0
            vec![0x20, 0x60, 0x20, 0x20, 0x70], // 1
            vec![0xF0, 0x10, 0xf0, 0x80, 0xF0], // 2
            vec![0xF0, 0x10, 0xF0, 0x10, 0xF0], // 3
            vec![0x90, 0x90, 0xF0, 0x10, 0x10], // 4
            vec![0xF0, 0x80, 0xF0, 0x10, 0xF0], // 5
            vec![0xF0, 0x80, 0xF0, 0x90, 0xF0], // 6
            vec![0xF0, 0x10, 0x20, 0x40, 0x40], // 7
            vec![0xF0, 0x90, 0xF0, 0x90, 0xF0], // 8
            vec![0xF0, 0x90, 0xF0, 0x10, 0xF0], // 9
            vec![0xF0, 0x90, 0xF0, 0x90, 0x90], // A
            vec![0xE0, 0x90, 0xE0, 0x90, 0xE0], // B
            vec![0xF0, 0x80, 0x80, 0x80, 0xF0], // C
            vec![0xE0, 0x90, 0x90, 0x90, 0xE0], // D
            vec![0xF0, 0x80, 0xF0, 0x80, 0xF0], // E
            vec![0xF0, 0x80, 0xF0, 0x80, 0x80], // F
        ];

        for i in 0..CHARACTER_COUNT {
            // Find where the current character should be stored in memory.
            let start: usize = FONT_OFFSET + i * CHARACTER_SIZE;

            // Copy the current character into the calculated spot in memory.
            for j in 0..CHARACTER_SIZE {
                ram[start + j] = fonts[i][j];
            }
        }
    }
}
<|endoftext|>


<commit_before>/// Internal metric representation
///
use std::fmt;
use std::str::FromStr;
use std::cmp;

/// Enum of metric types
pub enum MetricKind {
    Counter(f64), // sample rate
    Gauge,
    Timer,
    Histogram
}

impl fmt::Debug for MetricKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MetricKind::Gauge      => write!(f, "Gauge"),
            MetricKind::Timer      => write!(f, "Timer"),
            MetricKind::Histogram  => write!(f, "Histogram"),
            MetricKind::Counter(s) => write!(f, "Counter(s={})", s)
        }
    }
}

/// Error types for parsing Metrics from strings.
///
pub enum ParseError {
    // Error message, column
    SyntaxError(&'static str, usize)
}

/// Metric value objects.
///
#[derive(Debug)]
pub struct Metric {
    kind: MetricKind,
    name: String,
    value: f64
}

impl FromStr for Metric {
    type Err = ParseError;

    /// Valid message formats are:
    ///
    /// - `<str:metric_name>:<f64:value>|<str:type>`
    /// - `<str:metric_name>:<f64:value>|c|@<f64:sample_rate>`
    fn from_str(line: &str) -> Result<Metric, ParseError> {
        let name_parts: Vec<&str> = line.split(':').collect();
        if name_parts.len() < 2 || name_parts[0].is_empty() {
            return Err(ParseError::SyntaxError(
                    "Metrics require a name.",
                    0))
        }
        let name = name_parts[0].to_string();

        let val_parts: Vec<&str>= name_parts[1].split('|').collect();
        if val_parts.len() < 2 || val_parts[0].is_empty() {
            return Err(ParseError::SyntaxError(
                    "Metrics require a value.",
                    name.len()))
        }
        let value = val_parts[0].parse::<f64>().ok().unwrap();

        Ok(Metric{name: name, value: value, kind: MetricKind::Timer})
        /*
        let name = match line.find(':') {
            // We don't want to allow blank key names.
            Some(pos) if pos != 0 => {
                idx += pos + 1;
                line.slice_chars(0, pos).to_owned()
            },

            _ => return Err(ParseError::SyntaxError(
                    "Metrics require a name",
                    idx))
        };

        // Try to parse `<f64>|`, return None if no match is found.
        let value_opt = line.slice_chars(idx, end).find('|').and_then(|loc| {
            let number = line.slice_chars(idx, idx + loc).parse::<f64>();

            idx = loc + 1;
            Some(number.ok())
        });

        let value = match value_opt {
            Some(v) => v.unwrap(),
            None => return Err(ParseError::SyntaxError(
                        "Metrics require a value",
                        idx))
        };

        let end_idx = cmp::min(idx + 3, end);

        let kind = match line.slice_chars(idx, end_idx) {
            "c" => MetricKind::Counter(1.0),
            "ms" => MetricKind::Timer,
            "h" => MetricKind::Histogram,
            "g" => MetricKind::Gauge,
            // Sampled counter
            /*
            "c|@" => match line.slice_chars(end_idx, end).parse::<f64>() {
                Ok(sample) => MetricKind::Counter(sample),
                _ => return Err(ParseError::SyntaxError(
                            "Counters require a sampling rate",
                            idx))
            },
            */

            // Unknown type
            _ => return Err(ParseError::SyntaxError(
                        "Unknown metric type.",
                        idx))
        };

        Ok(Metric{kind: kind, name: name, value: value})
        */
    }
}

//
// Tests
//
#[test]
fn test_metric_kind_debug_fmt() {
    assert_eq!(
        "Gauge",
        format!("{:?}", MetricKind::Gauge)
    );
    assert_eq!(
        "Timer",
        format!("{:?}", MetricKind::Timer)
    );
    assert_eq!(
        "Histogram",
        format!("{:?}", MetricKind::Histogram)
    );
    assert_eq!(
        "Counter(s=6)",
        format!("{:?}", MetricKind::Counter(6.0))
    );
}

#[test]
fn test_metric_from_str_invalid_no_name() {
    let res = Metric::from_str("");
    assert!(res.is_err(), "Should have an error");
    assert!(!res.is_ok(), "Should have an error");
}

#[test]
fn test_metric_from_str_invalid_no_value() {
    let res = Metric::from_str("foo:");
    assert!(res.is_err(), "Should have an error");
    assert!(!res.is_ok(), "Should have an error");
}

#[test]
fn test_metric_from_str_invalid_no_type() {
    let res = Metric::from_str("foo:12.3");
    assert!(res.is_err(), "Should have an error");
    assert!(!res.is_ok(), "Should have an error");
}

#[test]
fn test_metric_from_str_timer() {
    let res = Metric::from_str("test:12|ms");
    assert!(!res.is_err(), "Should have no error");
    assert!(res.is_ok(), "Should have no error");
    let metric = res.ok().unwrap();
    assert_eq!("test", metric.name);
    assert_eq!(12.0, metric.value);
    // assert_eq!(MetricKind::Timer, metric.kind);
}

#[test]
fn test_metric_from_str_gauge() {
}

#[test]
fn test_metric_from_str_histogram() {
}

#[test]
fn test_metric_from_str_counter() {
}
<commit_msg>Metric parsing works for all types including counters with rates.<commit_after>/// Internal metric representation
///
use std::fmt;
use std::str::FromStr;
use std::collections::HashMap;

/// Enum of metric types
pub enum MetricKind {
    Counter(f64), // sample rate
    Gauge,
    Timer,
    Histogram
}

impl fmt::Debug for MetricKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            MetricKind::Gauge      => write!(f, "Gauge"),
            MetricKind::Timer      => write!(f, "Timer"),
            MetricKind::Histogram  => write!(f, "Histogram"),
            MetricKind::Counter(s) => write!(f, "Counter(s={})", s)
        }
    }
}

/// Error types for parsing Metrics from strings.
///
pub enum ParseError {
    // Error message, column
    SyntaxError(&'static str, usize)
}

/// Metric value objects.
///
#[derive(Debug)]
pub struct Metric {
    kind: MetricKind,
    name: String,
    value: f64
}

impl Metric {
    fn new(name: &str, value: f64, kind: MetricKind) -> Metric {
        Metric{name: name.to_string(), value: value, kind: kind}
    }
}

impl FromStr for Metric {
    type Err = ParseError;

    /// Valid message formats are:
    ///
    /// - `<str:metric_name>:<f64:value>|<str:type>`
    /// - `<str:metric_name>:<f64:value>|c|@<f64:sample_rate>`
    fn from_str(line: &str) -> Result<Metric, ParseError> {
        // Get the metric name
        let name_parts: Vec<&str> = line.split(':').collect();
        if name_parts.len() < 2 || name_parts[0].is_empty() {
            return Err(ParseError::SyntaxError(
                    "Metrics require a name.",
                    0))
        }
        let name = name_parts[0].to_string();

        // Get the float val
        let val_parts: Vec<&str> = name_parts[1].split('|').collect();
        if val_parts.len() < 2 || val_parts[0].is_empty() {
            return Err(ParseError::SyntaxError(
                    "Metrics require a value.",
                    name.len()))
        }
        let value = val_parts[0].parse::<f64>().ok().unwrap();

        // Get kind parts
        let kind = match val_parts[1] {
            "ms" => MetricKind::Timer,
            "g" => MetricKind::Gauge,
            "h" => MetricKind::Histogram,
            "c" => {
                let mut rate:f64 = 1.0;
                if val_parts.len() == 3 {
                    rate = val_parts[2].trim_left_matches('@')
                        .parse::<f64>().ok().unwrap();
                }
                MetricKind::Counter(rate)
            }
            _ => return Err(ParseError::SyntaxError(
                    "Unknown metric type.",
                    2))
        };

        Ok(Metric{name: name, value: value, kind: kind})
    }
}

//
// Tests
//
#[test]
fn test_metric_kind_debug_fmt() {
    assert_eq!(
        "Gauge",
        format!("{:?}", MetricKind::Gauge)
    );
    assert_eq!(
        "Timer",
        format!("{:?}", MetricKind::Timer)
    );
    assert_eq!(
        "Histogram",
        format!("{:?}", MetricKind::Histogram)
    );
    assert_eq!(
        "Counter(s=6)",
        format!("{:?}", MetricKind::Counter(6.0))
    );
}

#[test]
fn test_metric_from_str_invalid_no_name() {
    let res = Metric::from_str("");
    assert!(res.is_err(), "Should have an error");
    assert!(!res.is_ok(), "Should have an error");
}

#[test]
fn test_metric_from_str_invalid_no_value() {
    let res = Metric::from_str("foo:");
    assert!(res.is_err(), "Should have an error");
    assert!(!res.is_ok(), "Should have an error");
}

#[test]
fn test_metric_valid() {
    let mut valid = HashMap::new();
    valid.insert(
        "foo.test:12.3|ms",
        Metric::new("foo.test", 12.3, MetricKind::Timer)
    );
    valid.insert(
        "test:18.123|g",
        Metric::new("test", 18.123, MetricKind::Gauge)
    );
    valid.insert(
        "test:18.123|g",
        Metric::new("test", 18.123, MetricKind::Gauge)
    );
    valid.insert(
        "foo.bar.val:18.123|h",
        Metric::new("foo.bar.val", 18.123, MetricKind::Histogram)
    );
    valid.insert(
        "thing.total:12|c",
        Metric::new("thing.total", 12.0, MetricKind::Counter(1.0))
    );
    valid.insert(
        "thing.total:5.6|c|@123",
        Metric::new("thing.total", 5.6, MetricKind::Counter(123.0))
    );

    for (input, expected) in valid.iter() {
        let result = Metric::from_str(*input);
        assert!(result.is_ok());

        let actual = result.ok().unwrap();
        assert_eq!(expected.name, actual.name);
        assert_eq!(expected.value, actual.value);

        // TODO this is stupid, there must be a better way.
        assert_eq!(
            format!("{:?}", expected.kind),
            format!("{:?}", actual.kind)
        );
    }
}
<|endoftext|>


<commit_before>//! Modules are the recommended way to implement larger or more
//! pluggable applications.
pub struct Module;
<commit_msg>Added basic module<commit_after>//! Modules are the recommended way to implement larger or more
//! pluggable applications.

use app::Pencil;

/// Represents a module.
pub struct Module {
    /// The name of the module.
    pub name: String,
    /// The path where your module locates.
    pub root_path: String,
    /// The url prefix.
    pub url_prefix: String,
    /// The folder with static files that should be served at `static_url_path`.
    /// Defaults to the `"static"` folder in the root path of the module.
    pub static_folder: Option<String>,
    /// The url path for the static files on the web.
    pub static_url_path: Option<String>,
    /// The folder that contains the templates that should be used for the module.
    /// Defaults to `''templates''` folder in the root path of the module.
    pub template_folder: Option<String>,
    deferred_functions: Vec<Box<Fn(&mut Pencil)>>,
}

impl Module {
    pub fn new(name: &str, root_path: &str, url_prefix: &str) -> Module {
        Module {
            name: name.to_string(),
            root_path: root_path.to_string(),
            url_prefix: url_prefix.to_string(),
            static_folder: None,
            static_url_path: None,
            template_folder: None,
            deferred_functions: Vec::new(),
        }
    }

    pub fn record<F: Fn(&mut Pencil) + 'static>(&mut self, f: F) {
        self.deferred_functions.push(Box::new(f));
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::{self, Read, Write};

extern crate byteorder;
use byteorder::{LittleEndian, WriteBytesExt};
extern crate idchoppers;
use idchoppers::errors::{Error, Result};
extern crate svg;
use svg::Document;
use svg::node::Node;
use svg::node::element::{Group, Line, Path, Rectangle, Style};
use svg::node::element::path::Data;
extern crate termcolor;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

fn main() {
    match run() {
        Ok(()) => {}
        Err(err) => {
            drop(write_err(err));
        }
    }
}

fn write_err(err: Error) -> Result<()> {
    let mut stderr = StandardStream::stderr(ColorChoice::Auto);
    stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))?;
    write!(&mut stderr, "error: ")?;
    stderr.set_color(&ColorSpec::new())?;
    writeln!(&mut stderr, "{}", err.backtrace())?;
    Ok(())
}

fn run() -> Result<()> {
    let mut buf = Vec::new();
    io::stdin().read_to_end(&mut buf)?;

    let wad = try!(idchoppers::parse_wad(buf.as_slice()));
    println!("found {:?}, {:?}, {:?}", wad.header.identification, wad.header.numlumps, wad.header.infotableofs);
    for map_range in wad.iter_maps() {
        let bare_map = try!(idchoppers::parse_doom_map(&wad, &map_range));
        match bare_map {
            // TODO interesting diagnostic: mix of map formats in the same wad
            idchoppers::BareMap::Doom(map) => {
                println!("{} - Doom format map", map_range.name);
            }
            idchoppers::BareMap::Hexen(map) => {
                println!("{} - Hexen format map", map_range.name);
            }
        }
    }

    // FIXME this also catches F1_START etc, dammit
    /*
    for entry in wad.iter_entries_between("F_START", "F_END") {
        println!("{}", entry.name);
    }
    */

    let mut buffer = Vec::new();
    let mut directory = Vec::new();
    let mut filepos: usize = 0;
    for map_range in wad.iter_maps() {
        let mut bare_map = try!(idchoppers::parse_doom_map(&wad, &map_range));
        if let idchoppers::BareMap::Doom(mut map) = bare_map {
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: 0,
                name: wad.directory[map_range.marker_index].name,
            });

            // Things
            for thing in map.things.iter_mut() {
                thing.y = -thing.y;
                thing.angle = -thing.angle;
                thing.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "THINGS",
            });
            filepos = buffer.len();

            // Lines
            for line in map.lines.iter_mut() {
                std::mem::swap(&mut line.v0, &mut line.v1);
                line.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "LINEDEFS",
            });
            filepos = buffer.len();

            // Sides
            for side in map.sides.iter_mut() {
                side.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "SIDEDEFS",
            });
            filepos = buffer.len();

            // Vertices
            for vertex in map.vertices.iter_mut() {
                vertex.y = -vertex.y;
                vertex.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "VERTEXES",
            });
            filepos = buffer.len();

            // Sectors
            for sector in map.sectors.iter_mut() {
                sector.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "SECTORS",
            });
            filepos = buffer.len();

            println!("{} - Doom format map", map_range.name);
        }
    }
    let mut f = try!(File::create("flipped.wad"));
    try!(f.write("PWAD".as_bytes()));
    try!(f.write_u32::<LittleEndian>(directory.len() as u32));
    try!(f.write_u32::<LittleEndian>((12 + buffer.len()) as u32));
    try!(f.write_all(&buffer[..]));
    for entry in directory.iter() {
        println!("{:?}", entry);
        try!(f.write_u32::<LittleEndian>(entry.filepos));
        try!(f.write_u32::<LittleEndian>(entry.size));
        try!(f.write(entry.name.as_bytes()));
        for _ in entry.name.len() .. 8 {
            try!(f.write(&[0]));
        }
    }

    Ok(())
}

fn write_bare_map_as_svg<L: idchoppers::BareBinaryLine, T: idchoppers::BareBinaryThing>(map: &idchoppers::BareBinaryMap<L, T>) {
    let mut group = Group::new();
    let mut minx;
    let mut miny;
    if let Some(vertex) = map.vertices.first() {
        minx = vertex.x;
        miny = vertex.y;
    }
    else if let Some(thing) = map.things.first() {
        let (x, y) = thing.coords();
        minx = x;
        miny = y;
    }
    else {
        minx = 0;
        miny = 0;
    }
    let mut maxx = minx;
    let mut maxy = miny;

    for vertex in map.vertices.iter() {
        if vertex.x < minx {
            minx = vertex.x;
        }
        if vertex.x > maxx {
            maxx = vertex.x;
        }
        if vertex.y < miny {
            miny = vertex.y;
        }
        if vertex.y > maxy {
            maxy = vertex.y;
        }
    }

    let mut classes = Vec::new();
    for line in map.lines.iter() {
        classes.clear();
        let (v0i, v1i) = line.vertex_indices();
        let v0 = &map.vertices[v0i as usize];
        let v1 = &map.vertices[v1i as usize];
        classes.push("line");
        let (frontid, backid) = line.side_indices();
        if frontid == -1 && backid == -1 {
            classes.push("zero-sided");
        }
        else if frontid == -1 || backid == -1 {
            classes.push("one-sided");
        }
        else {
            classes.push("two-sided");
        }
        if line.has_special() {
            classes.push("has-special");
        }

        group.append(
            Line::new()
            .set("x1", v0.x)
            .set("y1", v0.y)
            .set("x2", v1.x)
            .set("y2", v1.y)
            .set("class", classes.join(" "))
        );
    }

    for thing in map.things.iter() {
        let (x, y) = thing.coords();
        if x < minx {
            minx = x;
        }
        if x > maxx {
            maxx = x;
        }
        if y < miny {
            miny = y;
        }
        if y > maxy {
            maxy = y;
        }
        let (color, radius);
        if let Some(thing_type) = idchoppers::universe::lookup_thing_type(thing.doomednum() as u32) {
            color = match thing_type.category {
                idchoppers::universe::ThingCategory::PlayerStart(_) => "green",
                idchoppers::universe::ThingCategory::Monster => "red",
            };
            radius = thing_type.radius;
        }
        else {
            color = "gray";
            radius = 8;
        }
        group.append(
            Rectangle::new()
            .set("x", x - (radius as i16))
            .set("y", y - (radius as i16))
            .set("width", radius * 2)
            .set("height", radius * 2)
            .set("fill", color));
    }

    for (s, sector) in map.sectors.iter().enumerate() {
        let mut data = Data::new();
        let polys = map.sector_to_polygons(s);
        // TODO wait, hang on, can i have multiple shapes in one path?  i think so...
        for poly in polys.iter() {
            let (first, others) = poly.split_first().unwrap();
            data = data.move_to((first.x, first.y));
            for vertex in others.iter() {
                data = data.line_to((vertex.x, vertex.y));
            }
            data = data.line_to((first.x, first.y));
        }

        let mut path = Path::new().set("d", data);
        if sector.sector_tag != 0 {
            path.assign("data-sector-tag", sector.sector_tag);
        }
        classes.clear();
        classes.push("sector");
        if sector.sector_type == 9 {
            classes.push("secret");
        }
        path.assign("class", classes.join(" "));
        group.append(path);
    }

    // Doom's y-axis points up, but SVG's points down.  Rather than mucking with coordinates
    // everywhere we write them, just flip the entire map.  (WebKit doesn't support "transform" on
    // the <svg> element, hence the need for this group.)
    group.assign("transform", "scale(1 -1)");
    let doc = Document::new()
        .set("viewBox", (minx, -maxy, maxx - minx, maxy - miny))
        .add(Style::new(include_str!("map-svg.css")))
        .add(group);
    svg::save("idchoppers-temp.svg", &doc).unwrap();
}
<commit_msg>Remove backtrace()<commit_after>use std::fs::File;
use std::io::{self, Read, Write};

extern crate byteorder;
use byteorder::{LittleEndian, WriteBytesExt};
extern crate idchoppers;
use idchoppers::errors::{Error, Result};
extern crate svg;
use svg::Document;
use svg::node::Node;
use svg::node::element::{Group, Line, Path, Rectangle, Style};
use svg::node::element::path::Data;
extern crate termcolor;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};

fn main() {
    match run() {
        Ok(()) => {}
        Err(err) => {
            drop(write_err(err));
        }
    }
}

fn write_err(err: Error) -> Result<()> {
    let mut stderr = StandardStream::stderr(ColorChoice::Auto);
    stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true))?;
    write!(&mut stderr, "error: ")?;
    stderr.set_color(&ColorSpec::new())?;
    writeln!(&mut stderr, "{}", err)?;
    Ok(())
}

fn run() -> Result<()> {
    let mut buf = Vec::new();
    io::stdin().read_to_end(&mut buf)?;

    let wad = try!(idchoppers::parse_wad(buf.as_slice()));
    println!("found {:?}, {:?}, {:?}", wad.header.identification, wad.header.numlumps, wad.header.infotableofs);
    for map_range in wad.iter_maps() {
        let bare_map = try!(idchoppers::parse_doom_map(&wad, &map_range));
        match bare_map {
            // TODO interesting diagnostic: mix of map formats in the same wad
            idchoppers::BareMap::Doom(map) => {
                println!("{} - Doom format map", map_range.name);
            }
            idchoppers::BareMap::Hexen(map) => {
                println!("{} - Hexen format map", map_range.name);
            }
        }
    }

    // FIXME this also catches F1_START etc, dammit
    /*
    for entry in wad.iter_entries_between("F_START", "F_END") {
        println!("{}", entry.name);
    }
    */

    let mut buffer = Vec::new();
    let mut directory = Vec::new();
    let mut filepos: usize = 0;
    for map_range in wad.iter_maps() {
        let mut bare_map = try!(idchoppers::parse_doom_map(&wad, &map_range));
        if let idchoppers::BareMap::Doom(mut map) = bare_map {
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: 0,
                name: wad.directory[map_range.marker_index].name,
            });

            // Things
            for thing in map.things.iter_mut() {
                thing.y = -thing.y;
                thing.angle = -thing.angle;
                thing.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "THINGS",
            });
            filepos = buffer.len();

            // Lines
            for line in map.lines.iter_mut() {
                std::mem::swap(&mut line.v0, &mut line.v1);
                line.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "LINEDEFS",
            });
            filepos = buffer.len();

            // Sides
            for side in map.sides.iter_mut() {
                side.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "SIDEDEFS",
            });
            filepos = buffer.len();

            // Vertices
            for vertex in map.vertices.iter_mut() {
                vertex.y = -vertex.y;
                vertex.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "VERTEXES",
            });
            filepos = buffer.len();

            // Sectors
            for sector in map.sectors.iter_mut() {
                sector.write_to(&mut buffer);
            }
            directory.push(idchoppers::BareWADDirectoryEntry{
                filepos: (filepos + 12) as u32,
                size: (buffer.len() - filepos) as u32,
                name: "SECTORS",
            });
            filepos = buffer.len();

            println!("{} - Doom format map", map_range.name);
        }
    }
    let mut f = try!(File::create("flipped.wad"));
    try!(f.write("PWAD".as_bytes()));
    try!(f.write_u32::<LittleEndian>(directory.len() as u32));
    try!(f.write_u32::<LittleEndian>((12 + buffer.len()) as u32));
    try!(f.write_all(&buffer[..]));
    for entry in directory.iter() {
        println!("{:?}", entry);
        try!(f.write_u32::<LittleEndian>(entry.filepos));
        try!(f.write_u32::<LittleEndian>(entry.size));
        try!(f.write(entry.name.as_bytes()));
        for _ in entry.name.len() .. 8 {
            try!(f.write(&[0]));
        }
    }

    Ok(())
}

fn write_bare_map_as_svg<L: idchoppers::BareBinaryLine, T: idchoppers::BareBinaryThing>(map: &idchoppers::BareBinaryMap<L, T>) {
    let mut group = Group::new();
    let mut minx;
    let mut miny;
    if let Some(vertex) = map.vertices.first() {
        minx = vertex.x;
        miny = vertex.y;
    }
    else if let Some(thing) = map.things.first() {
        let (x, y) = thing.coords();
        minx = x;
        miny = y;
    }
    else {
        minx = 0;
        miny = 0;
    }
    let mut maxx = minx;
    let mut maxy = miny;

    for vertex in map.vertices.iter() {
        if vertex.x < minx {
            minx = vertex.x;
        }
        if vertex.x > maxx {
            maxx = vertex.x;
        }
        if vertex.y < miny {
            miny = vertex.y;
        }
        if vertex.y > maxy {
            maxy = vertex.y;
        }
    }

    let mut classes = Vec::new();
    for line in map.lines.iter() {
        classes.clear();
        let (v0i, v1i) = line.vertex_indices();
        let v0 = &map.vertices[v0i as usize];
        let v1 = &map.vertices[v1i as usize];
        classes.push("line");
        let (frontid, backid) = line.side_indices();
        if frontid == -1 && backid == -1 {
            classes.push("zero-sided");
        }
        else if frontid == -1 || backid == -1 {
            classes.push("one-sided");
        }
        else {
            classes.push("two-sided");
        }
        if line.has_special() {
            classes.push("has-special");
        }

        group.append(
            Line::new()
            .set("x1", v0.x)
            .set("y1", v0.y)
            .set("x2", v1.x)
            .set("y2", v1.y)
            .set("class", classes.join(" "))
        );
    }

    for thing in map.things.iter() {
        let (x, y) = thing.coords();
        if x < minx {
            minx = x;
        }
        if x > maxx {
            maxx = x;
        }
        if y < miny {
            miny = y;
        }
        if y > maxy {
            maxy = y;
        }
        let (color, radius);
        if let Some(thing_type) = idchoppers::universe::lookup_thing_type(thing.doomednum() as u32) {
            color = match thing_type.category {
                idchoppers::universe::ThingCategory::PlayerStart(_) => "green",
                idchoppers::universe::ThingCategory::Monster => "red",
            };
            radius = thing_type.radius;
        }
        else {
            color = "gray";
            radius = 8;
        }
        group.append(
            Rectangle::new()
            .set("x", x - (radius as i16))
            .set("y", y - (radius as i16))
            .set("width", radius * 2)
            .set("height", radius * 2)
            .set("fill", color));
    }

    for (s, sector) in map.sectors.iter().enumerate() {
        let mut data = Data::new();
        let polys = map.sector_to_polygons(s);
        // TODO wait, hang on, can i have multiple shapes in one path?  i think so...
        for poly in polys.iter() {
            let (first, others) = poly.split_first().unwrap();
            data = data.move_to((first.x, first.y));
            for vertex in others.iter() {
                data = data.line_to((vertex.x, vertex.y));
            }
            data = data.line_to((first.x, first.y));
        }

        let mut path = Path::new().set("d", data);
        if sector.sector_tag != 0 {
            path.assign("data-sector-tag", sector.sector_tag);
        }
        classes.clear();
        classes.push("sector");
        if sector.sector_type == 9 {
            classes.push("secret");
        }
        path.assign("class", classes.join(" "));
        group.append(path);
    }

    // Doom's y-axis points up, but SVG's points down.  Rather than mucking with coordinates
    // everywhere we write them, just flip the entire map.  (WebKit doesn't support "transform" on
    // the <svg> element, hence the need for this group.)
    group.assign("transform", "scale(1 -1)");
    let doc = Document::new()
        .set("viewBox", (minx, -maxy, maxx - minx, maxy - miny))
        .add(Style::new(include_str!("map-svg.css")))
        .add(group);
    svg::save("idchoppers-temp.svg", &doc).unwrap();
}
<|endoftext|>