<commit_before>use traitdefs::{Float, Point, Vector};
use std::convert::{AsRef, AsMut, From};
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;

use super::traits::BezCurve;
use super::lerp;

/// A struct that contains range information for slicing, used for slicing into the global factor
/// vector. The reason this is used instead of stdlib's `Range` struct is that `Range` does not
/// implement Copy, which means we have to use `RefCell`s instead of `Cell`s for interior mutability.
#[derive(Copy, Clone)]
struct RangeSlice {
    start: usize,
    end: usize
}

impl RangeSlice {
    #[inline]
    fn new(start: usize, end: usize) -> RangeSlice {
        RangeSlice {
            start: start,
            end: end
        }
    }

    fn len(&self) -> usize {
        self.end - self.start
    }
}

fn combination(n: u64, k: u64) -> u64 {
    factorial(n) / (factorial(k) * factorial(n - k))
}

fn factorial(mut n: u64) -> u64 {
    let mut accumulator: u64 = 1;
    while n > 0 {
        accumulator = accumulator.checked_mul(n).expect("Attempted to create Bézier curve with combination that overflow u64; decrease curve order");
        n -= 1;
    }
    accumulator
}

/// Gets the index of the bezier factors inside of FACTORS global.
fn order_index(order: usize) -> usize {
    (order*order+order)/2
}

thread_local!{
    static FACTORS: RefCell<(isize, Vec<u64>)> = RefCell::new((-1, Vec::with_capacity(order_index(20+1))))
}

/// Returns a RangeSlice for FACTORS with the appropriate factors for the given order. Calculates
/// factors if necessary.
fn factors(order: usize) -> RangeSlice {
    FACTORS.with(|f| {
        let max_order = f.borrow().0;
        if order as isize > max_order {
            let mut f = f.borrow_mut();
            f.0 = order as isize;

            // Because max_order defines the maximum current order, we need to increment it in order to avoid
            // re-pushing the current max order. Also, we increment the upper bound, `order`, so that the
            // calculations include `order`.
            for n in (max_order+1) as usize..order+1 {
                for k in 0..n+1 {
                    f.1.push(combination(n as u64, k as u64));
                }
            }
        }

        let order_index = order_index(order);
        RangeSlice::new(order_index, order_index+order+1)
    })
}

/// An n-order bezier polynomial
#[derive(Clone)]
pub struct NBezPoly<F, C = Vec<F>>
        where F: Float,
              C: AsRef<[F]> {
    points: C,
    factors: Cell<RangeSlice>,
    dfactors: Cell<RangeSlice>,
    phantom: PhantomData<F>
}

impl<F, C> From<C> for NBezPoly<F, C>
        where F: Float,
              C: AsRef<[F]> {
    fn from(container: C) -> NBezPoly<F, C> {
        NBezPoly::from_container(container)
    }
}

impl<F, C> NBezPoly<F, C>
        where F: Float,
              C: AsRef<[F]> {
    #[inline]
    pub fn from_container(points: C) -> NBezPoly<F, C> {
        if points.as_ref().len() >= 22 {
            panic!("Cannot create Bézier polynomials with an order >= 21")
        }

        NBezPoly {
            points: points,
            factors: Cell::new(RangeSlice::new(0, 0)),
            dfactors: Cell::new(RangeSlice::new(0, 0)),
            phantom: PhantomData
        }
    }

    #[inline]
    pub fn unwrap(self) -> C {
        self.points
    }

    unsafe fn interp_unchecked<I: Iterator<Item=F>>(t: F, factors: RangeSlice, iter: I) -> F {
        let t1 = F::from_f32(1.0).unwrap() - t;
        let order = factors.len() - 1;
        let mut acc = F::from_f32(0.0).unwrap();
        let mut factor = 0;

        FACTORS.with(|fs| {
            let fs = fs.borrow();
            for point in iter {
                acc = acc + t.powi(factor as i32) *
                            t1.powi((order-factor) as i32) *
                            point *
                            F::from_u64(fs.1[factors.start + factor]).unwrap();
                factor += 1;
            }
        });
        acc
    }

    unsafe fn slope_unchecked<I: Iterator<Item=F>>(t: F, dfactors: RangeSlice, mut iter: I) -> F {
        let t1 = F::from_f32(1.0).unwrap() - t;
        let order = dfactors.len() - 1;
        let mut acc = F::from_f32(0.0).unwrap();
        let mut factor = 0;
        let mut point_next = iter.next().unwrap();

        FACTORS.with(|fs| {
            let fs = fs.borrow();
            for point in iter {
                acc = acc + t.powi(factor as i32) *
                            t1.powi((order-factor) as i32) *
                            (point_next - point) *
                            F::from_u64(fs.1[dfactors.start + factor] * (order + 1) as u64).unwrap();
                point_next = point;
                factor += 1;
            }
        });
        acc
    }
}

impl<F, C> BezCurve<F> for NBezPoly<F, C>
        where F: Float,
              C: AsRef<[F]> {
    type Point = F;
    type Vector = F;
    type Elevated = NBezPoly<F>;

    fn from_slice(_: &[F]) -> Option<NBezPoly<F, C>> {
        None
    }

    fn interp_unbounded(&self, t: F) -> F {
        let points = self.points.as_ref();
        if self.factors.get().len() != self.order() {
            self.factors.set(factors(self.order()))
        }

        unsafe{ NBezPoly::<_, &[_]>::interp_unchecked(t, self.factors.get(), points.iter().map(|f| *f)) }
    }

    fn slope_unbounded(&self, t: F) -> F {
        let points = self.points.as_ref();
        let order = self.order() - 1;
        if self.dfactors.get().len() != order {
            self.dfactors.set(factors(order))
        }

        unsafe{ NBezPoly::<_, &[_]>::slope_unchecked(t, self.dfactors.get(), points.iter().map(|f| *f)) }
    }

    fn elevate(&self) -> NBezPoly<F> {
        let points = self.points.as_ref();
        let order = self.order() + 1;
        let order_f = F::from_usize(order).unwrap();

        // Elevated points
        let mut el_points = Vec::with_capacity(order + 1);
        el_points.push(points[0]);

        let mut prev_p = points[0];
        for (i, p) in points.iter().map(|p| *p).enumerate().skip(1) {
            el_points.push(lerp(prev_p, p, F::from_usize(i).unwrap()/order_f));

            prev_p = p;
        }

        el_points.push(points[self.order()]);
        NBezPoly::from_container(el_points)
    }

    fn order(&self) -> usize {
        self.points.as_ref().len()-1
    }

    fn order_static() -> Option<usize> {
        None
    }
}

impl<F, C> AsRef<C> for NBezPoly<F, C>
        where F: Float,
              C: AsRef<[F]> {
    fn as_ref(&self) -> &C {
        &self.points
    }
}

impl<F, C> AsMut<C> for NBezPoly<F, C>
        where F: Float,
              C: AsRef<[F]> {
    fn as_mut(&mut self) -> &mut C {
        &mut self.points
    }
}

#[derive(Clone)]
pub struct NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F>,
              V: Vector<F, P> {
    points: C,
    factors: Cell<RangeSlice>,
    dfactors: Cell<RangeSlice>,

    float_phantom: PhantomData<F>,
    point_phantom: PhantomData<P>,
    vector_phantom: PhantomData<V>
}

impl<P, V, F, C> From<C> for NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F>,
              V: Vector<F, P> {
    fn from(container: C) -> NBez<P, V, F, C> {
        NBez::from_container(container)
    }
}

impl<P, V, F, C> NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F>,
              V: Vector<F, P> {
    /// Create a new `NBez` curve from a container
    pub fn from_container(container: C) -> NBez<P, V, F, C> {
        if container.as_ref().len() >= 22 {
            panic!("Cannot create Bézier curves with an order >= 21")
        }

        NBez {
            points: container,
            factors: Cell::new(RangeSlice::new(0, 0)),
            dfactors: Cell::new(RangeSlice::new(0, 0)),

            float_phantom: PhantomData,
            point_phantom: PhantomData,
            vector_phantom: PhantomData
        }
    }

    /// Get the wrapped container, destroying the `NBez` curve
    pub fn unwrap(self) -> C {
        self.points
    }
}

impl<P, V, F, C> BezCurve<F> for NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F> + ::std::fmt::Debug,
              V: Vector<F, P> {
    type Point = P;
    type Vector = V;
    type Elevated = NBez<P, V, F, Vec<P>>;

    fn from_slice(_: &[P]) -> Option<NBez<P, V, F, C>> {
        None
    }

    fn interp_unbounded(&self, t: F) -> P {
        let points = self.points.as_ref();

        // Initialize a point by cloning one from the point list
        let mut point = points[0].clone();

        // If the factors aren't correct for the current order, recompute factors
        if self.factors.get().len() != self.order() {
            self.factors.set(factors(self.order()))
        }

        // Iterate over all elements of the point and set them to the interpolated value
        for (i, f) in point.as_mut().iter_mut().enumerate() {
            let iter = points.iter().map(|p| p.as_ref()[i]);
            *f = unsafe{ NBezPoly::<_, &[_]>::interp_unchecked(t, self.factors.get(), iter) };
        }

        point
    }

    fn slope_unbounded(&self, t: F) -> V {
        let points = self.points.as_ref();

        // Initialize a vector by cloning it from the point list and converting it into a vector
        let mut vector: V = points[0].clone().into();
        let order = self.order() - 1;

        if self.dfactors.get().len() != order {
            self.dfactors.set(factors(order))
        }

        // Iterate over all elements of the vector and set them to the interpolated value
        for (i, f) in vector.as_mut().iter_mut().enumerate() {
            let iter = points.iter().map(|p| p.as_ref()[i]);
            *f = unsafe{ NBezPoly::<_, &[_]>::slope_unchecked(t, self.dfactors.get(), iter) };
        }

        vector
    }

    fn elevate(&self) -> NBez<P, V, F, Vec<P>> {
        let points = self.points.as_ref();
        let order = F::from_usize(self.order() + 1).unwrap();

        let mut el_points = Vec::with_capacity(points.len() + 1);
        el_points.push(points[0].clone());

        let mut prev_p = points[0].clone();
        for (i, p) in points.iter().enumerate().skip(1) {
            // Push an arbitrary point onto the vector for modification
            el_points.push(p.clone());

            // Iterate over each dimension of the current (c) and previous (r) point and interpolate
            // between them
            for (d, (c, r)) in p.as_ref().iter().zip(prev_p.as_ref().iter()).map(|(c, r)| (*c, *r)).enumerate() {
                el_points[i].as_mut()[d] = lerp(r, c, F::from_usize(i).unwrap()/order);
            }
            prev_p = p.clone();
        }
        el_points.push(points[points.len()-1].clone());
        NBez::from_container(el_points)
    }

    fn order(&self) -> usize {
        self.points.as_ref().len() - 1
    }

    fn order_static() -> Option<usize> {
        None
    }
}

impl<P, V, F, C> AsRef<C> for NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F>,
              V: Vector<F, P> {
    fn as_ref(&self) -> &C {
        &self.points
    }
}

impl<P, V, F, C> AsMut<C> for NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F>,
              V: Vector<F, P> {
    fn as_mut(&mut self) -> &mut C {
        &mut self.points
    }
}<commit_msg>Fixed bug that would cause NBez factor recalculation upon each interpolation<commit_after>use traitdefs::{Float, Point, Vector};
use std::convert::{AsRef, AsMut, From};
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;

use super::traits::BezCurve;
use super::lerp;

/// A struct that contains range information for slicing, used for slicing into the global factor
/// vector. The reason this is used instead of stdlib's `Range` struct is that `Range` does not
/// implement Copy, which means we have to use `RefCell`s instead of `Cell`s for interior mutability.
#[derive(Copy, Clone)]
struct RangeSlice {
    start: usize,
    end: usize
}

impl RangeSlice {
    #[inline]
    fn new(start: usize, end: usize) -> RangeSlice {
        RangeSlice {
            start: start,
            end: end
        }
    }

    fn len(&self) -> usize {
        self.end - self.start
    }
}

fn combination(n: u64, k: u64) -> u64 {
    factorial(n) / (factorial(k) * factorial(n - k))
}

fn factorial(mut n: u64) -> u64 {
    let mut accumulator: u64 = 1;
    while n > 0 {
        accumulator = accumulator.checked_mul(n).expect("Attempted to create Bézier curve with combination that overflow u64; decrease curve order");
        n -= 1;
    }
    accumulator
}

/// Gets the index of the bezier factors inside of FACTORS global.
fn order_index(order: usize) -> usize {
    (order*order+order)/2
}

thread_local!{
    static FACTORS: RefCell<(isize, Vec<u64>)> = RefCell::new((-1, Vec::with_capacity(order_index(20+1))))
}

/// Returns a RangeSlice for FACTORS with the appropriate factors for the given order. Calculates
/// factors if necessary.
fn factors(order: usize) -> RangeSlice {
    FACTORS.with(|f| {
        let max_order = f.borrow().0;
        if order as isize > max_order {
            let mut f = f.borrow_mut();
            f.0 = order as isize;

            // Because max_order defines the maximum current order, we need to increment it in order to avoid
            // re-pushing the current max order. Also, we increment the upper bound, `order`, so that the
            // calculations include `order`.
            for n in (max_order+1) as usize..order+1 {
                for k in 0..n+1 {
                    f.1.push(combination(n as u64, k as u64));
                }
            }
        }

        let order_index = order_index(order);
        RangeSlice::new(order_index, order_index+order+1)
    })
}

/// An n-order bezier polynomial
#[derive(Clone)]
pub struct NBezPoly<F, C = Vec<F>>
        where F: Float,
              C: AsRef<[F]> {
    points: C,
    factors: Cell<RangeSlice>,
    dfactors: Cell<RangeSlice>,
    phantom: PhantomData<F>
}

impl<F, C> From<C> for NBezPoly<F, C>
        where F: Float,
              C: AsRef<[F]> {
    fn from(container: C) -> NBezPoly<F, C> {
        NBezPoly::from_container(container)
    }
}

impl<F, C> NBezPoly<F, C>
        where F: Float,
              C: AsRef<[F]> {
    #[inline]
    pub fn from_container(points: C) -> NBezPoly<F, C> {
        if points.as_ref().len() >= 22 {
            panic!("Cannot create Bézier polynomials with an order >= 21")
        }

        NBezPoly {
            points: points,
            factors: Cell::new(RangeSlice::new(0, 0)),
            dfactors: Cell::new(RangeSlice::new(0, 0)),
            phantom: PhantomData
        }
    }

    #[inline]
    pub fn unwrap(self) -> C {
        self.points
    }

    unsafe fn interp_unchecked<I: Iterator<Item=F>>(t: F, factors: RangeSlice, iter: I) -> F {
        let t1 = F::from_f32(1.0).unwrap() - t;
        let order = factors.len() - 1;
        let mut acc = F::from_f32(0.0).unwrap();
        let mut factor = 0;

        FACTORS.with(|fs| {
            let fs = fs.borrow();
            for point in iter {
                acc = acc + t.powi(factor as i32) *
                            t1.powi((order-factor) as i32) *
                            point *
                            F::from_u64(fs.1[factors.start + factor]).unwrap();
                factor += 1;
            }
        });
        acc
    }

    unsafe fn slope_unchecked<I: Iterator<Item=F>>(t: F, dfactors: RangeSlice, mut iter: I) -> F {
        let t1 = F::from_f32(1.0).unwrap() - t;
        let order = dfactors.len() - 1;
        let mut acc = F::from_f32(0.0).unwrap();
        let mut factor = 0;
        let mut point_next = iter.next().unwrap();

        FACTORS.with(|fs| {
            let fs = fs.borrow();
            for point in iter {
                acc = acc + t.powi(factor as i32) *
                            t1.powi((order-factor) as i32) *
                            (point_next - point) *
                            F::from_u64(fs.1[dfactors.start + factor] * (order + 1) as u64).unwrap();
                point_next = point;
                factor += 1;
            }
        });
        acc
    }
}

impl<F, C> BezCurve<F> for NBezPoly<F, C>
        where F: Float,
              C: AsRef<[F]> {
    type Point = F;
    type Vector = F;
    type Elevated = NBezPoly<F>;

    fn from_slice(_: &[F]) -> Option<NBezPoly<F, C>> {
        None
    }

    fn interp_unbounded(&self, t: F) -> F {
        let points = self.points.as_ref();
        if self.factors.get().len() != self.order() + 1 {
            self.factors.set(factors(self.order()))
        }

        unsafe{ NBezPoly::<_, &[_]>::interp_unchecked(t, self.factors.get(), points.iter().map(|f| *f)) }
    }

    fn slope_unbounded(&self, t: F) -> F {
        let points = self.points.as_ref();
        let order = self.order() - 1;
        if self.dfactors.get().len() != order + 1 {
            self.dfactors.set(factors(order))
        }

        unsafe{ NBezPoly::<_, &[_]>::slope_unchecked(t, self.dfactors.get(), points.iter().map(|f| *f)) }
    }

    fn elevate(&self) -> NBezPoly<F> {
        let points = self.points.as_ref();
        let order = self.order() + 1;
        let order_f = F::from_usize(order).unwrap();

        // Elevated points
        let mut el_points = Vec::with_capacity(order + 1);
        el_points.push(points[0]);

        let mut prev_p = points[0];
        for (i, p) in points.iter().map(|p| *p).enumerate().skip(1) {
            el_points.push(lerp(prev_p, p, F::from_usize(i).unwrap()/order_f));

            prev_p = p;
        }

        el_points.push(points[self.order()]);
        NBezPoly::from_container(el_points)
    }

    fn order(&self) -> usize {
        self.points.as_ref().len()-1
    }

    fn order_static() -> Option<usize> {
        None
    }
}

impl<F, C> AsRef<C> for NBezPoly<F, C>
        where F: Float,
              C: AsRef<[F]> {
    fn as_ref(&self) -> &C {
        &self.points
    }
}

impl<F, C> AsMut<C> for NBezPoly<F, C>
        where F: Float,
              C: AsRef<[F]> {
    fn as_mut(&mut self) -> &mut C {
        &mut self.points
    }
}

#[derive(Clone)]
pub struct NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F>,
              V: Vector<F, P> {
    points: C,
    factors: Cell<RangeSlice>,
    dfactors: Cell<RangeSlice>,

    float_phantom: PhantomData<F>,
    point_phantom: PhantomData<P>,
    vector_phantom: PhantomData<V>
}

impl<P, V, F, C> From<C> for NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F>,
              V: Vector<F, P> {
    fn from(container: C) -> NBez<P, V, F, C> {
        NBez::from_container(container)
    }
}

impl<P, V, F, C> NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F>,
              V: Vector<F, P> {
    /// Create a new `NBez` curve from a container
    pub fn from_container(container: C) -> NBez<P, V, F, C> {
        if container.as_ref().len() >= 22 {
            panic!("Cannot create Bézier curves with an order >= 21")
        }

        NBez {
            points: container,
            factors: Cell::new(RangeSlice::new(0, 0)),
            dfactors: Cell::new(RangeSlice::new(0, 0)),

            float_phantom: PhantomData,
            point_phantom: PhantomData,
            vector_phantom: PhantomData
        }
    }

    /// Get the wrapped container, destroying the `NBez` curve
    pub fn unwrap(self) -> C {
        self.points
    }
}

impl<P, V, F, C> BezCurve<F> for NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F> + ::std::fmt::Debug,
              V: Vector<F, P> {
    type Point = P;
    type Vector = V;
    type Elevated = NBez<P, V, F, Vec<P>>;

    fn from_slice(_: &[P]) -> Option<NBez<P, V, F, C>> {
        None
    }

    fn interp_unbounded(&self, t: F) -> P {
        let points = self.points.as_ref();

        // Initialize a point by cloning one from the point list
        let mut point = points[0].clone();

        // If the factors aren't correct for the current order, recompute factors
        if self.factors.get().len() != self.order() + 1 {
            self.factors.set(factors(self.order()))
        }

        // Iterate over all elements of the point and set them to the interpolated value
        for (i, f) in point.as_mut().iter_mut().enumerate() {
            let iter = points.iter().map(|p| p.as_ref()[i]);
            *f = unsafe{ NBezPoly::<_, &[_]>::interp_unchecked(t, self.factors.get(), iter) };
        }

        point
    }

    fn slope_unbounded(&self, t: F) -> V {
        let points = self.points.as_ref();

        // Initialize a vector by cloning it from the point list and converting it into a vector
        let mut vector: V = points[0].clone().into();
        let order = self.order() - 1;

        if self.dfactors.get().len() != order + 1{
            self.dfactors.set(factors(order))
        }

        // Iterate over all elements of the vector and set them to the interpolated value
        for (i, f) in vector.as_mut().iter_mut().enumerate() {
            let iter = points.iter().map(|p| p.as_ref()[i]);
            *f = unsafe{ NBezPoly::<_, &[_]>::slope_unchecked(t, self.dfactors.get(), iter) };
        }

        vector
    }

    fn elevate(&self) -> NBez<P, V, F, Vec<P>> {
        let points = self.points.as_ref();
        let order = F::from_usize(self.order() + 1).unwrap();

        let mut el_points = Vec::with_capacity(points.len() + 1);
        el_points.push(points[0].clone());

        let mut prev_p = points[0].clone();
        for (i, p) in points.iter().enumerate().skip(1) {
            // Push an arbitrary point onto the vector for modification
            el_points.push(p.clone());

            // Iterate over each dimension of the current (c) and previous (r) point and interpolate
            // between them
            for (d, (c, r)) in p.as_ref().iter().zip(prev_p.as_ref().iter()).map(|(c, r)| (*c, *r)).enumerate() {
                el_points[i].as_mut()[d] = lerp(r, c, F::from_usize(i).unwrap()/order);
            }
            prev_p = p.clone();
        }
        el_points.push(points[points.len()-1].clone());
        NBez::from_container(el_points)
    }

    fn order(&self) -> usize {
        self.points.as_ref().len() - 1
    }

    fn order_static() -> Option<usize> {
        None
    }
}

impl<P, V, F, C> AsRef<C> for NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F>,
              V: Vector<F, P> {
    fn as_ref(&self) -> &C {
        &self.points
    }
}

impl<P, V, F, C> AsMut<C> for NBez<P, V, F, C>
        where F: Float,
              C: AsRef<[P]>,
              P: Point<F>,
              V: Vector<F, P> {
    fn as_mut(&mut self) -> &mut C {
        &mut self.points
    }
}<|endoftext|>


<commit_before>//! Safe-interface corresponding to out-place transform

use super::aligned_vec::*;
use super::error::*;
use super::plan::*;

use ndarray::*;
use ndarray_linalg::Scalar;

/// Safe-interface corresponding to out-place transform
///
/// FFTW interface modifies an array in `fftw_execute` function
/// which does not takes the array as its arguments.
/// It is not compatible to the programing model of safe Rust.
/// `Pair` interface composes the array and plan to manage
/// mutability in the safe Rust way.
pub struct Pair<A, B, D: Dimension>
where
    A: Scalar,
    B: Scalar<Real = A::Real>,
{
    pub a: AlignedVec<A>,
    pub b: AlignedVec<B>,
    pub(crate) size: D,
    pub(crate) forward: RawPlan,
    pub(crate) backward: RawPlan,
    // normaliztion factors
    // `None` means no normaliztion
    pub(crate) factor_f: Option<A::Real>,
    pub(crate) factor_b: Option<B::Real>,
}

impl<A, B, D: Dimension> Pair<A, B, D>
where
    A: Scalar,
    B: Scalar<Real = A::Real>,
{
    pub fn size(&self) -> D {
        self.size.clone()
    }

    /// Executes copy the input to `a`, forward transform,
    /// and returns the result `b` as a reference
    pub fn forward(&mut self, input: &[A]) -> &mut [B] {
        self.a.copy_from_slice(input);
        self.exec_forward();
        if let Some(n) = self.factor_f.as_ref() {
            for val in self.b.iter_mut() {
                *val = val.mul_real(*n);
            }
        }
        &mut self.b
    }

    /// Execute copy to pair, forward transform,
    /// and returns a reference of the result.
    pub fn backward(&mut self, input: &[B]) -> &mut [A]
    where
        A: Scalar,
        B: Scalar,
    {
        self.b.copy_from_slice(input);
        self.exec_backward();
        if let Some(n) = self.factor_b.as_ref() {
            for val in self.a.iter_mut() {
                *val = val.mul_real(*n);
            }
        }
        &mut self.a
    }

    /// Execute a forward transform (`a` to `b`)
    pub fn exec_forward(&mut self) {
        unsafe { self.forward.execute() }
    }

    /// Execute a backward transform (`b` to `a`)
    pub fn exec_backward(&mut self) {
        unsafe { self.backward.execute() }
    }

    pub(crate) fn null_checked(self) -> Result<Self> {
        if self.forward.is_null() || self.backward.is_null() {
            Err(InvalidPlanError {}.into())
        } else {
            Ok(self)
        }
    }
}

/// Create a `Pair` from a setting struct e.g. `R2C1D`.
pub trait ToPair<A, B>
where
    A: Scalar,
    B: Scalar<Real = A::Real>,
{
    type Dim: Dimension;
    /// Generate `Pair` from a setting struct
    fn to_pair(&self) -> Result<Pair<A, B, Self::Dim>>;
}
<commit_msg>Add ndarray::ArrayView interface<commit_after>//! Safe-interface corresponding to out-place transform

use super::aligned_vec::*;
use super::error::*;
use super::plan::*;

use ndarray::*;
use ndarray_linalg::Scalar;

/// Safe-interface corresponding to out-place transform
///
/// FFTW interface modifies an array in `fftw_execute` function
/// which does not takes the array as its arguments.
/// It is not compatible to the programing model of safe Rust.
/// `Pair` interface composes the array and plan to manage
/// mutability in the safe Rust way.
pub struct Pair<A, B, D: Dimension>
where
    A: Scalar,
    B: Scalar<Real = A::Real>,
{
    pub a: AlignedVec<A>,
    pub b: AlignedVec<B>,
    pub(crate) size: D,
    pub(crate) forward: RawPlan,
    pub(crate) backward: RawPlan,
    // normaliztion factors
    // `None` means no normaliztion
    pub(crate) factor_f: Option<A::Real>,
    pub(crate) factor_b: Option<B::Real>,
}

impl<A, B> Pair<A, B, Ix1>
where
    A: Scalar,
    B: Scalar<Real = A::Real>,
{
    /// Execute `Pair::forward` with `ndarray::ArrayView`
    pub fn forward_array<'a, 'b>(&'a mut self, input: ArrayView1<'b, A>) -> ArrayViewMut1<'a, B> {
        let sl = self.forward(input.as_slice().unwrap());
        ArrayViewMut::from(sl)
    }

    /// Execute `Pair::backward` with `ndarray::ArrayView`
    pub fn backward_array<'a, 'b>(&'a mut self, input: ArrayView1<'b, B>) -> ArrayViewMut1<'a, A> {
        let sl = self.backward(input.as_slice().unwrap());
        ArrayViewMut::from(sl)
    }
}

impl<A, B, D: Dimension> Pair<A, B, D>
where
    A: Scalar,
    B: Scalar<Real = A::Real>,
{
    pub fn size(&self) -> D {
        self.size.clone()
    }

    /// Executes copy the input to `a`, forward transform,
    /// and returns the result `b` as a reference
    pub fn forward(&mut self, input: &[A]) -> &mut [B] {
        self.a.copy_from_slice(input);
        self.exec_forward();
        if let Some(n) = self.factor_f.as_ref() {
            for val in self.b.iter_mut() {
                *val = val.mul_real(*n);
            }
        }
        &mut self.b
    }

    /// Execute copy to pair, forward transform,
    /// and returns a reference of the result.
    pub fn backward(&mut self, input: &[B]) -> &mut [A]
    where
        A: Scalar,
        B: Scalar,
    {
        self.b.copy_from_slice(input);
        self.exec_backward();
        if let Some(n) = self.factor_b.as_ref() {
            for val in self.a.iter_mut() {
                *val = val.mul_real(*n);
            }
        }
        &mut self.a
    }

    /// Execute a forward transform (`a` to `b`)
    pub fn exec_forward(&mut self) {
        unsafe { self.forward.execute() }
    }

    /// Execute a backward transform (`b` to `a`)
    pub fn exec_backward(&mut self) {
        unsafe { self.backward.execute() }
    }

    pub(crate) fn null_checked(self) -> Result<Self> {
        if self.forward.is_null() || self.backward.is_null() {
            Err(InvalidPlanError {}.into())
        } else {
            Ok(self)
        }
    }
}

/// Create a `Pair` from a setting struct e.g. `R2C1D`.
pub trait ToPair<A, B>
where
    A: Scalar,
    B: Scalar<Real = A::Real>,
{
    type Dim: Dimension;
    /// Generate `Pair` from a setting struct
    fn to_pair(&self) -> Result<Pair<A, B, Self::Dim>>;
}
<|endoftext|>


<commit_before>#![crate_name = "rboy"]
#![license    = "MIT" ]
#![crate_type = "lib" ]
#![feature(phase)]

#[phase(plugin, link)] extern crate log;
                       extern crate spinlock;
                       extern crate time;
#[cfg(test)]           extern crate test;

pub use keypad::{KeypadKey, Right, Left, Up, Down, A, B, Select, Start};

pub mod device;

mod cpu;
mod gbmode;
mod gpu;
mod keypad;
mod mbc;
mod mmu;
mod register;
mod serial;
mod sound;
mod timer;
mod util;
<commit_msg>Forgot to remove spinlock dependency<commit_after>#![crate_name = "rboy"]
#![license    = "MIT" ]
#![crate_type = "lib" ]
#![feature(phase)]

#[phase(plugin, link)] extern crate log;
                       extern crate time;
#[cfg(test)]           extern crate test;

pub use keypad::{KeypadKey, Right, Left, Up, Down, A, B, Select, Start};

pub mod device;

mod cpu;
mod gbmode;
mod gpu;
mod keypad;
mod mbc;
mod mmu;
mod register;
mod serial;
mod sound;
mod timer;
mod util;
<|endoftext|>


<commit_before>//! Parse hoon source into AST twig

use std::str;
use std::str::FromStr;
use num::bigint::BigUint;

use twig::{self, Twig, Rune, Odor};

pub type ParseResult<'a, T> = Result<(&'a [u8], T), &'a [u8]>;

/// Parse Hoon source code into a Twig AST.
///
/// Return trailing input after a successful parse along with the resulting
/// Twig. Return trailing input after the point of failed parse on error.
pub fn ream(mut input: &[u8]) -> ParseResult<Twig> {
    // Eat initial space
    loop {
        if let Ok((tail, _)) = space_or_comment(input) {
            input = tail;
        } else {
            break;
        }
    }
    twig(input)
}

fn twig(input: &[u8]) -> ParseResult<Twig> {
    use twig::Rune::*;

    if let Ok(x) = atom(input) {
        return Ok(x);
    }
    if let Ok(x) = wing(input) {
        return Ok(x);
    }

    // Just crunch through the whole set of rune data and look for all the
    // ones that look like they can be parsed naively.
    for i in twig::RUNES.iter() {
        if i.rune.is_regular() && i.rune.glyph().is_some() {
            if let Ok(x) = rune(input, i.rune) {
                return Ok(x);
            }
        }
    }

    Err(input)
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Form {
    Tall,
    Wide,
}

fn rune<'a>(input: &'a [u8], rune: Rune) -> ParseResult<'a, Twig> {
    assert!(rune.is_regular(),
            "Can't use standard parser on this type of rune!");
    let glyph = rune.glyph().expect("Trying to parse an unprintable rune");
    let (input, form) = try!(rune_start(input, &glyph));
    let (input, args) = try!(rune_args(input, form, rune.arity()));
    Ok((input, Twig::Cell(box Twig::Rune(rune), box args)))
}

/// Parse the start of a rune with the given glyph and determine if the rune
/// is in wide form (.*(p q) or tall form (.*  p  q).
fn rune_start<'a>(input: &'a [u8], glyph: &str) -> ParseResult<'a, Form> {
    assert!(glyph.len() == 2, "Rune isn't 2 characters");
    let (mut tail, _) = try!(tag(input, glyph));

    if let Ok((tail, _)) = tag(tail, "(") {
        return Ok((tail, Form::Wide));
    }

    if let Ok((tail, _)) = gap(tail) {
        return Ok((tail, Form::Tall));
    }

    Err(input)
}

fn rune_args(input: &[u8], form: Form, n: usize) -> ParseResult<Twig> {
    assert!(n > 0);
    if n == 1 {
        let (input, p) = try!(twig(input));
        let (input, _) = try!(rune_end(input, form));
        Ok((input, p))
    } else {
        let (input, p) = try!(twig(input));
        let (input, _) = try!(rune_sep(input, form));
        let (input, ps) = try!(rune_args(input, form, n - 1));
        Ok((input, Twig::Cell(box p, box ps)))
    }
}

fn rune_sep(input: &[u8], form: Form) -> ParseResult<&[u8]> {
    match form {
        Form::Tall => gap(input),
        Form::Wide => tag(input, " "),
    }
}

fn rune_end(input: &[u8], form: Form) -> ParseResult<&[u8]> {
    match form {
        Form::Tall => Ok((input, &input[..0])),
        Form::Wide => tag(input, ")"),
    }
}

fn atom(input: &[u8]) -> ParseResult<Twig> {
    if let Ok((tail, num)) = ud(input) {
        Ok((tail,
            Twig::Cell(box Twig::Rune(Rune::dtzy), box Twig::Atom(Odor::ud, num))))
    } else {
        Err(input)
    }
}

/// Unsigned decimal
fn ud(input: &[u8]) -> ParseResult<BigUint> {
    let mut idx = 0;
    let mut num = String::new();
    for c in input.iter() {
        if *c >= '0' as u8 && *c <= '9' as u8 {
            num.push(*c as char);
        } else if idx > 0 && *c == '.' as u8 {
            // Ignore separator dots.
            // We're lazy, so we let them show up in any pattern after the
            // initial digit instead of insisting that they group the digits
            // in sized groups.
        } else {
            if idx == 0 {
                return Err(input);
            }
            break;
        }
        idx += 1;
    }

    Ok((&input[idx..], FromStr::from_str(&num).unwrap()))
}

fn wing(input: &[u8]) -> ParseResult<Twig> {
    // TODO: Dotted forms expanding to multi-term wings.
    let (input, term) = try!(term(input));
    let s = str::from_utf8(term).unwrap().to_string();
    Ok((input,
        Twig::Cell(box Twig::Rune(Rune::cnzz), box Twig::Wing(vec![s]))))
}

fn term(input: &[u8]) -> ParseResult<&[u8]> {
    let mut idx = 0;
    for i in input.iter() {
        if idx == 0 && *i == '$' as u8 {
            // Special $
            if input.len() > 1 && !is_ident_middle(input[1]) {
                // Can't have something like $foo.
                return Err(input);
            }
            idx += 1;
            break;
        }

        if idx == 0 && !is_ident_start(*i) {
            break;
        }

        if idx > 0 && !is_ident_middle(*i) {
            break;
        }

        idx += 1;
    }

    if idx == 0 {
        Err(input)
    } else {
        Ok((&input[idx..], &input[..idx]))
    }
}

fn space_or_comment(input: &[u8]) -> ParseResult<()> {
    let mut tail = input;
    loop {
        if let Ok((t, _)) = comment(tail) {
            tail = t
        } else {
            if tail.len() == 0 {
                break;
            } else if is_whitespace(tail[0]) {
                tail = &tail[1..];
            } else {
                break;
            }
        }
    }
    if tail == input {
        Err(input)
    } else {
        Ok((tail, ()))
    }
}

/// At least one newline or at least two spaces.
///
/// Used to separate tall forms in Hoon.
fn gap(input: &[u8]) -> ParseResult<&[u8]> {
    let mut spaces = 0;
    let mut idx = 0;
    for i in input.iter() {
        if *i == '\n' as u8 {
            spaces += 2;
        } else if is_whitespace(*i) {
            spaces += 1;
        } else {
            break;
        }

        idx += 1;
    }

    if spaces < 2 {
        Err(input)
    } else {
        Ok((&input[idx..], &input[0..idx]))
    }
}

fn comment(input: &[u8]) -> ParseResult<&[u8]> {
    let (input, _) = try!(tag(input, "::"));
    // XXX: The initial bit with the "::" won't make it to the return value.
    split_after(input, '\n' as u8)
}

fn is_whitespace(c: u8) -> bool {
    // Physical tabs not included. A physical tab in the code is a syntax
    // error.
    c == 0x10 || c == 0x13 || c == ' ' as u8
}

fn is_lowercase(c: u8) -> bool {
    c >= 'a' as u8 && c <= 'z' as u8
}

fn is_numeric(c: u8) -> bool {
    c >= '0' as u8 && c <= '9' as u8
}

fn is_ident_start(c: u8) -> bool {
    is_lowercase(c)
}

fn is_ident_middle(c: u8) -> bool {
    is_lowercase(c) || is_numeric(c) || c == '-' as u8
}

fn tag<'a>(input: &'a [u8], prefix: &str) -> ParseResult<'a, &'a [u8]> {
    let prefix = prefix.as_bytes();
    if input.len() < prefix.len() {
        return Err(input);
    }

    for (x, y) in prefix.iter().zip(input.iter()) {
        if x != y {
            return Err(input);
        }
    }

    Ok((&input[prefix.len()..], &input[0..prefix.len()]))
}

fn split_after(input: &[u8], c: u8) -> ParseResult<&[u8]> {
    let mut idx = 1;
    for i in input.iter() {
        if *i == c {
            break;
        }
        idx += 1;
    }
    Ok((&input[idx..], &input[0..idx]))
}
<commit_msg>Bugfix $ parse<commit_after>//! Parse hoon source into AST twig

use std::str;
use std::str::FromStr;
use num::bigint::BigUint;

use twig::{self, Twig, Rune, Odor};

pub type ParseResult<'a, T> = Result<(&'a [u8], T), &'a [u8]>;

/// Parse Hoon source code into a Twig AST.
///
/// Return trailing input after a successful parse along with the resulting
/// Twig. Return trailing input after the point of failed parse on error.
pub fn ream(mut input: &[u8]) -> ParseResult<Twig> {
    // Eat initial space
    loop {
        if let Ok((tail, _)) = space_or_comment(input) {
            input = tail;
        } else {
            break;
        }
    }
    twig(input)
}

fn twig(input: &[u8]) -> ParseResult<Twig> {
    use twig::Rune::*;

    if let Ok(x) = atom(input) {
        return Ok(x);
    }
    if let Ok(x) = wing(input) {
        return Ok(x);
    }

    // Just crunch through the whole set of rune data and look for all the
    // ones that look like they can be parsed naively.
    for i in twig::RUNES.iter() {
        if i.rune.is_regular() && i.rune.glyph().is_some() {
            if let Ok(x) = rune(input, i.rune) {
                return Ok(x);
            }
        }
    }

    Err(input)
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
enum Form {
    Tall,
    Wide,
}

fn rune<'a>(input: &'a [u8], rune: Rune) -> ParseResult<'a, Twig> {
    assert!(rune.is_regular(),
            "Can't use standard parser on this type of rune!");
    let glyph = rune.glyph().expect("Trying to parse an unprintable rune");
    let (input, form) = try!(rune_start(input, &glyph));
    let (input, args) = try!(rune_args(input, form, rune.arity()));
    Ok((input, Twig::Cell(box Twig::Rune(rune), box args)))
}

/// Parse the start of a rune with the given glyph and determine if the rune
/// is in wide form (.*(p q) or tall form (.*  p  q).
fn rune_start<'a>(input: &'a [u8], glyph: &str) -> ParseResult<'a, Form> {
    assert!(glyph.len() == 2, "Rune isn't 2 characters");
    let (mut tail, _) = try!(tag(input, glyph));

    if let Ok((tail, _)) = tag(tail, "(") {
        return Ok((tail, Form::Wide));
    }

    if let Ok((tail, _)) = gap(tail) {
        return Ok((tail, Form::Tall));
    }

    Err(input)
}

fn rune_args(input: &[u8], form: Form, n: usize) -> ParseResult<Twig> {
    assert!(n > 0);
    if n == 1 {
        let (input, p) = try!(twig(input));
        let (input, _) = try!(rune_end(input, form));
        Ok((input, p))
    } else {
        let (input, p) = try!(twig(input));
        let (input, _) = try!(rune_sep(input, form));
        let (input, ps) = try!(rune_args(input, form, n - 1));
        Ok((input, Twig::Cell(box p, box ps)))
    }
}

fn rune_sep(input: &[u8], form: Form) -> ParseResult<&[u8]> {
    match form {
        Form::Tall => gap(input),
        Form::Wide => tag(input, " "),
    }
}

fn rune_end(input: &[u8], form: Form) -> ParseResult<&[u8]> {
    match form {
        Form::Tall => Ok((input, &input[..0])),
        Form::Wide => tag(input, ")"),
    }
}

fn atom(input: &[u8]) -> ParseResult<Twig> {
    if let Ok((tail, num)) = ud(input) {
        Ok((tail,
            Twig::Cell(box Twig::Rune(Rune::dtzy), box Twig::Atom(Odor::ud, num))))
    } else {
        Err(input)
    }
}

/// Unsigned decimal
fn ud(input: &[u8]) -> ParseResult<BigUint> {
    let mut idx = 0;
    let mut num = String::new();
    for c in input.iter() {
        if *c >= '0' as u8 && *c <= '9' as u8 {
            num.push(*c as char);
        } else if idx > 0 && *c == '.' as u8 {
            // Ignore separator dots.
            // We're lazy, so we let them show up in any pattern after the
            // initial digit instead of insisting that they group the digits
            // in sized groups.
        } else {
            if idx == 0 {
                return Err(input);
            }
            break;
        }
        idx += 1;
    }

    Ok((&input[idx..], FromStr::from_str(&num).unwrap()))
}

fn wing(input: &[u8]) -> ParseResult<Twig> {
    // TODO: Dotted forms expanding to multi-term wings.
    let (input, term) = try!(term(input));
    let s = str::from_utf8(term).unwrap().to_string();
    Ok((input,
        Twig::Cell(box Twig::Rune(Rune::cnzz), box Twig::Wing(vec![s]))))
}

fn term(input: &[u8]) -> ParseResult<&[u8]> {
    let mut idx = 0;
    for i in input.iter() {
        if idx == 0 && *i == '$' as u8 {
            // Special $
            if input.len() > 1 && is_ident_middle(input[1]) {
                // Can't have something like $foo.
                return Err(input);
            }
            idx += 1;
            break;
        }

        if idx == 0 && !is_ident_start(*i) {
            break;
        }

        if idx > 0 && !is_ident_middle(*i) {
            break;
        }

        idx += 1;
    }

    if idx == 0 {
        Err(input)
    } else {
        Ok((&input[idx..], &input[..idx]))
    }
}

fn space_or_comment(input: &[u8]) -> ParseResult<()> {
    let mut tail = input;
    loop {
        if let Ok((t, _)) = comment(tail) {
            tail = t
        } else {
            if tail.len() == 0 {
                break;
            } else if is_whitespace(tail[0]) {
                tail = &tail[1..];
            } else {
                break;
            }
        }
    }
    if tail == input {
        Err(input)
    } else {
        Ok((tail, ()))
    }
}

/// At least one newline or at least two spaces.
///
/// Used to separate tall forms in Hoon.
fn gap(input: &[u8]) -> ParseResult<&[u8]> {
    let mut spaces = 0;
    let mut idx = 0;
    for i in input.iter() {
        if *i == '\n' as u8 {
            spaces += 2;
        } else if is_whitespace(*i) {
            spaces += 1;
        } else {
            break;
        }

        idx += 1;
    }

    if spaces < 2 {
        Err(input)
    } else {
        Ok((&input[idx..], &input[0..idx]))
    }
}

fn comment(input: &[u8]) -> ParseResult<&[u8]> {
    let (input, _) = try!(tag(input, "::"));
    // XXX: The initial bit with the "::" won't make it to the return value.
    split_after(input, '\n' as u8)
}

fn is_whitespace(c: u8) -> bool {
    // Physical tabs not included. A physical tab in the code is a syntax
    // error.
    c == 0x10 || c == 0x13 || c == ' ' as u8
}

fn is_lowercase(c: u8) -> bool {
    c >= 'a' as u8 && c <= 'z' as u8
}

fn is_numeric(c: u8) -> bool {
    c >= '0' as u8 && c <= '9' as u8
}

fn is_ident_start(c: u8) -> bool {
    is_lowercase(c)
}

fn is_ident_middle(c: u8) -> bool {
    is_lowercase(c) || is_numeric(c) || c == '-' as u8
}

fn tag<'a>(input: &'a [u8], prefix: &str) -> ParseResult<'a, &'a [u8]> {
    let prefix = prefix.as_bytes();
    if input.len() < prefix.len() {
        return Err(input);
    }

    for (x, y) in prefix.iter().zip(input.iter()) {
        if x != y {
            return Err(input);
        }
    }

    Ok((&input[prefix.len()..], &input[0..prefix.len()]))
}

fn split_after(input: &[u8], c: u8) -> ParseResult<&[u8]> {
    let mut idx = 1;
    for i in input.iter() {
        if *i == c {
            break;
        }
        idx += 1;
    }
    Ok((&input[idx..], &input[0..idx]))
}
<|endoftext|>


<commit_before>use std::io::fs;
use std::path::posix::Path;
use std::os::homedir;
#[cfg(test)] use test_helper::{cleanup_dir,random_name};

static MUXED_NAME_STR: &'static str = "muxed";
#[cfg(test)] static MUXED_PATH: Path = Path::new(format!("/tmp/.{}/", &MUXED_NAME_STR.to_string()));
//#[cfg(not_test)] static MUXED_DIR: Path = Path::new(format!("{}/.{}/", root::homedir_string(), &DEFAULT_MUXED_DIR.to_string()));

/// Create the muxed directory and return the path if creation is successful.
pub fn create_muxed_dir(name: &String) -> Path {
    let path = &Path::new(format!("{}/.{}", homedir_string(), name));
    match fs::mkdir(path, ::std::io::UserRWX) {
        Ok(()) => (),
        Err(_e) => println!("Failed to create project {}", path.filename()),
    }

    path.clone()
}

/// Return a boolean if the ~/.muxed/ dir exists.
pub fn muxed_dir_exists(name: &String) -> bool {
    let path = &Path::new(format!("{}/.{}", homedir_string(), name));
    path.exists()
}

/// Return the users current homedir as a string.
pub fn homedir_string() -> String {
    let home_unwrap = homedir().unwrap();
    format!("{}", home_unwrap.display())
}

#[test]
fn muxed_dir_exists_returns_false() {
    assert!(!muxed_dir_exists(&random_name()));
}

#[test]
fn muxed_dir_exists_returns_true() {
    let dir = random_name();
    create_muxed_dir(&dir);
    assert!(muxed_dir_exists(&dir));

    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    cleanup_dir(muxed_path);
}

#[test]
fn creates_muxed_dir() {
    let dir = random_name();
    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    create_muxed_dir(&dir);
    assert!(muxed_path.exists());
    cleanup_dir(muxed_path);
}
<commit_msg>No more need to verify if the muxed_dir exists. Just call for the dir and create it if it does not exist. This is more of a tell don't ask policy.<commit_after>use std::io::fs;
use std::path::posix::Path;
use std::os::homedir;
#[cfg(test)] use test_helper::{cleanup_dir,random_name};

static MUXED_NAME_STR: &'static str = "muxed";
#[cfg(test)] static MUXED_PATH: Path = Path::new(format!("/tmp/.{}/", &MUXED_NAME_STR.to_string()));
//#[cfg(not_test)] static MUXED_DIR: Path = Path::new(format!("{}/.{}/", root::homedir_string(), &DEFAULT_MUXED_DIR.to_string()));

/// Create the muxed directory and return the path if creation is successful.
pub fn create_muxed_dir(name: &String) -> Path {
    let path = &Path::new(format!("{}/.{}", homedir_string(), name));
    match fs::mkdir(path, ::std::io::UserRWX) {
        Ok(()) => (),
        Err(_e) => println!("Failed to create project {}", path.filename()),
    }

    path.clone()
}

/// Return the users current homedir as a string.
pub fn homedir_string() -> String {
    let home_unwrap = homedir().unwrap();
    format!("{}", home_unwrap.display())
}

pub fn muxed_dir() -> Path {
    match MUXED_DIR.exists() {
        true  => MUXED_DIR,
        false => create_muxed_dir(MUXED_DIR)
    }
}

#[test]
fn muxed_dir_exists_returns_false() {
    assert!(!muxed_dir_exists(&random_name()));
}

#[test]
fn muxed_dir_exists_returns_true() {
    let dir = random_name();
    create_muxed_dir(&dir);
    assert!(muxed_dir_exists(&dir));

    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    cleanup_dir(muxed_path);
}

#[test]
fn creates_muxed_dir() {
    let dir = random_name();
    let muxed_path  = &Path::new(format!("{}/.{}/", homedir_string(), dir));
    create_muxed_dir(&dir);
    assert!(muxed_path.exists());
    cleanup_dir(muxed_path);
}
<|endoftext|>


<commit_before>use std::default::Default;
use std::ffi::OsStr;
use std::fs;
use std::io::Read;
use std::path;

use liquid;
use serde_json;
use serde_yaml;
use toml;

use error::*;
use files;

const DATA_DIR: &'static str = "_data";

#[derive(Debug, PartialEq, Clone)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SiteBuilder {
    pub name: Option<String>,
    pub description: Option<String>,
    pub base_url: Option<String>,
    pub data: Option<liquid::Object>,
    #[serde(skip)]
    pub data_dir: &'static str,
}

impl Default for SiteBuilder {
    fn default() -> SiteBuilder {
        SiteBuilder {
            name: None,
            description: None,
            base_url: None,
            data: None,
            data_dir: DATA_DIR,
        }
    }
}

impl SiteBuilder {
    pub fn build(self, root: &path::Path) -> Result<Site> {
        let SiteBuilder {
            name,
            description,
            base_url,
            data,
            data_dir,
        } = self;
        let base_url = base_url.map(|mut l| {
                                        if l.ends_with('/') {
                                            l.pop();
                                        }
                                        l
                                    });

        let mut attributes = liquid::Object::new();
        if let Some(ref name) = name {
            attributes.insert("name".to_owned(), liquid::Value::str(name));
        }
        if let Some(ref description) = description {
            attributes.insert("description".to_owned(), liquid::Value::str(description));
        }
        if let Some(ref base_url) = base_url {
            attributes.insert("base_url".to_owned(), liquid::Value::str(base_url));
        }
        let mut data = data.unwrap_or_default();
        insert_data_dir(&mut data, &root.join(data_dir))?;
        if !data.is_empty() {
            attributes.insert("data".to_owned(), liquid::Value::Object(data));
        }

        Ok(Site {
               name,
               description,
               base_url,
               attributes,
           })
    }
}

#[derive(Debug, PartialEq, Default, Clone)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Site {
    pub name: Option<String>,
    pub description: Option<String>,
    pub base_url: Option<String>,
    pub attributes: liquid::Object,
}

fn deep_insert(data_map: &mut liquid::Object,
               file_path: &path::Path,
               target_key: String,
               data: liquid::Value)
               -> Result<()> {
    // now find the nested map it is supposed to be in
    let target_map = if let Some(path) = file_path.parent() {
        let mut map = data_map;
        for part in path.iter() {
            let key = part.to_str().ok_or_else(|| {
                format!("The data from {:?} can't be loaded as it contains non utf-8 characters",
                        path)
            })?;
            let cur_map = map;
            map = cur_map
                .entry(String::from(key))
                .or_insert_with(|| liquid::Value::Object(liquid::Object::new()))
                .as_object_mut()
                .ok_or_else(|| {
                                format!("Aborting: Duplicate in data tree. Would overwrite {:?} ",
                                        path)
                            })?;
        }
        map
    } else {
        data_map
    };

    match target_map.insert(target_key, data) {
        None => Ok(()),
        _ => {
            Err(format!("The data from {:?} can't be loaded: the key already exists",
                        file_path)
                    .into())
        }
    }
}

fn load_data(data_path: &path::Path) -> Result<liquid::Value> {
    let ext = data_path.extension().unwrap_or_else(|| OsStr::new(""));

    let data: liquid::Value;

    if ext == OsStr::new("yml") || ext == OsStr::new("yaml") {
        let reader = fs::File::open(data_path)?;
        data = serde_yaml::from_reader(reader)?;
    } else if ext == OsStr::new("json") {
        let reader = fs::File::open(data_path)?;
        data = serde_json::from_reader(reader)?;
    } else if ext == OsStr::new("toml") {
        let mut reader = fs::File::open(data_path)?;
        let mut text = String::new();
        reader.read_to_string(&mut text)?;
        data = toml::from_str(&text)?;
    } else {
        bail!("Failed to load of data {:?}: unknown file type '{:?}'.\n\
              Supported data files extensions are: yml, yaml, json and toml.",
              data_path,
              ext);
    }

    Ok(data)
}

fn insert_data_dir(data: &mut liquid::Object, data_root: &path::Path) -> Result<()> {
    let data_files_builder = files::FilesBuilder::new(data_root)?;
    let data_files = data_files_builder.build()?;
    for full_path in data_files.files() {
        let rel_path = full_path
            .strip_prefix(data_root)
            .expect("file was found under the root");

        let file_stem = full_path
            .file_stem()
            .expect("Files will always return with a stem");
        let file_stem = String::from(file_stem.to_str().unwrap());

        let data_fragment = load_data(&full_path)?;

        deep_insert(data, rel_path, file_stem, data_fragment)?;
    }

    Ok(())
}
<commit_msg>fix(data-files): Provide information which file caused an error<commit_after>use std::default::Default;
use std::ffi::OsStr;
use std::fs;
use std::io::Read;
use std::path;

use liquid;
use serde_json;
use serde_yaml;
use toml;

use error::*;
use files;

const DATA_DIR: &'static str = "_data";

#[derive(Debug, PartialEq, Clone)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SiteBuilder {
    pub name: Option<String>,
    pub description: Option<String>,
    pub base_url: Option<String>,
    pub data: Option<liquid::Object>,
    #[serde(skip)]
    pub data_dir: &'static str,
}

impl Default for SiteBuilder {
    fn default() -> SiteBuilder {
        SiteBuilder {
            name: None,
            description: None,
            base_url: None,
            data: None,
            data_dir: DATA_DIR,
        }
    }
}

impl SiteBuilder {
    pub fn build(self, root: &path::Path) -> Result<Site> {
        let SiteBuilder {
            name,
            description,
            base_url,
            data,
            data_dir,
        } = self;
        let base_url = base_url.map(|mut l| {
                                        if l.ends_with('/') {
                                            l.pop();
                                        }
                                        l
                                    });

        let mut attributes = liquid::Object::new();
        if let Some(ref name) = name {
            attributes.insert("name".to_owned(), liquid::Value::str(name));
        }
        if let Some(ref description) = description {
            attributes.insert("description".to_owned(), liquid::Value::str(description));
        }
        if let Some(ref base_url) = base_url {
            attributes.insert("base_url".to_owned(), liquid::Value::str(base_url));
        }
        let mut data = data.unwrap_or_default();
        insert_data_dir(&mut data, &root.join(data_dir))?;
        if !data.is_empty() {
            attributes.insert("data".to_owned(), liquid::Value::Object(data));
        }

        Ok(Site {
               name,
               description,
               base_url,
               attributes,
           })
    }
}

#[derive(Debug, PartialEq, Default, Clone)]
#[derive(Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Site {
    pub name: Option<String>,
    pub description: Option<String>,
    pub base_url: Option<String>,
    pub attributes: liquid::Object,
}

fn deep_insert(data_map: &mut liquid::Object,
               file_path: &path::Path,
               target_key: String,
               data: liquid::Value)
               -> Result<()> {
    // now find the nested map it is supposed to be in
    let target_map = if let Some(path) = file_path.parent() {
        let mut map = data_map;
        for part in path.iter() {
            let key = part.to_str().ok_or_else(|| {
                format!("The data from {:?} can't be loaded as it contains non utf-8 characters",
                        path)
            })?;
            let cur_map = map;
            map = cur_map
                .entry(String::from(key))
                .or_insert_with(|| liquid::Value::Object(liquid::Object::new()))
                .as_object_mut()
                .ok_or_else(|| {
                                format!("Aborting: Duplicate in data tree. Would overwrite {:?} ",
                                        path)
                            })?;
        }
        map
    } else {
        data_map
    };

    match target_map.insert(target_key, data) {
        None => Ok(()),
        _ => {
            Err(format!("The data from {:?} can't be loaded: the key already exists",
                        file_path)
                    .into())
        }
    }
}

fn load_data(data_path: &path::Path) -> Result<liquid::Value> {
    let ext = data_path.extension().unwrap_or_else(|| OsStr::new(""));

    let data: liquid::Value;

    if ext == OsStr::new("yml") || ext == OsStr::new("yaml") {
        let reader = fs::File::open(data_path)?;
        data = serde_yaml::from_reader(reader)?;
    } else if ext == OsStr::new("json") {
        let reader = fs::File::open(data_path)?;
        data = serde_json::from_reader(reader)?;
    } else if ext == OsStr::new("toml") {
        let mut reader = fs::File::open(data_path)?;
        let mut text = String::new();
        reader.read_to_string(&mut text)?;
        data = toml::from_str(&text)?;
    } else {
        bail!("Failed to load of data {:?}: unknown file type '{:?}'.\n\
              Supported data files extensions are: yml, yaml, json and toml.",
              data_path,
              ext);
    }

    Ok(data)
}

fn insert_data_dir(data: &mut liquid::Object, data_root: &path::Path) -> Result<()> {
    let data_files_builder = files::FilesBuilder::new(data_root)?;
    let data_files = data_files_builder.build()?;
    for full_path in data_files.files() {
        let rel_path = full_path
            .strip_prefix(data_root)
            .expect("file was found under the root");

        let file_stem = full_path
            .file_stem()
            .expect("Files will always return with a stem");
        let file_stem = String::from(file_stem.to_str().unwrap());
        let data_fragment = load_data(&full_path)
            .chain_err(|| format!("Loading data from {:?} failed", full_path))?;

        deep_insert(data, rel_path, file_stem, data_fragment)
            .chain_err(|| format!("Merging data into {:?} failed", rel_path))?;
    }

    trace!("Done loading data directory.");

    Ok(())
}
<|endoftext|>


<commit_before>use std::io;
use std::io::{IoResult, IoError};
use std::iter::range_step_inclusive;
use types;
use types::ZipFile;
use util;

static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
static CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06054b50;

pub fn central_header_to_zip_file<R: Reader+Seek>(reader: &mut R) -> IoResult<ZipFile>
{
    // Parse central header
    let signature = try!(reader.read_le_u32());
    if signature != CENTRAL_DIRECTORY_HEADER_SIGNATURE
    {
        return Err(IoError {
            kind: io::MismatchedFileTypeForOperation,
            desc: "Invalid central directory header",
            detail: None })
    }

    try!(reader.read_le_u16());
    try!(reader.read_le_u16());
    let flags = try!(reader.read_le_u16());
    let encrypted = flags & 1 == 1;
    let compression_method = try!(reader.read_le_u16());
    let last_mod_time = try!(reader.read_le_u16());
    let last_mod_date = try!(reader.read_le_u16());
    let crc32 = try!(reader.read_le_u32());
    let compressed_size = try!(reader.read_le_u32());
    let uncompressed_size = try!(reader.read_le_u32());
    let file_name_length = try!(reader.read_le_u16()) as uint;
    let extra_field_length = try!(reader.read_le_u16()) as uint;
    let file_comment_length = try!(reader.read_le_u16()) as uint;
    try!(reader.read_le_u16());
    try!(reader.read_le_u16());
    try!(reader.read_le_u32());
    let offset = try!(reader.read_le_u32()) as i64;
    let file_name = try!(reader.read_exact(file_name_length));
    try!(reader.read_exact(extra_field_length));
    let file_comment  = try!(reader.read_exact(file_comment_length));

    // Remember end of central header
    let return_position = try!(reader.tell()) as i64;

    // Parse local header
    try!(reader.seek(offset, io::SeekSet));
    let signature = try!(reader.read_le_u32());
    if signature != LOCAL_FILE_HEADER_SIGNATURE
    {
        return Err(IoError {
            kind: io::MismatchedFileTypeForOperation,
            desc: "Invalid local file header",
            detail: None })
    }

    try!(reader.seek(22, io::SeekCur));
    let file_name_length = try!(reader.read_le_u16()) as u64;
    let extra_field_length = try!(reader.read_le_u16()) as u64;
    let magic_and_header = 4 + 22 + 2 + 2;
    let data_start = offset as u64 + magic_and_header + file_name_length + extra_field_length;

    // Construct the result
    let result = ZipFile
    {
        encrypted: encrypted,
        compression_method: FromPrimitive::from_u16(compression_method).unwrap_or(types::Unknown),
        last_modified_time: util::msdos_datetime_to_tm(last_mod_time, last_mod_date),
        crc32: crc32,
        compressed_size: compressed_size as u64,
        uncompressed_size: uncompressed_size as u64,
        file_name: file_name,
        file_comment: file_comment,
        header_start: offset as u64,
        data_start: data_start,
    };

    // Go back after the central header
    try!(reader.seek(return_position, io::SeekSet));

    Ok(result)
}

pub fn write_local_file_header<T: Writer>(writer: &mut T, file: &ZipFile) -> IoResult<()>
{
    try!(writer.write_le_u32(LOCAL_FILE_HEADER_SIGNATURE));
    try!(writer.write_le_u16(20));
    let flag = if file.encrypted { 1 } else { 0 };
    try!(writer.write_le_u16(flag));
    try!(writer.write_le_u16(file.compression_method as u16));
    try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time)));
    try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time)));
    try!(writer.write_le_u32(file.crc32));
    try!(writer.write_le_u32(file.compressed_size as u32));
    try!(writer.write_le_u32(file.uncompressed_size as u32));
    try!(writer.write_le_u16(file.file_name.len() as u16));
    try!(writer.write_le_u16(0));
    try!(writer.write(file.file_name.as_slice()));

    Ok(())
}

pub fn write_central_directory_header<T: Writer>(writer: &mut T, file: &ZipFile) -> IoResult<()>
{
    try!(writer.write_le_u32(CENTRAL_DIRECTORY_HEADER_SIGNATURE));
    try!(writer.write_le_u16(1337));
    try!(writer.write_le_u16(20));
    let flag = if file.encrypted { 1 } else { 0 };
    try!(writer.write_le_u16(flag));
    try!(writer.write_le_u16(file.compression_method as u16));
    try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time)));
    try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time)));
    try!(writer.write_le_u32(file.crc32));
    try!(writer.write_le_u32(file.compressed_size as u32));
    try!(writer.write_le_u32(file.uncompressed_size as u32));
    try!(writer.write_le_u16(file.file_name.len() as u16));
    try!(writer.write_le_u16(0));
    try!(writer.write_le_u16(0));
    try!(writer.write_le_u16(0));
    try!(writer.write_le_u16(0));
    try!(writer.write_le_u32(0));
    try!(writer.write_le_u32(file.header_start as u32));
    try!(writer.write(file.file_name.as_slice()));

    Ok(())
}

pub struct CentralDirectoryEnd
{
    pub disk_number: u16,
    pub disk_with_central_directory: u16,
    pub number_of_files_on_this_disk: u16,
    pub number_of_files: u16,
    pub central_directory_size: u32,
    pub central_directory_offset: u32,
    pub zip_file_comment: Vec<u8>,
}

impl CentralDirectoryEnd
{
    pub fn parse<T: Reader>(reader: &mut T) -> IoResult<CentralDirectoryEnd>
    {
        let magic = try!(reader.read_le_u32());
        if magic != CENTRAL_DIRECTORY_END_SIGNATURE
        {
            return Err(IoError {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Invalid digital signature header",
                detail: None })
        }
        let disk_number = try!(reader.read_le_u16());
        let disk_with_central_directory = try!(reader.read_le_u16());
        let number_of_files_on_this_disk = try!(reader.read_le_u16());
        let number_of_files = try!(reader.read_le_u16());
        let central_directory_size = try!(reader.read_le_u32());
        let central_directory_offset = try!(reader.read_le_u32());
        let zip_file_comment_length = try!(reader.read_le_u16()) as uint;
        let zip_file_comment = try!(reader.read_exact(zip_file_comment_length));

        Ok(CentralDirectoryEnd
           {
               disk_number: disk_number,
               disk_with_central_directory: disk_with_central_directory,
               number_of_files_on_this_disk: number_of_files_on_this_disk,
               number_of_files: number_of_files,
               central_directory_size: central_directory_size,
               central_directory_offset: central_directory_offset,
               zip_file_comment: zip_file_comment,
           })
    }

    pub fn find_and_parse<T: Reader+Seek>(reader: &mut T) -> IoResult<CentralDirectoryEnd>
    {
        let header_size = 22;
        let bytes_between_magic_and_comment_size = header_size - 6;
        try!(reader.seek(0, io::SeekEnd));
        let file_length = try!(reader.tell()) as i64;

        let search_upper_bound = ::std::cmp::max(0, file_length - header_size - ::std::u16::MAX as i64);
        for pos in range_step_inclusive(file_length - header_size, search_upper_bound, -1)
        {
            try!(reader.seek(pos, io::SeekSet));
            if try!(reader.read_le_u32()) == CENTRAL_DIRECTORY_END_SIGNATURE
            {
                try!(reader.seek(bytes_between_magic_and_comment_size, io::SeekCur));
                let comment_length = try!(reader.read_le_u16()) as i64;
                if file_length - pos - header_size == comment_length
                {
                    try!(reader.seek(pos, io::SeekSet));
                    return CentralDirectoryEnd::parse(reader);
                }
            }
        }
        Err(IoError
            {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Could not find central directory end",
                detail: None
            })
    }

    pub fn write<T: Writer>(&self, writer: &mut T) -> IoResult<()>
    {
        try!(writer.write_le_u32(CENTRAL_DIRECTORY_END_SIGNATURE));
        try!(writer.write_le_u16(self.disk_number));
        try!(writer.write_le_u16(self.disk_with_central_directory));
        try!(writer.write_le_u16(self.number_of_files_on_this_disk));
        try!(writer.write_le_u16(self.number_of_files));
        try!(writer.write_le_u32(self.central_directory_size));
        try!(writer.write_le_u32(self.central_directory_offset));
        try!(writer.write_le_u16(self.zip_file_comment.len() as u16));
        try!(writer.write(self.zip_file_comment.as_slice()));
        Ok(())
    }
}
<commit_msg>Set made-by to a value with an upper byte of 0<commit_after>use std::io;
use std::io::{IoResult, IoError};
use std::iter::range_step_inclusive;
use types;
use types::ZipFile;
use util;

static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50;
static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50;
static CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06054b50;

pub fn central_header_to_zip_file<R: Reader+Seek>(reader: &mut R) -> IoResult<ZipFile>
{
    // Parse central header
    let signature = try!(reader.read_le_u32());
    if signature != CENTRAL_DIRECTORY_HEADER_SIGNATURE
    {
        return Err(IoError {
            kind: io::MismatchedFileTypeForOperation,
            desc: "Invalid central directory header",
            detail: None })
    }

    try!(reader.read_le_u16());
    try!(reader.read_le_u16());
    let flags = try!(reader.read_le_u16());
    let encrypted = flags & 1 == 1;
    let compression_method = try!(reader.read_le_u16());
    let last_mod_time = try!(reader.read_le_u16());
    let last_mod_date = try!(reader.read_le_u16());
    let crc32 = try!(reader.read_le_u32());
    let compressed_size = try!(reader.read_le_u32());
    let uncompressed_size = try!(reader.read_le_u32());
    let file_name_length = try!(reader.read_le_u16()) as uint;
    let extra_field_length = try!(reader.read_le_u16()) as uint;
    let file_comment_length = try!(reader.read_le_u16()) as uint;
    try!(reader.read_le_u16());
    try!(reader.read_le_u16());
    try!(reader.read_le_u32());
    let offset = try!(reader.read_le_u32()) as i64;
    let file_name = try!(reader.read_exact(file_name_length));
    try!(reader.read_exact(extra_field_length));
    let file_comment  = try!(reader.read_exact(file_comment_length));

    // Remember end of central header
    let return_position = try!(reader.tell()) as i64;

    // Parse local header
    try!(reader.seek(offset, io::SeekSet));
    let signature = try!(reader.read_le_u32());
    if signature != LOCAL_FILE_HEADER_SIGNATURE
    {
        return Err(IoError {
            kind: io::MismatchedFileTypeForOperation,
            desc: "Invalid local file header",
            detail: None })
    }

    try!(reader.seek(22, io::SeekCur));
    let file_name_length = try!(reader.read_le_u16()) as u64;
    let extra_field_length = try!(reader.read_le_u16()) as u64;
    let magic_and_header = 4 + 22 + 2 + 2;
    let data_start = offset as u64 + magic_and_header + file_name_length + extra_field_length;

    // Construct the result
    let result = ZipFile
    {
        encrypted: encrypted,
        compression_method: FromPrimitive::from_u16(compression_method).unwrap_or(types::Unknown),
        last_modified_time: util::msdos_datetime_to_tm(last_mod_time, last_mod_date),
        crc32: crc32,
        compressed_size: compressed_size as u64,
        uncompressed_size: uncompressed_size as u64,
        file_name: file_name,
        file_comment: file_comment,
        header_start: offset as u64,
        data_start: data_start,
    };

    // Go back after the central header
    try!(reader.seek(return_position, io::SeekSet));

    Ok(result)
}

pub fn write_local_file_header<T: Writer>(writer: &mut T, file: &ZipFile) -> IoResult<()>
{
    try!(writer.write_le_u32(LOCAL_FILE_HEADER_SIGNATURE));
    try!(writer.write_le_u16(20));
    let flag = if file.encrypted { 1 } else { 0 };
    try!(writer.write_le_u16(flag));
    try!(writer.write_le_u16(file.compression_method as u16));
    try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time)));
    try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time)));
    try!(writer.write_le_u32(file.crc32));
    try!(writer.write_le_u32(file.compressed_size as u32));
    try!(writer.write_le_u32(file.uncompressed_size as u32));
    try!(writer.write_le_u16(file.file_name.len() as u16));
    try!(writer.write_le_u16(0));
    try!(writer.write(file.file_name.as_slice()));

    Ok(())
}

pub fn write_central_directory_header<T: Writer>(writer: &mut T, file: &ZipFile) -> IoResult<()>
{
    try!(writer.write_le_u32(CENTRAL_DIRECTORY_HEADER_SIGNATURE));
    try!(writer.write_le_u16(0x00FF));
    try!(writer.write_le_u16(20));
    let flag = if file.encrypted { 1 } else { 0 };
    try!(writer.write_le_u16(flag));
    try!(writer.write_le_u16(file.compression_method as u16));
    try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time)));
    try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time)));
    try!(writer.write_le_u32(file.crc32));
    try!(writer.write_le_u32(file.compressed_size as u32));
    try!(writer.write_le_u32(file.uncompressed_size as u32));
    try!(writer.write_le_u16(file.file_name.len() as u16));
    try!(writer.write_le_u16(0));
    try!(writer.write_le_u16(0));
    try!(writer.write_le_u16(0));
    try!(writer.write_le_u16(0));
    try!(writer.write_le_u32(0));
    try!(writer.write_le_u32(file.header_start as u32));
    try!(writer.write(file.file_name.as_slice()));

    Ok(())
}

pub struct CentralDirectoryEnd
{
    pub disk_number: u16,
    pub disk_with_central_directory: u16,
    pub number_of_files_on_this_disk: u16,
    pub number_of_files: u16,
    pub central_directory_size: u32,
    pub central_directory_offset: u32,
    pub zip_file_comment: Vec<u8>,
}

impl CentralDirectoryEnd
{
    pub fn parse<T: Reader>(reader: &mut T) -> IoResult<CentralDirectoryEnd>
    {
        let magic = try!(reader.read_le_u32());
        if magic != CENTRAL_DIRECTORY_END_SIGNATURE
        {
            return Err(IoError {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Invalid digital signature header",
                detail: None })
        }
        let disk_number = try!(reader.read_le_u16());
        let disk_with_central_directory = try!(reader.read_le_u16());
        let number_of_files_on_this_disk = try!(reader.read_le_u16());
        let number_of_files = try!(reader.read_le_u16());
        let central_directory_size = try!(reader.read_le_u32());
        let central_directory_offset = try!(reader.read_le_u32());
        let zip_file_comment_length = try!(reader.read_le_u16()) as uint;
        let zip_file_comment = try!(reader.read_exact(zip_file_comment_length));

        Ok(CentralDirectoryEnd
           {
               disk_number: disk_number,
               disk_with_central_directory: disk_with_central_directory,
               number_of_files_on_this_disk: number_of_files_on_this_disk,
               number_of_files: number_of_files,
               central_directory_size: central_directory_size,
               central_directory_offset: central_directory_offset,
               zip_file_comment: zip_file_comment,
           })
    }

    pub fn find_and_parse<T: Reader+Seek>(reader: &mut T) -> IoResult<CentralDirectoryEnd>
    {
        let header_size = 22;
        let bytes_between_magic_and_comment_size = header_size - 6;
        try!(reader.seek(0, io::SeekEnd));
        let file_length = try!(reader.tell()) as i64;

        let search_upper_bound = ::std::cmp::max(0, file_length - header_size - ::std::u16::MAX as i64);
        for pos in range_step_inclusive(file_length - header_size, search_upper_bound, -1)
        {
            try!(reader.seek(pos, io::SeekSet));
            if try!(reader.read_le_u32()) == CENTRAL_DIRECTORY_END_SIGNATURE
            {
                try!(reader.seek(bytes_between_magic_and_comment_size, io::SeekCur));
                let comment_length = try!(reader.read_le_u16()) as i64;
                if file_length - pos - header_size == comment_length
                {
                    try!(reader.seek(pos, io::SeekSet));
                    return CentralDirectoryEnd::parse(reader);
                }
            }
        }
        Err(IoError
            {
                kind: io::MismatchedFileTypeForOperation,
                desc: "Could not find central directory end",
                detail: None
            })
    }

    pub fn write<T: Writer>(&self, writer: &mut T) -> IoResult<()>
    {
        try!(writer.write_le_u32(CENTRAL_DIRECTORY_END_SIGNATURE));
        try!(writer.write_le_u16(self.disk_number));
        try!(writer.write_le_u16(self.disk_with_central_directory));
        try!(writer.write_le_u16(self.number_of_files_on_this_disk));
        try!(writer.write_le_u16(self.number_of_files));
        try!(writer.write_le_u32(self.central_directory_size));
        try!(writer.write_le_u32(self.central_directory_offset));
        try!(writer.write_le_u16(self.zip_file_comment.len() as u16));
        try!(writer.write(self.zip_file_comment.as_slice()));
        Ok(())
    }
}
<|endoftext|>