<commit_before>//! Initialization of the signed distance function near the level set.
use super::min;
use std;

/// Computes the signed distance function from a plane given as the _zero_ level set of a
/// linear function on a tetrahedron at 4 points with unit coordinates starting at (0, 0, 0) and
/// ending at (1, 1, 1), and in between exactly one coordinate changes from 0 to 1.
///
/// Inputs are `u`, the values at the vertices.
///
/// The function returns the values of the (non-signed) distance function or `None` if the zero
/// level set does not pass through the tetrahedron.
pub fn tetrahedron_dist<F>(mut u: [f64; 4], mut dual_norm: F, perm: [usize; 3]) -> Option<[f64; 4]>
where
    F: FnMut([f64; 3]) -> f64,
{
    let mut n_pos = 0;
    let mut n_neg = 0;
    for u in &mut u {
        if *u > 0. {
            n_pos += 1;
        } else if *u < 0. {
            n_neg += 1;
        }
    }
    // check if sign differs (level set goes throught the triangle)
    if n_neg == 4 || n_pos == 4 {
        return None;
    }

    // everything is zero
    if n_neg + n_pos == 0 {
        return Some([0.; 4]);
    }

    let g = [u[1] - u[0], u[2] - u[1], u[3] - u[2]];
    let g = [g[perm[0]], g[perm[1]], g[perm[2]]];
    let g_norm_rcp = 1. / dual_norm(g);

    // FIXME: this requires for norm to be even
    // Support triangular anisotropies?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// Initializes the distance around the free boundary.
///
/// Based on the level set function with values `u` given on a regular grid, it computes the
/// distance from the _zero_ level set in the nodes of the triangles through which the level set
/// passes.  Stores the result in the preallocated slice `d`.
///
/// Nodes away from the boundary have their value set to `std::f64::MAX`.
///
/// Splits every cube into six tetrahedra and computes the distance on each of them.
pub fn init_dist_3d<F>(d: &mut [f64], u: &[f64], dim: (usize, usize, usize), mut dual_norm: F)
where
    F: FnMut([f64; 3]) -> f64,
{
    let (ni, nj, nk) = dim;
    assert_eq!(ni * nj * nk, u.len());
    assert_eq!(ni * nj * nk, d.len());
    let (si, sj, _sk) = (nj * nk, nk, 1);

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    // split each cube into 6 tetrahedrons
    let ids = [
        [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (1, 0, 0), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (0, 1, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1)],
    ];

    let perms = [
        [0, 1, 2],
        [0, 2, 1],
        [1, 0, 2],
        [2, 0, 1],
        [1, 2, 0],
        [2, 1, 0],
    ];

    for i in 1..ni {
        for j in 1..nj {
            let s = i * si + j * sj;
            let v = [u[s], u[s - si], u[s - sj], u[s - si - sj]];
            let mut all_pos_prev = v.iter().all(|&v| v > 0.);
            let mut all_neg_prev = v[0] < 0. && v[1] < 0. && v[2] < 0. && v[3] < 0.;
            for k in 1..nk {
                let s = i * si + j * sj + k;
                let v = [u[s], u[s - si], u[s - sj], u[s - si - sj]];
                let mut all_pos = v.iter().all(|&v| v > 0.);
                let mut all_neg = v[0] < 0. && v[1] < 0. && v[2] < 0. && v[3] < 0.;

                if !((all_pos_prev && all_pos) || (all_neg_prev && all_neg)) {
                    let mut v = [0.; 4];

                    for (idx, perm) in ids.iter().zip(perms.iter()) {
                        for m in 0..4 {
                            v[m] = u[s - idx[m].0 * si - idx[m].1 * sj - idx[m].2];
                        }

                        let r = tetrahedron_dist(v, &mut dual_norm, *perm);
                        if let Some(r) = r {
                            for m in 0..4 {
                                let q = s - idx[m].0 * si - idx[m].1 * sj - idx[m].2;
                                d[q] = min(d[q], r[m]);
                            }
                        }
                    }
                }
                all_pos_prev = all_pos;
                all_neg_prev = all_neg;
            }
        }
    }
}

/// Compute the anisotropic distance to the zero level set of a function on a axes-aligned
/// right triangle. The right angle is assumed to be at vertex 1.
///
/// `perm` specifies which leg of the triangle corresponds to the directions e₁ and e₂:
///
/// `[0, 1]`: 0-1 leg is parallel to e₁, 1-2 leg is parallel to e₂
/// `[1, 0]`: 0-1 leg is parallel to e₂, 1-2 leg is parallel to e₁
///
/// ```text
/// [0, 1]    [1, 0]
/// ------    ------
///
///     2      1--2
///    /|      | /
///   / |      |/
///  0--1      0
/// ```
///
fn triangle_dist<F>(mut u: [f64; 3], perm: [usize; 2], mut dual_norm: F) -> Option<[f64; 3]>
where
    F: FnMut([f64; 2]) -> f64,
{
    // check if sign differs (level set goes throught the triangle)
    if (u[0] > 0. && u[1] > 0. && u[2] > 0.) || (u[0] < 0. && u[1] < 0. && u[2] < 0.) {
        return None;
    }

    let g = [u[1] - u[0], u[2] - u[1]];
    let g = [g[perm[0]], g[perm[1]]];
    let norm = dual_norm(g);
    // all values are zero
    if norm == 0. {
        return Some([0., 0., 0.]);
    }
    let g_norm_rcp = 1. / norm;

    // TODO: This supports only even norms. Do we want to support triangle norms, for instance?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// As `init_dist_2d`, but for general anisotropic norm.
///
/// `dual_norm` is the __dual__ norm. It must be a positively one-homogeneous function.
pub fn init_dist_2d<F>(d: &mut [f64], u: &[f64], dim: (usize, usize), mut dual_norm: F)
where
    F: FnMut([f64; 2]) -> f64,
{
    let (nx, ny) = dim;
    assert_eq!(nx * ny, u.len());
    assert_eq!(nx * ny, d.len());

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    for j in 1..nx {
        for i in 1..ny {
            let s = j * ny + i;
            let v = [s - ny - 1, s - ny, s];
            let r = triangle_dist([u[v[0]], u[v[1]], u[v[2]]], [1, 0], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
            let v = [s - ny - 1, s - 1, s];
            let r = triangle_dist([u[v[0]], u[v[1]], u[v[2]]], [0, 1], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::norm::{DualNorm, EuclideanNorm};

    #[test]
    fn simple_triangles() {
        let eucl_triangle_dist = |v| triangle_dist(v, [0, 1], |p| EuclideanNorm.dual_norm(p));
        assert_eq!(eucl_triangle_dist([0., 0., 0.]), Some([0., 0., 0.]));
        assert_eq!(eucl_triangle_dist([1., 1., 1.]), None);
        assert_eq!(eucl_triangle_dist([-1., -1., -1.]), None);
        assert_eq!(
            eucl_triangle_dist([0., 1., 0.]),
            Some([0., 1. / (2f64).sqrt(), 0.])
        );
        assert_eq!(eucl_triangle_dist([0., -1., -1.]), Some([0., 1., 1.]));
        assert_eq!(eucl_triangle_dist([0., 1., 1.]), Some([0., 1., 1.]));
        assert_eq!(eucl_triangle_dist([1., 1., 0.]), Some([1., 1., 0.]));
        assert_eq!(eucl_triangle_dist([-1., 0., 0.]), Some([1., 0., 0.]));
        assert_eq!(
            eucl_triangle_dist([1., 0., 1.]),
            Some([1. / (2f64).sqrt(), 0., 1. / (2f64).sqrt()])
        );
    }

    #[test]
    fn anisotropic_norm_2d() {
        // Du = (1, 0)
        let u = [0., 0., 1., 1.];
        let mut d = [0.; 4];
        init_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0., 1., 1.]);

        // Du = (0, 1)
        let u = [0., 1., 0., 1.];
        let mut d = [0.; 4];
        init_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0.5, 0., 0.5]);
    }

    #[test]
    fn anisotropic_norm_3d() {
        // Du = (1, 0, 0)
        let u = [0., 0., 0., 0., 1., 1., 1., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0., 0., 1., 1., 1., 1.]);

        // Du = (0, 1, 0)
        let u = [0., 0., 1., 1., 0., 0., 1., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0.5, 0.5, 0., 0., 0.5, 0.5]);

        // Du = (0, 0, 1)
        let u = [0., 1., 0., 1., 0., 1., 0., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0.25, 0., 0.25, 0., 0.25, 0., 0.25]);
    }
}
<commit_msg>Tweak<commit_after>//! Initialization of the signed distance function near the level set.
use super::min;
use std;

/// Computes the signed distance function from a plane given as the _zero_ level set of a
/// linear function on a tetrahedron at 4 points with unit coordinates starting at (0, 0, 0) and
/// ending at (1, 1, 1), and in between exactly one coordinate changes from 0 to 1.
///
/// Inputs are `u`, the values at the vertices.
///
/// The function returns the values of the (non-signed) distance function or `None` if the zero
/// level set does not pass through the tetrahedron.
pub fn tetrahedron_dist<F>(mut u: [f64; 4], mut dual_norm: F, perm: [usize; 3]) -> Option<[f64; 4]>
where
    F: FnMut([f64; 3]) -> f64,
{
    let mut n_pos = 0;
    let mut n_neg = 0;
    for u in &mut u {
        if *u > 0. {
            n_pos += 1;
        } else if *u < 0. {
            n_neg += 1;
        }
    }
    // check if sign differs (level set goes throught the triangle)
    if n_neg == 4 || n_pos == 4 {
        return None;
    }

    // everything is zero
    if n_neg + n_pos == 0 {
        return Some([0.; 4]);
    }

    let g = [u[1] - u[0], u[2] - u[1], u[3] - u[2]];
    let g = [g[perm[0]], g[perm[1]], g[perm[2]]];
    let g_norm_rcp = 1. / dual_norm(g);

    // FIXME: this requires for norm to be even
    // Support triangular anisotropies?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// Initializes the distance around the free boundary.
///
/// Based on the level set function with values `u` given on a regular grid, it computes the
/// distance from the _zero_ level set in the nodes of the triangles through which the level set
/// passes.  Stores the result in the preallocated slice `d`.
///
/// Nodes away from the boundary have their value set to `std::f64::MAX`.
///
/// Splits every cube into six tetrahedra and computes the distance on each of them.
pub fn init_dist_3d<F>(d: &mut [f64], u: &[f64], dim: (usize, usize, usize), mut dual_norm: F)
where
    F: FnMut([f64; 3]) -> f64,
{
    let (ni, nj, nk) = dim;
    assert_eq!(ni * nj * nk, u.len());
    assert_eq!(ni * nj * nk, d.len());
    let (si, sj, _sk) = (nj * nk, nk, 1);

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    // split each cube into 6 tetrahedrons
    let ids = [
        [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (1, 0, 0), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (1, 1, 0), (1, 1, 1)],
        [(0, 0, 0), (0, 1, 0), (0, 1, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (1, 0, 1), (1, 1, 1)],
        [(0, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1)],
    ];

    let perms = [
        [0, 1, 2],
        [0, 2, 1],
        [1, 0, 2],
        [2, 0, 1],
        [1, 2, 0],
        [2, 1, 0],
    ];

    for i in 1..ni {
        for j in 1..nj {
            let s = i * si + j * sj;
            let v = [u[s], u[s - si], u[s - sj], u[s - si - sj]];
            let mut all_pos_prev = v.iter().all(|&v| v > 0.);
            let mut all_neg_prev = v.iter().all(|&v| v < 0.);
            for k in 1..nk {
                let s = i * si + j * sj + k;
                let v = [u[s], u[s - si], u[s - sj], u[s - si - sj]];
                let mut all_pos = v.iter().all(|&v| v > 0.);
                let mut all_neg = v[0] < 0. && v[1] < 0. && v[2] < 0. && v[3] < 0.;

                if !((all_pos_prev && all_pos) || (all_neg_prev && all_neg)) {
                    let mut v = [0.; 4];

                    for (idx, perm) in ids.iter().zip(perms.iter()) {
                        for m in 0..4 {
                            v[m] = u[s - idx[m].0 * si - idx[m].1 * sj - idx[m].2];
                        }

                        let r = tetrahedron_dist(v, &mut dual_norm, *perm);
                        if let Some(r) = r {
                            for m in 0..4 {
                                let q = s - idx[m].0 * si - idx[m].1 * sj - idx[m].2;
                                d[q] = min(d[q], r[m]);
                            }
                        }
                    }
                }
                all_pos_prev = all_pos;
                all_neg_prev = all_neg;
            }
        }
    }
}

/// Compute the anisotropic distance to the zero level set of a function on a axes-aligned
/// right triangle. The right angle is assumed to be at vertex 1.
///
/// `perm` specifies which leg of the triangle corresponds to the directions e₁ and e₂:
///
/// `[0, 1]`: 0-1 leg is parallel to e₁, 1-2 leg is parallel to e₂
/// `[1, 0]`: 0-1 leg is parallel to e₂, 1-2 leg is parallel to e₁
///
/// ```text
/// [0, 1]    [1, 0]
/// ------    ------
///
///     2      1--2
///    /|      | /
///   / |      |/
///  0--1      0
/// ```
///
fn triangle_dist<F>(mut u: [f64; 3], perm: [usize; 2], mut dual_norm: F) -> Option<[f64; 3]>
where
    F: FnMut([f64; 2]) -> f64,
{
    // check if sign differs (level set goes throught the triangle)
    if (u[0] > 0. && u[1] > 0. && u[2] > 0.) || (u[0] < 0. && u[1] < 0. && u[2] < 0.) {
        return None;
    }

    let g = [u[1] - u[0], u[2] - u[1]];
    let g = [g[perm[0]], g[perm[1]]];
    let norm = dual_norm(g);
    // all values are zero
    if norm == 0. {
        return Some([0., 0., 0.]);
    }
    let g_norm_rcp = 1. / norm;

    // TODO: This supports only even norms. Do we want to support triangle norms, for instance?
    for u in u.iter_mut() {
        *u = u.abs() * g_norm_rcp;
    }
    Some(u)
}

/// As `init_dist_2d`, but for general anisotropic norm.
///
/// `dual_norm` is the __dual__ norm. It must be a positively one-homogeneous function.
pub fn init_dist_2d<F>(d: &mut [f64], u: &[f64], dim: (usize, usize), mut dual_norm: F)
where
    F: FnMut([f64; 2]) -> f64,
{
    let (nx, ny) = dim;
    assert_eq!(nx * ny, u.len());
    assert_eq!(nx * ny, d.len());

    for d in &mut *d {
        *d = std::f64::MAX;
    }

    for j in 1..nx {
        for i in 1..ny {
            let s = j * ny + i;
            let v = [s - ny - 1, s - ny, s];
            let r = triangle_dist([u[v[0]], u[v[1]], u[v[2]]], [1, 0], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
            let v = [s - ny - 1, s - 1, s];
            let r = triangle_dist([u[v[0]], u[v[1]], u[v[2]]], [0, 1], &mut dual_norm);
            if let Some(e) = r {
                for i in 0..3 {
                    d[v[i]] = min(e[i], d[v[i]]);
                }
            }
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::norm::{DualNorm, EuclideanNorm};

    #[test]
    fn simple_triangles() {
        let eucl_triangle_dist = |v| triangle_dist(v, [0, 1], |p| EuclideanNorm.dual_norm(p));
        assert_eq!(eucl_triangle_dist([0., 0., 0.]), Some([0., 0., 0.]));
        assert_eq!(eucl_triangle_dist([1., 1., 1.]), None);
        assert_eq!(eucl_triangle_dist([-1., -1., -1.]), None);
        assert_eq!(
            eucl_triangle_dist([0., 1., 0.]),
            Some([0., 1. / (2f64).sqrt(), 0.])
        );
        assert_eq!(eucl_triangle_dist([0., -1., -1.]), Some([0., 1., 1.]));
        assert_eq!(eucl_triangle_dist([0., 1., 1.]), Some([0., 1., 1.]));
        assert_eq!(eucl_triangle_dist([1., 1., 0.]), Some([1., 1., 0.]));
        assert_eq!(eucl_triangle_dist([-1., 0., 0.]), Some([1., 0., 0.]));
        assert_eq!(
            eucl_triangle_dist([1., 0., 1.]),
            Some([1. / (2f64).sqrt(), 0., 1. / (2f64).sqrt()])
        );
    }

    #[test]
    fn anisotropic_norm_2d() {
        // Du = (1, 0)
        let u = [0., 0., 1., 1.];
        let mut d = [0.; 4];
        init_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0., 1., 1.]);

        // Du = (0, 1)
        let u = [0., 1., 0., 1.];
        let mut d = [0.; 4];
        init_dist_2d(&mut d, &u, (2, 2), |p| p[0].abs().max(2. * p[1].abs()));

        assert_eq!(d, [0., 0.5, 0., 0.5]);
    }

    #[test]
    fn anisotropic_norm_3d() {
        // Du = (1, 0, 0)
        let u = [0., 0., 0., 0., 1., 1., 1., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0., 0., 1., 1., 1., 1.]);

        // Du = (0, 1, 0)
        let u = [0., 0., 1., 1., 0., 0., 1., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0., 0.5, 0.5, 0., 0., 0.5, 0.5]);

        // Du = (0, 0, 1)
        let u = [0., 1., 0., 1., 0., 1., 0., 1.];
        let mut d = [0.; 8];
        init_dist_3d(&mut d, &u, (2, 2, 2), |p| {
            p[0].abs().max(2. * p[1].abs()).max(4. * p[2].abs())
        });

        assert_eq!(d, [0., 0.25, 0., 0.25, 0., 0.25, 0., 0.25]);
    }
}
<|endoftext|>


<commit_before>use mio;
use mio::tcp::*;
use mio::{TryRead,TryWrite};
use serde::ser::Serialize;
use serde::de::Deserialize;
use spki_sexp as sexp;
use std::fmt;
use std::collections::VecDeque;

use super::{ChainRepl, ChainReplMsg, OpResp, Operation, PeerMsg, ReplicationMessage};

const NL : u8 = '\n' as u8;
const DEFAULT_BUFSZ : usize = 1 << 12;

pub trait Encoder<T> {
    fn encode(&self, s: T) -> Vec<u8>;
}

pub trait Reader<T> {
    fn feed(&mut self, slice: &[u8]);
    fn take(&mut self) -> Option<T>;
    fn new(mio::Token) -> Self;
}

#[derive(Debug)]
pub struct LineConn<T> {
    socket: TcpStream,
    sock_status: mio::EventSet,
    token: mio::Token,
    read_eof: bool,
    failed: bool,
    write_buf: Vec<u8>,
    codec: T,
}

impl<T: Reader<ChainReplMsg> + Encoder<OpResp> + fmt::Debug> LineConn<T> {
    fn new(socket: TcpStream, token: mio::Token, codec: T) -> LineConn<T> {
        trace!("New client connection {:?} from {:?}", token, socket.local_addr());
        LineConn {
            socket: socket,
            sock_status: mio::EventSet::none(),
            token: token,
            write_buf: Vec::with_capacity(DEFAULT_BUFSZ),
            read_eof: false,
            failed: false,
            codec: codec,
        }
    }

    pub fn token(&self) -> mio::Token {
        self.token
    }

    pub fn initialize(&self, event_loop: &mut mio::EventLoop<ChainRepl>, token: mio::Token) {
        event_loop.register_opt(
                &self.socket,
                token,
                mio::EventSet::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot())
            .expect("event loop initialize");
    }

    // Event updates arrive here
    pub fn handle_event(&mut self, _event_loop: &mut mio::EventLoop<ChainRepl>, events: mio::EventSet) {
        self.sock_status.insert(events);
        trace!("LineConn::handle_event: {:?}; this time: {:?}; now: {:?}",
                self.socket.peer_addr(), events, self.sock_status);
    }

    // actions are processed here on down.

    pub fn process_rules<F: FnMut(ChainReplMsg)>(&mut self, event_loop: &mut mio::EventLoop<ChainRepl>,
        to_parent: &mut F) -> bool {

        if self.sock_status.is_readable() {
            self.read();
            self.sock_status.remove(mio::EventSet::readable());
        }

        let changed = self.process_buffer(to_parent);

        if self.sock_status.is_writable() {
            self.write();
            self.sock_status.remove(mio::EventSet::writable());
        }

        if !self.is_closed() {
            self.reinitialize(event_loop)
        }
        changed
    }

    fn process_buffer<F: FnMut(ChainReplMsg)>(&mut self, to_parent: &mut F) -> bool {
        let mut changed = false;
        // trace!("{:?}: Read buffer: {:?}", self.socket.peer_addr(), self.read_buf);
        while let Some(cmd) = self.codec.take() {
            debug!("Read message {:?}", cmd);
            to_parent(cmd);

            changed = true;
        }

        changed
    }

    fn read(&mut self) {
        let mut abuf = Vec::with_capacity(DEFAULT_BUFSZ);
        match self.socket.try_read_buf(&mut abuf) {
            Ok(Some(0)) => {
                debug!("{:?}: EOF!", self.socket.peer_addr());
                self.read_eof = true
            },
            Ok(Some(n)) => {
                trace!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.codec.feed(&abuf);
            },
            Ok(None) => {
                trace!("{:?}: Noop!", self.socket.peer_addr());
            },
            Err(e) => {
                error!("got an error trying to read; err={:?}", e);
                self.failed =true;
            }
        }
    }

    fn write(&mut self) {
        match self.socket.try_write(&mut self.write_buf) {
            Ok(Some(n)) => {
                trace!("{:?}: Wrote {} of {} in buffer", self.socket.peer_addr(), n,
                    self.write_buf.len());
                self.write_buf = self.write_buf[n..].to_vec();
                trace!("{:?}: Now {:?}b", self.socket.peer_addr(), self.write_buf.len());
            },
            Ok(None) => {
                trace!("Write unready");
            },
            Err(e) => {
                error!("got an error trying to write; err={:?}", e);
                self.failed = true;
            }
        }
    }

    fn reinitialize(&mut self, event_loop: &mut mio::EventLoop<ChainRepl>) {
        let mut flags = mio::EventSet::readable();
        if !self.write_buf.is_empty() {
            flags.insert(mio::EventSet::writable());
        }
        trace!("Registering {:?} with {:?}", self, flags);

        event_loop.reregister(
                &self.socket,
                self.token,
                flags,
                mio::PollOpt::oneshot()).expect("EventLoop#reinitialize")
    }

    pub fn is_closed(&self) -> bool {
        trace!("Closed? failed: {:?}; read_eof:{:?}; write_empty: {:?}", self.failed, self.read_eof, self.write_buf.is_empty());
        self.failed || (self.read_eof && self.write_buf.is_empty())
    }

    // Per item to output
    pub fn response(&mut self, s: OpResp) {
        debug!("{:?}: Response: {:?}", self.token, s);
        let chunk = self.codec.encode(s);
        self.write_buf.extend(chunk);
    }

}

#[derive(Debug)]
pub struct SexpPeer {
    token: mio::Token,
    packets: sexp::Packetiser,
}

impl SexpPeer {
    pub fn fresh(token: mio::Token) -> SexpPeer {
        SexpPeer { token: token, packets: sexp::Packetiser::new() }
    }
}

impl<T: Serialize> Encoder<T> for SexpPeer {
    fn encode(&self, s: T) -> Vec<u8> {
        sexp::as_bytes(&s).expect("Encode json response")
    }
}

impl Reader<ChainReplMsg> for SexpPeer {
    fn new(token: mio::Token) -> SexpPeer {
        Self::fresh(token)
    }

    fn feed(&mut self, slice: &[u8]) {
        self.packets.feed(slice)
    }
    fn take(&mut self) -> Option<ChainReplMsg> {
        if let Some(msg) = self.packets.take().expect("Pull packet") {
            let _ : ReplicationMessage = msg;
            debug!("Decoding PeerMsg: {:?}", msg);
            let ret = match msg {
                 ReplicationMessage { epoch, msg: PeerMsg::Commit(seqno, op) } =>
                    ChainReplMsg::Operation { source: self.token, epoch: Some(epoch), seqno: Some(seqno), op: op },
            };
            Some(ret)
        } else {
            None
        }
    }
}

impl Reader<OpResp> for SexpPeer {
    fn new(token: mio::Token) -> SexpPeer {
        Self::fresh(token)
    }
    fn feed(&mut self, slice: &[u8]) {
        self.packets.feed(slice)
    }
    fn take(&mut self) -> Option<OpResp> {
        if let Some(msg) = self.packets.take().expect("Pull packet") {
            debug!("Decoded OpResp: {:?}", msg);
            Some(msg)
        } else {
            None
        }
    }
}

impl LineConn<SexpPeer> {
    pub fn upstream(socket: TcpStream, token: mio::Token) -> LineConn<SexpPeer> {
        Self::new(socket, token, SexpPeer::fresh(token))
    }
}

#[derive(Debug)]
pub struct PlainClient {
    token: mio::Token,
    buf: VecDeque<u8>
}

impl<T: fmt::Debug> Encoder<T> for PlainClient {
    fn encode(&self, s: T) -> Vec<u8> {
        format!("{:?}\n", s).as_bytes().to_vec()
    }
}

impl Reader<ChainReplMsg> for PlainClient {
    fn new(token: mio::Token) -> PlainClient {
        PlainClient { token: token, buf: VecDeque::new() }
    }
    fn feed(&mut self, slice: &[u8]) {
        self.buf.extend(slice);
    }
    fn take(&mut self) -> Option<ChainReplMsg> {
        if let Some(idx) = self.buf.iter().enumerate().filter_map(|(i, &c)| if c == NL { Some(i+1) } else { None }).next() {
            let slice = self.buf.drain(..idx).collect::<Vec<_>>();
            let s = String::from_utf8_lossy(&slice);
            let op = if s.trim().is_empty() {
                Operation::Get
            } else {
                Operation::Set(s.trim().to_string())
            };
            let ret = ChainReplMsg::Operation { source: self.token, epoch: None, seqno: None, op: op };
            Some(ret)
        } else {
            None
        }
    }
}

impl LineConn<PlainClient> {
    pub fn client(socket: TcpStream, token: mio::Token) -> LineConn<PlainClient> {
        Self::new(socket, token, PlainClient::new(token))
    }
}
<commit_msg>Re-use read buffers.<commit_after>use mio;
use mio::tcp::*;
use mio::{TryRead,TryWrite};
use serde::ser::Serialize;
use serde::de::Deserialize;
use spki_sexp as sexp;
use std::fmt;
use std::collections::VecDeque;

use super::{ChainRepl, ChainReplMsg, OpResp, Operation, PeerMsg, ReplicationMessage};

const NL : u8 = '\n' as u8;
const DEFAULT_BUFSZ : usize = 1 << 12;

pub trait Encoder<T> {
    fn encode(&self, s: T) -> Vec<u8>;
}

pub trait Reader<T> {
    fn feed(&mut self, slice: &[u8]);
    fn take(&mut self) -> Option<T>;
    fn new(mio::Token) -> Self;
}

#[derive(Debug)]
pub struct LineConn<T> {
    socket: TcpStream,
    sock_status: mio::EventSet,
    token: mio::Token,
    read_eof: bool,
    failed: bool,
    write_buf: Vec<u8>,
    read_buf: Vec<u8>,
    codec: T,
}

impl<T: Reader<ChainReplMsg> + Encoder<OpResp> + fmt::Debug> LineConn<T> {
    fn new(socket: TcpStream, token: mio::Token, codec: T) -> LineConn<T> {
        trace!("New client connection {:?} from {:?}", token, socket.local_addr());
        LineConn {
            socket: socket,
            sock_status: mio::EventSet::none(),
            token: token,
            write_buf: Vec::with_capacity(DEFAULT_BUFSZ),
            read_buf: Vec::with_capacity(DEFAULT_BUFSZ),
            read_eof: false,
            failed: false,
            codec: codec,
        }
    }

    pub fn token(&self) -> mio::Token {
        self.token
    }

    pub fn initialize(&self, event_loop: &mut mio::EventLoop<ChainRepl>, token: mio::Token) {
        event_loop.register_opt(
                &self.socket,
                token,
                mio::EventSet::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot())
            .expect("event loop initialize");
    }

    // Event updates arrive here
    pub fn handle_event(&mut self, _event_loop: &mut mio::EventLoop<ChainRepl>, events: mio::EventSet) {
        self.sock_status.insert(events);
        trace!("LineConn::handle_event: {:?}; this time: {:?}; now: {:?}",
                self.socket.peer_addr(), events, self.sock_status);
    }

    // actions are processed here on down.

    pub fn process_rules<F: FnMut(ChainReplMsg)>(&mut self, event_loop: &mut mio::EventLoop<ChainRepl>,
        to_parent: &mut F) -> bool {

        if self.sock_status.is_readable() {
            self.read();
            self.sock_status.remove(mio::EventSet::readable());
        }

        let changed = self.process_buffer(to_parent);

        if self.sock_status.is_writable() {
            self.write();
            self.sock_status.remove(mio::EventSet::writable());
        }

        if !self.is_closed() {
            self.reinitialize(event_loop)
        }
        changed
    }

    fn process_buffer<F: FnMut(ChainReplMsg)>(&mut self, to_parent: &mut F) -> bool {
        let mut changed = false;
        // trace!("{:?}: Read buffer: {:?}", self.socket.peer_addr(), self.read_buf);
        while let Some(cmd) = self.codec.take() {
            debug!("Read message {:?}", cmd);
            to_parent(cmd);

            changed = true;
        }

        changed
    }

    fn read(&mut self) {
        match self.socket.try_read_buf(&mut self.read_buf) {
            Ok(Some(0)) => {
                debug!("{:?}: EOF!", self.socket.peer_addr());
                self.read_eof = true
            },
            Ok(Some(n)) => {
                trace!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.codec.feed(&self.read_buf);
                self.read_buf.clear();
            },
            Ok(None) => {
                trace!("{:?}: Noop!", self.socket.peer_addr());
            },
            Err(e) => {
                error!("got an error trying to read; err={:?}", e);
                self.failed =true;
            }
        }
    }

    fn write(&mut self) {
        match self.socket.try_write(&mut self.write_buf) {
            Ok(Some(n)) => {
                trace!("{:?}: Wrote {} of {} in buffer", self.socket.peer_addr(), n,
                    self.write_buf.len());
                self.write_buf = self.write_buf[n..].to_vec();
                trace!("{:?}: Now {:?}b", self.socket.peer_addr(), self.write_buf.len());
            },
            Ok(None) => {
                trace!("Write unready");
            },
            Err(e) => {
                error!("got an error trying to write; err={:?}", e);
                self.failed = true;
            }
        }
    }

    fn reinitialize(&mut self, event_loop: &mut mio::EventLoop<ChainRepl>) {
        let mut flags = mio::EventSet::readable();
        if !self.write_buf.is_empty() {
            flags.insert(mio::EventSet::writable());
        }
        trace!("Registering {:?} with {:?}", self, flags);

        event_loop.reregister(
                &self.socket,
                self.token,
                flags,
                mio::PollOpt::oneshot()).expect("EventLoop#reinitialize")
    }

    pub fn is_closed(&self) -> bool {
        trace!("Closed? failed: {:?}; read_eof:{:?}; write_empty: {:?}", self.failed, self.read_eof, self.write_buf.is_empty());
        self.failed || (self.read_eof && self.write_buf.is_empty())
    }

    // Per item to output
    pub fn response(&mut self, s: OpResp) {
        debug!("{:?}: Response: {:?}", self.token, s);
        let chunk = self.codec.encode(s);
        self.write_buf.extend(chunk);
    }

}

#[derive(Debug)]
pub struct SexpPeer {
    token: mio::Token,
    packets: sexp::Packetiser,
}

impl SexpPeer {
    pub fn fresh(token: mio::Token) -> SexpPeer {
        SexpPeer { token: token, packets: sexp::Packetiser::new() }
    }
}

impl<T: Serialize> Encoder<T> for SexpPeer {
    fn encode(&self, s: T) -> Vec<u8> {
        sexp::as_bytes(&s).expect("Encode json response")
    }
}

impl Reader<ChainReplMsg> for SexpPeer {
    fn new(token: mio::Token) -> SexpPeer {
        Self::fresh(token)
    }

    fn feed(&mut self, slice: &[u8]) {
        self.packets.feed(slice)
    }
    fn take(&mut self) -> Option<ChainReplMsg> {
        if let Some(msg) = self.packets.take().expect("Pull packet") {
            let _ : ReplicationMessage = msg;
            debug!("Decoding PeerMsg: {:?}", msg);
            let ret = match msg {
                 ReplicationMessage { epoch, msg: PeerMsg::Commit(seqno, op) } =>
                    ChainReplMsg::Operation { source: self.token, epoch: Some(epoch), seqno: Some(seqno), op: op },
            };
            Some(ret)
        } else {
            None
        }
    }
}

impl Reader<OpResp> for SexpPeer {
    fn new(token: mio::Token) -> SexpPeer {
        Self::fresh(token)
    }
    fn feed(&mut self, slice: &[u8]) {
        self.packets.feed(slice)
    }
    fn take(&mut self) -> Option<OpResp> {
        if let Some(msg) = self.packets.take().expect("Pull packet") {
            debug!("Decoded OpResp: {:?}", msg);
            Some(msg)
        } else {
            None
        }
    }
}

impl LineConn<SexpPeer> {
    pub fn upstream(socket: TcpStream, token: mio::Token) -> LineConn<SexpPeer> {
        Self::new(socket, token, SexpPeer::fresh(token))
    }
}

#[derive(Debug)]
pub struct PlainClient {
    token: mio::Token,
    buf: VecDeque<u8>
}

impl<T: fmt::Debug> Encoder<T> for PlainClient {
    fn encode(&self, s: T) -> Vec<u8> {
        format!("{:?}\n", s).as_bytes().to_vec()
    }
}

impl Reader<ChainReplMsg> for PlainClient {
    fn new(token: mio::Token) -> PlainClient {
        PlainClient { token: token, buf: VecDeque::new() }
    }
    fn feed(&mut self, slice: &[u8]) {
        self.buf.extend(slice);
    }
    fn take(&mut self) -> Option<ChainReplMsg> {
        if let Some(idx) = self.buf.iter().enumerate().filter_map(|(i, &c)| if c == NL { Some(i+1) } else { None }).next() {
            let slice = self.buf.drain(..idx).collect::<Vec<_>>();
            let s = String::from_utf8_lossy(&slice);
            let op = if s.trim().is_empty() {
                Operation::Get
            } else {
                Operation::Set(s.trim().to_string())
            };
            let ret = ChainReplMsg::Operation { source: self.token, epoch: None, seqno: None, op: op };
            Some(ret)
        } else {
            None
        }
    }
}

impl LineConn<PlainClient> {
    pub fn client(socket: TcpStream, token: mio::Token) -> LineConn<PlainClient> {
        Self::new(socket, token, PlainClient::new(token))
    }
}
<|endoftext|>


<commit_before>extern crate num;

use super::*;

use std::{str, ptr};

use self::num::ToPrimitive;

#[derive(Debug, PartialEq)]
pub struct LineWrapParameters {
    // number of lines that need an ending
    pub lines_with_endings: usize,
    // length of last line (which never needs an ending)
    pub last_line_len: usize,
    // length of lines that need an ending (which are always full lines), with their endings
    pub total_full_wrapped_lines_len: usize,
    // length of all lines, including endings for the ones that need them
    pub total_len: usize,
    // length of the line endings only
    pub total_line_endings_len: usize
}

/// Calculations about how many lines we'll get for a given line length, line ending, etc.
/// This assumes that the last line will not get an ending, even if it is the full line length.
pub fn line_wrap_parameters(input_len: usize, line_len: usize, line_ending: LineEnding)
                            -> LineWrapParameters {
    let line_ending_len = line_ending.len();

    if input_len <= line_len {
        // no wrapping needed
        return LineWrapParameters {
            lines_with_endings: 0,
            last_line_len: input_len,
            total_full_wrapped_lines_len: 0,
            total_len: input_len,
            total_line_endings_len: 0
        };
    };

    // num_lines_with_endings > 0, last_line_length > 0
    let (num_lines_with_endings, last_line_length) = if input_len % line_len > 0 {
        // Every full line has an ending since there is a partial line at the end
        (input_len / line_len, input_len % line_len)
    } else {
        // Every line is a full line, but no trailing ending.
        // Subtraction will not underflow since we know input_len > line_len.
        (input_len / line_len - 1, line_len)
    };

    // TODO should we expose exceeding usize via Result to be kind to 16-bit users? Or is that
    // always going to be a panic anyway in practice? If we choose to use a Result we could pull
    // line wrapping out of the normal encode path and have it be a separate step. Then only users
    // who need line wrapping would care about the possibility for error.

    let single_full_line_with_ending_len = line_len.checked_add(line_ending_len)
        .expect("Line length with ending exceeds usize");
    // length of just the full lines with line endings
    let total_full_wrapped_lines_len = num_lines_with_endings
        .checked_mul(single_full_line_with_ending_len)
        .expect("Full lines with endings length exceeds usize");
    // all lines with appropriate endings, including the last line
    let total_all_wrapped_len = total_full_wrapped_lines_len.checked_add(last_line_length)
        .expect("All lines with endings length exceeds usize");
    let total_line_endings_len = num_lines_with_endings.checked_mul(line_ending_len)
        .expect("Total line endings length exceeds usize");

    LineWrapParameters {
        lines_with_endings: num_lines_with_endings,
        last_line_len: last_line_length,
        total_full_wrapped_lines_len: total_full_wrapped_lines_len,
        total_len: total_all_wrapped_len,
        total_line_endings_len: total_line_endings_len
    }
}

/// Insert line endings into the encoded base64 after each complete line (except the last line, even
/// if it is complete).
/// The provided buffer must be large enough to handle the increased size after endings are
/// inserted.
/// `input_len` is the length of the encoded data in `encoded_buf`.
/// `line_len` is the width without line ending characters.
/// Returns the number of line ending bytes added.
pub fn line_wrap(encoded_buf: &mut [u8], input_len: usize, line_len: usize, line_ending: LineEnding) -> usize {
    let line_wrap_params = line_wrap_parameters(input_len, line_len, line_ending);

    assert!(encoded_buf.len() >= line_wrap_params.total_len,
    "Buffer must be able to hold encoded data after line wrapping");

    // Move the last line, either partial or full, by itself as it does not have a line ending
    // afterwards
    unsafe {
        let last_line_start = line_wrap_params.lines_with_endings.checked_mul(line_len)
            .and_then(|o| o.to_isize())
            .map(|o| encoded_buf.as_ptr().offset(o))
            .expect("Start of last line in input exceeds isize");
        // last line starts immediately after all the wrapped full lines
        let new_line_start = line_wrap_params.total_full_wrapped_lines_len.to_isize()
            .map(|o| encoded_buf.as_mut_ptr().offset(o))
            .expect("Full lines with endings length exceeds usize");

        ptr::copy(last_line_start, new_line_start, line_wrap_params.last_line_len);
    }

    let mut line_ending_bytes = 0;

    let line_len_isize = line_len.to_isize().expect("line_len must fit in isize");
    let line_ending_len = line_ending.len();

    // handle the full lines
    for line_num in 0..line_wrap_params.lines_with_endings {
        // doesn't underflow because line_num < lines_with_endings
        let lines_before_this_line = line_wrap_params.lines_with_endings - 1 - line_num;
        let line_start_offset = lines_before_this_line.checked_mul(line_len)
            .and_then(|l| l.to_isize())
            .expect("Line start offset exceeds isize");
        let total_endings_to_insert_before_this_line =
            lines_before_this_line.checked_mul(line_ending_len)
                .and_then(|t| t.to_isize())
                .expect("Cumulative line ending length before this line exceeds isize");

        unsafe {
            let orig_line_start = encoded_buf.as_ptr().offset(line_start_offset);

            let new_line_start = encoded_buf.as_mut_ptr().offset(line_start_offset)
                .offset(total_endings_to_insert_before_this_line);

            ptr::copy(orig_line_start, new_line_start, line_len);
            match line_ending {
                LineEnding::LF => {
                    ptr::write(new_line_start.offset(line_len_isize), b'\n');
                    line_ending_bytes += 1;
                }
                LineEnding::CRLF => {
                    ptr::write(new_line_start.offset(line_len_isize), b'\r');
                    ptr::write(new_line_start.offset(line_len_isize).offset(1), b'\n');
                    line_ending_bytes += 2;
                }
            }
        }
    }

    assert_eq!(line_wrap_params.total_line_endings_len, line_ending_bytes);

    line_ending_bytes
}

#[cfg(test)]
mod tests {
    extern crate rand;

    use super::super::*;
    use super::*;

    use self::rand::Rng;
    use self::rand::distributions::{IndependentSample, Range};

    #[test]
    fn line_params_perfect_multiple_of_line_length_lf() {
        let params = line_wrap_parameters(100, 20, LineEnding::LF);

        assert_eq!(LineWrapParameters {
            lines_with_endings: 4,
            last_line_len: 20,
            total_full_wrapped_lines_len: 84,
            total_len: 104,
            total_line_endings_len: 4
        }, params);
    }

    #[test]
    fn line_params_partial_last_line_crlf() {
        let params = line_wrap_parameters(103, 20, LineEnding::CRLF);

        assert_eq!(LineWrapParameters {
            lines_with_endings: 5,
            last_line_len: 3,
            total_full_wrapped_lines_len: 110,
            total_len: 113,
            total_line_endings_len: 10
        }, params);
    }

    #[test]
    fn line_params_line_len_1_crlf() {
        let params = line_wrap_parameters(100, 1, LineEnding::CRLF);

        assert_eq!(LineWrapParameters {
            lines_with_endings: 99,
            last_line_len: 1,
            total_full_wrapped_lines_len: 99 * 3,
            total_len: 99 * 3 + 1,
            total_line_endings_len: 99 * 2
        }, params);
    }

    #[test]
    fn line_params_line_len_longer_than_input_crlf() {
        let params = line_wrap_parameters(100, 200, LineEnding::CRLF);

        assert_eq!(LineWrapParameters {
            lines_with_endings: 0,
            last_line_len: 100,
            total_full_wrapped_lines_len: 0,
            total_len: 100,
            total_line_endings_len: 0
        }, params);
    }

    #[test]
    fn line_params_line_len_same_as_input_crlf() {
        let params = line_wrap_parameters(100, 100, LineEnding::CRLF);

        assert_eq!(LineWrapParameters {
            lines_with_endings: 0,
            last_line_len: 100,
            total_full_wrapped_lines_len: 0,
            total_len: 100,
            total_line_endings_len: 0
        }, params);
    }

    #[test]
    fn line_wrap_length_1_lf() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4];

        do_line_wrap(&mut buf, 1, LineEnding::LF);

        assert_eq!(vec![0x1, 0xA, 0x2, 0xA, 0x3, 0xA, 0x4], buf);
    }

    #[test]
    fn line_wrap_length_1_crlf() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4];

        do_line_wrap(&mut buf, 1, LineEnding::CRLF);

        assert_eq!(vec![0x1, 0xD, 0xA, 0x2, 0xD, 0xA, 0x3, 0xD, 0xA, 0x4], buf);
    }

    #[test]
    fn line_wrap_length_2_lf_full_lines() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4];

        do_line_wrap(&mut buf, 2, LineEnding::LF);

        assert_eq!(vec![0x1, 0x2, 0xA, 0x3, 0x4], buf);
    }

    #[test]
    fn line_wrap_length_2_crlf_full_lines() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4];

        do_line_wrap(&mut buf, 2, LineEnding::CRLF);

        assert_eq!(vec![0x1, 0x2, 0xD, 0xA, 0x3, 0x4], buf);
    }

    #[test]
    fn line_wrap_length_2_lf_partial_line() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4, 0x5];

        do_line_wrap(&mut buf, 2, LineEnding::LF);

        assert_eq!(vec![0x1, 0x2, 0xA, 0x3, 0x4, 0xA, 0x5], buf);
    }

    #[test]
    fn line_wrap_length_2_crlf_partial_line() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4, 0x5];

        do_line_wrap(&mut buf, 2, LineEnding::CRLF);

        assert_eq!(vec![0x1, 0x2, 0xD, 0xA, 0x3, 0x4, 0xD, 0xA, 0x5], buf);
    }

    #[test]
    fn line_wrap_random() {
        let mut buf: Vec<u8> = Vec::new();
        let buf_range = Range::new(10, 1000);
        let line_range = Range::new(10, 100);
        let mut rng = rand::weak_rng();

        for _ in 0..10_000 {
            buf.clear();

            let buf_len = buf_range.ind_sample(&mut rng);
            let line_len = line_range.ind_sample(&mut rng);
            let line_ending = if rng.gen() {
                LineEnding::LF
            } else {
                LineEnding::CRLF
            };
            let line_ending_len = line_ending.len();

            for _ in 0..buf_len {
                buf.push(rng.gen());
            }

            let line_wrap_params = line_wrap_parameters(buf_len, line_len, line_ending);

            let not_wrapped_buf = buf.to_vec();

            let line_ending_bytes = do_line_wrap(&mut buf, line_len, line_ending);

            assert_eq!(buf_len + line_wrap_params.lines_with_endings * line_ending_len, buf.len());
            assert_eq!(line_wrap_params.total_len, buf.len());
            assert_eq!(line_wrap_params.total_line_endings_len, line_ending_bytes);

            // remove the endings
            for line_ending_num in 0..line_wrap_params.lines_with_endings {
                let line_ending_offset = (line_ending_num + 1) * line_len;

                for _ in 0..line_ending_len {
                    buf.remove(line_ending_offset);
                }
            }

            assert_eq!(not_wrapped_buf, buf);
        }
    }

    fn do_line_wrap(buf: &mut Vec<u8>, line_len: usize, line_ending: LineEnding) -> usize {
        // a 3x inflation is enough for the worst case: line length 1, crlf ending
        let orig_len = buf.len();
        buf.reserve(orig_len * 2);
        unsafe {
            buf.set_len(orig_len * 3);
        }

        let bytes_written = line_wrap(&mut buf[..], orig_len, line_len, line_ending);

        unsafe {
            buf.set_len(orig_len + bytes_written);
        }

        bytes_written
    }
}
<commit_msg>Add explanatory comment<commit_after>extern crate num;

use super::*;

use std::{str, ptr};

use self::num::ToPrimitive;

#[derive(Debug, PartialEq)]
pub struct LineWrapParameters {
    // number of lines that need an ending
    pub lines_with_endings: usize,
    // length of last line (which never needs an ending)
    pub last_line_len: usize,
    // length of lines that need an ending (which are always full lines), with their endings
    pub total_full_wrapped_lines_len: usize,
    // length of all lines, including endings for the ones that need them
    pub total_len: usize,
    // length of the line endings only
    pub total_line_endings_len: usize
}

/// Calculations about how many lines we'll get for a given line length, line ending, etc.
/// This assumes that the last line will not get an ending, even if it is the full line length.
pub fn line_wrap_parameters(input_len: usize, line_len: usize, line_ending: LineEnding)
                            -> LineWrapParameters {
    let line_ending_len = line_ending.len();

    if input_len <= line_len {
        // no wrapping needed
        return LineWrapParameters {
            lines_with_endings: 0,
            last_line_len: input_len,
            total_full_wrapped_lines_len: 0,
            total_len: input_len,
            total_line_endings_len: 0
        };
    };

    // num_lines_with_endings > 0, last_line_length > 0
    let (num_lines_with_endings, last_line_length) = if input_len % line_len > 0 {
        // Every full line has an ending since there is a partial line at the end
        (input_len / line_len, input_len % line_len)
    } else {
        // Every line is a full line, but no trailing ending.
        // Subtraction will not underflow since we know input_len > line_len.
        (input_len / line_len - 1, line_len)
    };

    // TODO should we expose exceeding usize via Result to be kind to 16-bit users? Or is that
    // always going to be a panic anyway in practice? If we choose to use a Result we could pull
    // line wrapping out of the normal encode path and have it be a separate step. Then only users
    // who need line wrapping would care about the possibility for error.

    let single_full_line_with_ending_len = line_len.checked_add(line_ending_len)
        .expect("Line length with ending exceeds usize");
    // length of just the full lines with line endings
    let total_full_wrapped_lines_len = num_lines_with_endings
        .checked_mul(single_full_line_with_ending_len)
        .expect("Full lines with endings length exceeds usize");
    // all lines with appropriate endings, including the last line
    let total_all_wrapped_len = total_full_wrapped_lines_len.checked_add(last_line_length)
        .expect("All lines with endings length exceeds usize");
    let total_line_endings_len = num_lines_with_endings.checked_mul(line_ending_len)
        .expect("Total line endings length exceeds usize");

    LineWrapParameters {
        lines_with_endings: num_lines_with_endings,
        last_line_len: last_line_length,
        total_full_wrapped_lines_len: total_full_wrapped_lines_len,
        total_len: total_all_wrapped_len,
        total_line_endings_len: total_line_endings_len
    }
}

/// Insert line endings into the encoded base64 after each complete line (except the last line, even
/// if it is complete).
/// The provided buffer must be large enough to handle the increased size after endings are
/// inserted.
/// `input_len` is the length of the encoded data in `encoded_buf`.
/// `line_len` is the width without line ending characters.
/// Returns the number of line ending bytes added.
pub fn line_wrap(encoded_buf: &mut [u8], input_len: usize, line_len: usize, line_ending: LineEnding) -> usize {
    let line_wrap_params = line_wrap_parameters(input_len, line_len, line_ending);

    // ptr.offset() is undefined if it wraps, and there is no checked_offset(). However, because
    // we perform this check up front to make sure we have enough capacity, we know that none of
    // the subsequent pointer operations (assuming they implement the desired behavior of course!)
    // will overflow.
    assert!(encoded_buf.len() >= line_wrap_params.total_len,
        "Buffer must be able to hold encoded data after line wrapping");

    // Move the last line, either partial or full, by itself as it does not have a line ending
    // afterwards
    unsafe {
        let last_line_start = line_wrap_params.lines_with_endings.checked_mul(line_len)
            .and_then(|o| o.to_isize())
            .map(|o| encoded_buf.as_ptr().offset(o))
            .expect("Start of last line in input exceeds isize");
        // last line starts immediately after all the wrapped full lines
        let new_line_start = line_wrap_params.total_full_wrapped_lines_len.to_isize()
            .map(|o| encoded_buf.as_mut_ptr().offset(o))
            .expect("Full lines with endings length exceeds usize");

        ptr::copy(last_line_start, new_line_start, line_wrap_params.last_line_len);
    }

    let mut line_ending_bytes = 0;

    let line_len_isize = line_len.to_isize().expect("line_len must fit in isize");
    let line_ending_len = line_ending.len();

    // handle the full lines
    for line_num in 0..line_wrap_params.lines_with_endings {
        // doesn't underflow because line_num < lines_with_endings
        let lines_before_this_line = line_wrap_params.lines_with_endings - 1 - line_num;
        let line_start_offset = lines_before_this_line.checked_mul(line_len)
            .and_then(|l| l.to_isize())
            .expect("Line start offset exceeds isize");
        let total_endings_to_insert_before_this_line =
            lines_before_this_line.checked_mul(line_ending_len)
                .and_then(|t| t.to_isize())
                .expect("Cumulative line ending length before this line exceeds isize");

        unsafe {
            let orig_line_start = encoded_buf.as_ptr().offset(line_start_offset);

            let new_line_start = encoded_buf.as_mut_ptr().offset(line_start_offset)
                .offset(total_endings_to_insert_before_this_line);

            ptr::copy(orig_line_start, new_line_start, line_len);
            match line_ending {
                LineEnding::LF => {
                    ptr::write(new_line_start.offset(line_len_isize), b'\n');
                    line_ending_bytes += 1;
                }
                LineEnding::CRLF => {
                    ptr::write(new_line_start.offset(line_len_isize), b'\r');
                    ptr::write(new_line_start.offset(line_len_isize).offset(1), b'\n');
                    line_ending_bytes += 2;
                }
            }
        }
    }

    assert_eq!(line_wrap_params.total_line_endings_len, line_ending_bytes);

    line_ending_bytes
}

#[cfg(test)]
mod tests {
    extern crate rand;

    use super::super::*;
    use super::*;

    use self::rand::Rng;
    use self::rand::distributions::{IndependentSample, Range};

    #[test]
    fn line_params_perfect_multiple_of_line_length_lf() {
        let params = line_wrap_parameters(100, 20, LineEnding::LF);

        assert_eq!(LineWrapParameters {
            lines_with_endings: 4,
            last_line_len: 20,
            total_full_wrapped_lines_len: 84,
            total_len: 104,
            total_line_endings_len: 4
        }, params);
    }

    #[test]
    fn line_params_partial_last_line_crlf() {
        let params = line_wrap_parameters(103, 20, LineEnding::CRLF);

        assert_eq!(LineWrapParameters {
            lines_with_endings: 5,
            last_line_len: 3,
            total_full_wrapped_lines_len: 110,
            total_len: 113,
            total_line_endings_len: 10
        }, params);
    }

    #[test]
    fn line_params_line_len_1_crlf() {
        let params = line_wrap_parameters(100, 1, LineEnding::CRLF);

        assert_eq!(LineWrapParameters {
            lines_with_endings: 99,
            last_line_len: 1,
            total_full_wrapped_lines_len: 99 * 3,
            total_len: 99 * 3 + 1,
            total_line_endings_len: 99 * 2
        }, params);
    }

    #[test]
    fn line_params_line_len_longer_than_input_crlf() {
        let params = line_wrap_parameters(100, 200, LineEnding::CRLF);

        assert_eq!(LineWrapParameters {
            lines_with_endings: 0,
            last_line_len: 100,
            total_full_wrapped_lines_len: 0,
            total_len: 100,
            total_line_endings_len: 0
        }, params);
    }

    #[test]
    fn line_params_line_len_same_as_input_crlf() {
        let params = line_wrap_parameters(100, 100, LineEnding::CRLF);

        assert_eq!(LineWrapParameters {
            lines_with_endings: 0,
            last_line_len: 100,
            total_full_wrapped_lines_len: 0,
            total_len: 100,
            total_line_endings_len: 0
        }, params);
    }

    #[test]
    fn line_wrap_length_1_lf() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4];

        do_line_wrap(&mut buf, 1, LineEnding::LF);

        assert_eq!(vec![0x1, 0xA, 0x2, 0xA, 0x3, 0xA, 0x4], buf);
    }

    #[test]
    fn line_wrap_length_1_crlf() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4];

        do_line_wrap(&mut buf, 1, LineEnding::CRLF);

        assert_eq!(vec![0x1, 0xD, 0xA, 0x2, 0xD, 0xA, 0x3, 0xD, 0xA, 0x4], buf);
    }

    #[test]
    fn line_wrap_length_2_lf_full_lines() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4];

        do_line_wrap(&mut buf, 2, LineEnding::LF);

        assert_eq!(vec![0x1, 0x2, 0xA, 0x3, 0x4], buf);
    }

    #[test]
    fn line_wrap_length_2_crlf_full_lines() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4];

        do_line_wrap(&mut buf, 2, LineEnding::CRLF);

        assert_eq!(vec![0x1, 0x2, 0xD, 0xA, 0x3, 0x4], buf);
    }

    #[test]
    fn line_wrap_length_2_lf_partial_line() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4, 0x5];

        do_line_wrap(&mut buf, 2, LineEnding::LF);

        assert_eq!(vec![0x1, 0x2, 0xA, 0x3, 0x4, 0xA, 0x5], buf);
    }

    #[test]
    fn line_wrap_length_2_crlf_partial_line() {
        let mut buf = vec![0x1, 0x2, 0x3, 0x4, 0x5];

        do_line_wrap(&mut buf, 2, LineEnding::CRLF);

        assert_eq!(vec![0x1, 0x2, 0xD, 0xA, 0x3, 0x4, 0xD, 0xA, 0x5], buf);
    }

    #[test]
    fn line_wrap_random() {
        let mut buf: Vec<u8> = Vec::new();
        let buf_range = Range::new(10, 1000);
        let line_range = Range::new(10, 100);
        let mut rng = rand::weak_rng();

        for _ in 0..10_000 {
            buf.clear();

            let buf_len = buf_range.ind_sample(&mut rng);
            let line_len = line_range.ind_sample(&mut rng);
            let line_ending = if rng.gen() {
                LineEnding::LF
            } else {
                LineEnding::CRLF
            };
            let line_ending_len = line_ending.len();

            for _ in 0..buf_len {
                buf.push(rng.gen());
            }

            let line_wrap_params = line_wrap_parameters(buf_len, line_len, line_ending);

            let not_wrapped_buf = buf.to_vec();

            let line_ending_bytes = do_line_wrap(&mut buf, line_len, line_ending);

            assert_eq!(buf_len + line_wrap_params.lines_with_endings * line_ending_len, buf.len());
            assert_eq!(line_wrap_params.total_len, buf.len());
            assert_eq!(line_wrap_params.total_line_endings_len, line_ending_bytes);

            // remove the endings
            for line_ending_num in 0..line_wrap_params.lines_with_endings {
                let line_ending_offset = (line_ending_num + 1) * line_len;

                for _ in 0..line_ending_len {
                    buf.remove(line_ending_offset);
                }
            }

            assert_eq!(not_wrapped_buf, buf);
        }
    }

    fn do_line_wrap(buf: &mut Vec<u8>, line_len: usize, line_ending: LineEnding) -> usize {
        // a 3x inflation is enough for the worst case: line length 1, crlf ending
        let orig_len = buf.len();
        buf.reserve(orig_len * 2);
        unsafe {
            buf.set_len(orig_len * 3);
        }

        let bytes_written = line_wrap(&mut buf[..], orig_len, line_len, line_ending);

        unsafe {
            buf.set_len(orig_len + bytes_written);
        }

        bytes_written
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;

use proc_macro2::{Delimiter, Span};

use buffer::Cursor;
use error::{self, Error};
use span::IntoSpans;
use token::Token;

/// Support for checking the next token in a stream to decide how to parse.
///
/// Use [`ParseStream::lookahead1`] to construct this object.
///
/// [`ParseStream::lookahead1`]: struct.ParseBuffer.html#method.lookahead1
pub struct Lookahead1<'a> {
    scope: Span,
    cursor: Cursor<'a>,
    comparisons: RefCell<Vec<String>>,
}

pub fn new(scope: Span, cursor: Cursor) -> Lookahead1 {
    Lookahead1 {
        scope: scope,
        cursor: cursor,
        comparisons: RefCell::new(Vec::new()),
    }
}

impl<'a> Lookahead1<'a> {
    pub fn peek<T: Peek>(&self, token: T) -> bool {
        let _ = token;
        if T::Token::peek(self) {
            return true;
        }
        self.comparisons.borrow_mut().push(T::Token::display());
        false
    }

    pub fn error(self) -> Error {
        let comparisons = self.comparisons.borrow();
        match comparisons.len() {
            0 => if self.cursor.eof() {
                Error::new(self.scope, "unexpected end of input")
            } else {
                Error::new(self.cursor.span(), "unexpected token")
            },
            1 => {
                let message = format!("expected {}", comparisons[0]);
                error::new_at(self.scope, self.cursor, message)
            }
            _ => {
                let join = comparisons.join(", ");
                let message = format!("expected one of: {}", join);
                error::new_at(self.scope, self.cursor, message)
            }
        }
    }

    pub fn cursor(&self) -> Cursor<'a> {
        self.cursor
    }
}

/// Types that can be parsed by looking at just one token.
///
/// This trait is sealed and cannot be implemented for types outside of Syn.
pub trait Peek: private::Sealed {
    // Not public API.
    #[doc(hidden)]
    type Token: Token;
}

impl<F: FnOnce(TokenMarker) -> T, T: Token> Peek for F {
    type Token = T;
}

pub enum TokenMarker {}

impl<S> IntoSpans<S> for TokenMarker {
    fn into_spans(self) -> S {
        match self {}
    }
}

pub fn is_delimiter(lookahead: &Lookahead1, delimiter: Delimiter) -> bool {
    lookahead.cursor.group(delimiter).is_some()
}

mod private {
    use super::{Token, TokenMarker};
    pub trait Sealed {}
    impl<F: FnOnce(TokenMarker) -> T, T: Token> Sealed for F {}
}
<commit_msg>Document Peek trait<commit_after>use std::cell::RefCell;

use proc_macro2::{Delimiter, Span};

use buffer::Cursor;
use error::{self, Error};
use span::IntoSpans;
use token::Token;

/// Support for checking the next token in a stream to decide how to parse.
///
/// Use [`ParseStream::lookahead1`] to construct this object.
///
/// [`ParseStream::lookahead1`]: struct.ParseBuffer.html#method.lookahead1
pub struct Lookahead1<'a> {
    scope: Span,
    cursor: Cursor<'a>,
    comparisons: RefCell<Vec<String>>,
}

pub fn new(scope: Span, cursor: Cursor) -> Lookahead1 {
    Lookahead1 {
        scope: scope,
        cursor: cursor,
        comparisons: RefCell::new(Vec::new()),
    }
}

impl<'a> Lookahead1<'a> {
    pub fn peek<T: Peek>(&self, token: T) -> bool {
        let _ = token;
        if T::Token::peek(self) {
            return true;
        }
        self.comparisons.borrow_mut().push(T::Token::display());
        false
    }

    pub fn error(self) -> Error {
        let comparisons = self.comparisons.borrow();
        match comparisons.len() {
            0 => if self.cursor.eof() {
                Error::new(self.scope, "unexpected end of input")
            } else {
                Error::new(self.cursor.span(), "unexpected token")
            },
            1 => {
                let message = format!("expected {}", comparisons[0]);
                error::new_at(self.scope, self.cursor, message)
            }
            _ => {
                let join = comparisons.join(", ");
                let message = format!("expected one of: {}", join);
                error::new_at(self.scope, self.cursor, message)
            }
        }
    }

    pub fn cursor(&self) -> Cursor<'a> {
        self.cursor
    }
}

/// Types that can be parsed by looking at just one token.
///
/// Use [`ParseStream::peek`] to peek one of these types in a parse stream
/// without consuming it from the stream.
///
/// This trait is sealed and cannot be implemented for types outside of Syn.
///
/// [`ParseStream::peek`]: struct.ParseBuffer.html#method.peek
pub trait Peek: private::Sealed {
    // Not public API.
    #[doc(hidden)]
    type Token: Token;
}

impl<F: FnOnce(TokenMarker) -> T, T: Token> Peek for F {
    type Token = T;
}

pub enum TokenMarker {}

impl<S> IntoSpans<S> for TokenMarker {
    fn into_spans(self) -> S {
        match self {}
    }
}

pub fn is_delimiter(lookahead: &Lookahead1, delimiter: Delimiter) -> bool {
    lookahead.cursor.group(delimiter).is_some()
}

mod private {
    use super::{Token, TokenMarker};
    pub trait Sealed {}
    impl<F: FnOnce(TokenMarker) -> T, T: Token> Sealed for F {}
}
<|endoftext|>


<commit_before>use delivery::git::git_command;
use delivery::utils::copy_recursive;
use delivery::utils::say;
use std::io::prelude::*;
use tempdir::TempDir;
use std::fs::File;
use std::path::Path;
use support::paths::fixture_file;
use std::process::{Command, Output};
use std::env;
use rustc_serialize::json::Json;
use delivery::utils::path_join_many::PathJoinMany;

// ** Functions used in tests **

fn setup() {
    say::turn_off_spinner();
}

/// Sets up a mock delivery git project from the test_repo fixture.
/// Includes copying in the `.delivery/config.json` that you plan
/// on using.
fn setup_mock_delivery_project_git(dot_config: &str) -> TempDir {
    let tmpdir = TempDir::new("mock-delivery-remote").unwrap();
    let test_repo_path = fixture_file("test_repo");
    panic_on_error!(copy_recursive(&test_repo_path.join(".delivery"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("README.md"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("cookbooks"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&fixture_file(dot_config), &tmpdir.path().join_many(&[".delivery", "config.json"])));
    panic_on_error!(git_command(&["init", tmpdir.path().to_str().unwrap()], tmpdir.path()));
    panic_on_error!(git_command(&["add", "."], tmpdir.path()));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], tmpdir.path()));
    tmpdir
}

/// Given a path, it copies the build cookbook into it, and turns it
/// into a git repository
fn setup_build_cookbook_project(tmpdir: &Path) {
    let build_cookbook_path = fixture_file("delivery_test");
    panic_on_error!(copy_recursive(&build_cookbook_path, &tmpdir.to_path_buf()));
    panic_on_error!(git_command(&["init", tmpdir.join("delivery_test").to_str().unwrap()], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["add", "."], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], &tmpdir.join("delivery_test")));
}

/// Clones a mock delivery git project to a local copy, as if it was
/// on a workstation. Also creates a mock delivery remote pointing at
/// the on-disk mocked delivery project.
fn setup_local_project_clone(delivery_project_git: &Path) -> TempDir {
    let tmpdir = TempDir::new("local-project").unwrap();
    panic_on_error!(git_command(&["clone",
                                  delivery_project_git.to_str().unwrap(),
                                  tmpdir.path().to_str().unwrap()
                                 ], tmpdir.path()));
    panic_on_error!(git_command(&["remote", "add", "delivery", delivery_project_git.to_str().unwrap()], tmpdir.path()));
    let mut command = delivery_cmd();
    command.arg("setup")
           .arg("--user").arg("cavalera")
           .arg("--server").arg("localhost")
           .arg("--ent").arg("family")
           .arg("--org").arg("sepultura")
           .arg("--for").arg("master")
           .arg("--config-path").arg(tmpdir.path().to_str().unwrap());
    assert_command_successful(&mut command, &tmpdir);
    tmpdir
}

/// Makes a change to a project on the named branch. Creates a
/// file named `filename` and writes some stuff to it.
///
/// When it returns, the project you pass in will be left on your
/// new branch.
fn setup_change(tmpdir: &Path, branch: &str, filename: &str) {
    panic_on_error!(git_command(&["checkout", "master"], tmpdir));
    panic_on_error!(git_command(&["branch", branch], tmpdir));
    {
        let mut f = panic_on_error!(File::create(&tmpdir.join(filename)));
        panic_on_error!(f.write_all(b"I like cookies"));
    }
    panic_on_error!(git_command(&["add", filename], tmpdir));
    panic_on_error!(git_command(&["commit", "-a", "-m", filename], tmpdir));
}

/// Checks out the named branch
fn setup_checkout_branch(tmpdir: &TempDir, branch: &str) {
    panic_on_error!(git_command(&["checkout", branch], tmpdir.path()));
}

/// Runs the `Command` in `Dir`, and makes sure it exists with 0
/// Returns the result
fn assert_command_successful(command: &mut Command, dir: &TempDir) -> Output {
    let result = panic_on_error!(command.current_dir(&dir.path()).output());
    if ! result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Failed command {:?}\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.path().to_str().unwrap());
    };
    result
}

/// Same as `assert_command_successful` above, excepts it asserts the command
/// exits with a non-0 status code
fn assert_command_failed(command: &mut Command, dir: &TempDir) -> Output {
    let result = panic_on_error!(command.current_dir(&dir.path()).output());
    if result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Command {:?} should have failed!\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.path().to_str().unwrap());
    };
    result
}

/// Builds the command to run delivery review
fn delivery_review_command(pipeline: &str) -> Command {
    let mut command = delivery_cmd();
    command.arg("review").arg("--no-open").arg("--for").arg(pipeline);
    command
}

/// Builds the command to run a sample job
fn delivery_verify_command(job_root: &TempDir) -> Command {
    let mut command = delivery_cmd();
    command.arg("job")
           .arg("verify")
           .arg("unit")
           .arg("--no-spinner")
           .arg("--job-root").arg(job_root.path().to_str().unwrap());
    command
}

/// Calls delivery review, and creates the two stub branches that the
/// api would create (`_reviews/PIPELINE/BRANCH/1` and `_reviews/PIPELINE/BRANCH/latest`)
fn delivery_review(local: &TempDir, remote: &TempDir, branch: &str, pipeline: &str) {
    panic_on_error!(git_command(&["checkout", branch], local.path()));
    let mut command = delivery_review_command(pipeline);
    assert_command_successful(&mut command, local);

    // Stub out the behavior of the delivery-api
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/1", pipeline, branch)], remote.path()));
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/latest", pipeline, branch)], remote.path()));
}

/// Returns a Command set to the delivery binary created when you
/// ran `cargo test`.
fn delivery_cmd() -> Command {
    let mut delivery_path = env::current_exe().unwrap();
    delivery_path.pop();
    Command::new(delivery_path.join("delivery").to_str().unwrap())
}

/// A handy debugging function. Insert it when you want to sleep,
/// pass it a tmpdir, and you can inspect it.
///
/// Make sure you run `cargo test -- --nocapture` to see the output.
#[allow(dead_code)]
fn debug_sleep(tmpdir: &TempDir) {
    println!("Sleeping for 1000 seconds for {:?}", tmpdir.path());
    panic_on_error!(Command::new("sleep").arg("1000").output());
}

// ** Actual tests **

// Tests `delivery review`. Fails if the command fails, or if we fail to create
// the remote branch _for/master/rust/test, which is what we need to push to
// the API server.
test!(review {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git, "_for/master/rust/test");
});

test!(review_with_a_v1_config {
    let delivery_project_git = setup_mock_delivery_project_git("v1_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git, "_for/master/rust/test");
});

test!(review_without_dependencies {
    let delivery_project_git = setup_mock_delivery_project_git("no_deps_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git, "_for/master/rust/test");
});

test!(review_with_an_invalid_config {
    let delivery_project_git = setup_mock_delivery_project_git("invalid_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_review_command("rust/test");
    assert_command_failed(&mut command, &local_project);
});

test!(job_verify_unit_with_path_config {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project);
});

test!(job_verify_unit_with_git_config {
    let delivery_project_git = setup_mock_delivery_project_git("git_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_build_cookbook_project(&job_root.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project);
});

// TODO: This test requires internet access... we should move it out
// into an Acceptance-stage test instead of here in unit tests. It's
// impossible to run on a plane, for instance :(
test!(job_verify_unit_with_supermarket_config {
    let delivery_project_git = setup_mock_delivery_project_git("supermarket_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_failed(&mut command, &local_project);
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd"]).is_dir());
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd", "templates", "default", "magic.erb"]).is_file());
});

test!(job_verify_dna_json {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project);
    let mut dna_file = panic_on_error!(File::open(&job_root.path().join_many(&["chef", "dna.json"])));
    let mut dna_json = String::new();
    panic_on_error!(dna_file.read_to_string(&mut dna_json));
    let dna_data = panic_on_error!(Json::from_str(&dna_json));
    match dna_data.find_path(&["delivery", "workspace", "repo"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("repo").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/repo, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "chef"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("chef").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/chef, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "cache"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("cache").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/cache, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "root"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().to_str().unwrap());
        },
        None => panic!("No delivery/workspace/root, {}", dna_data)
    };
    match dna_data.find_path(&["delivery_builder", "build_user"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), "dbuild");
        },
        None => panic!("No delivery_builderl/build_user, {}", dna_data)
    };
});
<commit_msg>Path not TempDir for setup_checkout_branch<commit_after>use delivery::git::git_command;
use delivery::utils::copy_recursive;
use delivery::utils::say;
use std::io::prelude::*;
use tempdir::TempDir;
use std::fs::File;
use std::path::Path;
use support::paths::fixture_file;
use std::process::{Command, Output};
use std::env;
use rustc_serialize::json::Json;
use delivery::utils::path_join_many::PathJoinMany;

// ** Functions used in tests **

fn setup() {
    say::turn_off_spinner();
}

/// Sets up a mock delivery git project from the test_repo fixture.
/// Includes copying in the `.delivery/config.json` that you plan
/// on using.
fn setup_mock_delivery_project_git(dot_config: &str) -> TempDir {
    let tmpdir = TempDir::new("mock-delivery-remote").unwrap();
    let test_repo_path = fixture_file("test_repo");
    panic_on_error!(copy_recursive(&test_repo_path.join(".delivery"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("README.md"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&test_repo_path.join("cookbooks"), &tmpdir.path().to_path_buf()));
    panic_on_error!(copy_recursive(&fixture_file(dot_config), &tmpdir.path().join_many(&[".delivery", "config.json"])));
    panic_on_error!(git_command(&["init", tmpdir.path().to_str().unwrap()], tmpdir.path()));
    panic_on_error!(git_command(&["add", "."], tmpdir.path()));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], tmpdir.path()));
    tmpdir
}

/// Given a path, it copies the build cookbook into it, and turns it
/// into a git repository
fn setup_build_cookbook_project(tmpdir: &Path) {
    let build_cookbook_path = fixture_file("delivery_test");
    panic_on_error!(copy_recursive(&build_cookbook_path, &tmpdir.to_path_buf()));
    panic_on_error!(git_command(&["init", tmpdir.join("delivery_test").to_str().unwrap()], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["add", "."], &tmpdir.join("delivery_test")));
    panic_on_error!(git_command(&["commit", "-a", "-m", "Initial Commit"], &tmpdir.join("delivery_test")));
}

/// Clones a mock delivery git project to a local copy, as if it was
/// on a workstation. Also creates a mock delivery remote pointing at
/// the on-disk mocked delivery project.
fn setup_local_project_clone(delivery_project_git: &Path) -> TempDir {
    let tmpdir = TempDir::new("local-project").unwrap();
    panic_on_error!(git_command(&["clone",
                                  delivery_project_git.to_str().unwrap(),
                                  tmpdir.path().to_str().unwrap()
                                 ], tmpdir.path()));
    panic_on_error!(git_command(&["remote", "add", "delivery", delivery_project_git.to_str().unwrap()], tmpdir.path()));
    let mut command = delivery_cmd();
    command.arg("setup")
           .arg("--user").arg("cavalera")
           .arg("--server").arg("localhost")
           .arg("--ent").arg("family")
           .arg("--org").arg("sepultura")
           .arg("--for").arg("master")
           .arg("--config-path").arg(tmpdir.path().to_str().unwrap());
    assert_command_successful(&mut command, &tmpdir);
    tmpdir
}

/// Makes a change to a project on the named branch. Creates a
/// file named `filename` and writes some stuff to it.
///
/// When it returns, the project you pass in will be left on your
/// new branch.
fn setup_change(tmpdir: &Path, branch: &str, filename: &str) {
    panic_on_error!(git_command(&["checkout", "master"], tmpdir));
    panic_on_error!(git_command(&["branch", branch], tmpdir));
    {
        let mut f = panic_on_error!(File::create(&tmpdir.join(filename)));
        panic_on_error!(f.write_all(b"I like cookies"));
    }
    panic_on_error!(git_command(&["add", filename], tmpdir));
    panic_on_error!(git_command(&["commit", "-a", "-m", filename], tmpdir));
}

/// Checks out the named branch
fn setup_checkout_branch(tmpdir: &Path, branch: &str) {
    panic_on_error!(git_command(&["checkout", branch], tmpdir));
}

/// Runs the `Command` in `Dir`, and makes sure it exists with 0
/// Returns the result
fn assert_command_successful(command: &mut Command, dir: &TempDir) -> Output {
    let result = panic_on_error!(command.current_dir(&dir.path()).output());
    if ! result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Failed command {:?}\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.path().to_str().unwrap());
    };
    result
}

/// Same as `assert_command_successful` above, excepts it asserts the command
/// exits with a non-0 status code
fn assert_command_failed(command: &mut Command, dir: &TempDir) -> Output {
    let result = panic_on_error!(command.current_dir(&dir.path()).output());
    if result.status.success() {
        let output = String::from_utf8_lossy(&result.stdout);
        let error = String::from_utf8_lossy(&result.stderr);
        panic!("Command {:?} should have failed!\nOUT: {}\nERR: {}\nPath: {}", command, &output, &error, dir.path().to_str().unwrap());
    };
    result
}

/// Builds the command to run delivery review
fn delivery_review_command(pipeline: &str) -> Command {
    let mut command = delivery_cmd();
    command.arg("review").arg("--no-open").arg("--for").arg(pipeline);
    command
}

/// Builds the command to run a sample job
fn delivery_verify_command(job_root: &TempDir) -> Command {
    let mut command = delivery_cmd();
    command.arg("job")
           .arg("verify")
           .arg("unit")
           .arg("--no-spinner")
           .arg("--job-root").arg(job_root.path().to_str().unwrap());
    command
}

/// Calls delivery review, and creates the two stub branches that the
/// api would create (`_reviews/PIPELINE/BRANCH/1` and `_reviews/PIPELINE/BRANCH/latest`)
fn delivery_review(local: &TempDir, remote: &TempDir, branch: &str, pipeline: &str) {
    panic_on_error!(git_command(&["checkout", branch], local.path()));
    let mut command = delivery_review_command(pipeline);
    assert_command_successful(&mut command, local);

    // Stub out the behavior of the delivery-api
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/1", pipeline, branch)], remote.path()));
    panic_on_error!(git_command(&["branch", &format!("_reviews/{}/{}/latest", pipeline, branch)], remote.path()));
}

/// Returns a Command set to the delivery binary created when you
/// ran `cargo test`.
fn delivery_cmd() -> Command {
    let mut delivery_path = env::current_exe().unwrap();
    delivery_path.pop();
    Command::new(delivery_path.join("delivery").to_str().unwrap())
}

/// A handy debugging function. Insert it when you want to sleep,
/// pass it a tmpdir, and you can inspect it.
///
/// Make sure you run `cargo test -- --nocapture` to see the output.
#[allow(dead_code)]
fn debug_sleep(tmpdir: &TempDir) {
    println!("Sleeping for 1000 seconds for {:?}", tmpdir.path());
    panic_on_error!(Command::new("sleep").arg("1000").output());
}

// ** Actual tests **

// Tests `delivery review`. Fails if the command fails, or if we fail to create
// the remote branch _for/master/rust/test, which is what we need to push to
// the API server.
test!(review {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_with_a_v1_config {
    let delivery_project_git = setup_mock_delivery_project_git("v1_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_without_dependencies {
    let delivery_project_git = setup_mock_delivery_project_git("no_deps_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    delivery_review(&local_project, &delivery_project_git, "rust/test", "master");
    setup_checkout_branch(&delivery_project_git.path(), "_for/master/rust/test");
});

test!(review_with_an_invalid_config {
    let delivery_project_git = setup_mock_delivery_project_git("invalid_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_review_command("rust/test");
    assert_command_failed(&mut command, &local_project);
});

test!(job_verify_unit_with_path_config {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project);
});

test!(job_verify_unit_with_git_config {
    let delivery_project_git = setup_mock_delivery_project_git("git_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_build_cookbook_project(&job_root.path());
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project);
});

// TODO: This test requires internet access... we should move it out
// into an Acceptance-stage test instead of here in unit tests. It's
// impossible to run on a plane, for instance :(
test!(job_verify_unit_with_supermarket_config {
    let delivery_project_git = setup_mock_delivery_project_git("supermarket_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_failed(&mut command, &local_project);
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd"]).is_dir());
    assert!(job_root.path().join_many(&["chef", "cookbooks", "httpd", "templates", "default", "magic.erb"]).is_file());
});

test!(job_verify_dna_json {
    let delivery_project_git = setup_mock_delivery_project_git("path_config.json");
    let local_project = setup_local_project_clone(&delivery_project_git.path());
    let job_root = TempDir::new("job-root").unwrap();
    setup_change(&local_project.path(), "rust/test", "freaky");
    let mut command = delivery_verify_command(&job_root);
    assert_command_successful(&mut command, &local_project);
    let mut dna_file = panic_on_error!(File::open(&job_root.path().join_many(&["chef", "dna.json"])));
    let mut dna_json = String::new();
    panic_on_error!(dna_file.read_to_string(&mut dna_json));
    let dna_data = panic_on_error!(Json::from_str(&dna_json));
    match dna_data.find_path(&["delivery", "workspace", "repo"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("repo").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/repo, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "chef"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("chef").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/chef, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "cache"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().join("cache").to_str().unwrap());
        },
        None => panic!("No delivery/workspace/cache, {}", dna_data)
    };
    match dna_data.find_path(&["delivery", "workspace", "root"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), job_root.path().to_str().unwrap());
        },
        None => panic!("No delivery/workspace/root, {}", dna_data)
    };
    match dna_data.find_path(&["delivery_builder", "build_user"]) {
        Some(data) => {
            assert!(data.is_string());
            assert_eq!(data.as_string().unwrap(), "dbuild");
        },
        None => panic!("No delivery_builderl/build_user, {}", dna_data)
    };
});
<|endoftext|>


<commit_before>#[macro_use]
extern crate bson;
extern crate chrono;
extern crate rustc_serialize;

mod modules;

use bson::Bson;
use bson::spec::BinarySubtype;
use bson::oid::ObjectId;
use chrono::offset::utc::UTC;

#[test]
fn test_format() {
    let id_string = "thisismyname";
    let string_bytes : Vec<_> = id_string.bytes().collect();
    let mut bytes = [0; 12];

    for i in 0..12 {
        bytes[i] = string_bytes[i];
    }

    let id = ObjectId::with_bytes(bytes);
    let date = UTC::now();

    let doc = doc! {
        "float" => 2.4,
        "string" => "hello",
        "array" => ["testing", 1],
        "doc" => {
            "fish" => "in",
            "a" => "barrel",
            "!" => 1
        },
        "bool" => true,
        "null" => (Bson::Null),
        "regexp" => (Bson::RegExp("s[ao]d".to_owned(), "i".to_owned())),
        "code" => (Bson::JavaScriptCode("function(x) { return x._id; }".to_owned())),
        "i32" => 12,
        "i64" => (-55),
        "timestamp" => (Bson::TimeStamp(229999444)),
        "binary" => (Bson::Binary(BinarySubtype::Md5, "thingies".to_owned().into_bytes())),
        "_id" => id,
        "date" => (Bson::UtcDatetime(date))
    };

    let expected = format!("{{ float: 2.4, string: \"hello\", array: [\"testing\", 1], doc: {{ fish: \"in\", a: \"barrel\", !: 1 }}, bool: true, null: null, regexp: /s[ao]d/i, code: function(x) {{ return x._id; }}, i32: 12, i64: -55, timestamp: Timestamp(0, 229999444), binary: BinData(5, \"thingies\"), _id: ObjectId(\"{}\"), date: Date(\"{}\") }}", id_string, date);

    assert_eq!(expected, format!("{}", doc));
}
<commit_msg>reverted back to previous `macro_use` specifying which macros to import<commit_after>#[macro_use(bson, doc)]
extern crate bson;
extern crate chrono;
extern crate rustc_serialize;

mod modules;

use bson::Bson;
use bson::spec::BinarySubtype;
use bson::oid::ObjectId;
use chrono::offset::utc::UTC;

#[test]
fn test_format() {
    let id_string = "thisismyname";
    let string_bytes : Vec<_> = id_string.bytes().collect();
    let mut bytes = [0; 12];

    for i in 0..12 {
        bytes[i] = string_bytes[i];
    }

    let id = ObjectId::with_bytes(bytes);
    let date = UTC::now();

    let doc = doc! {
        "float" => 2.4,
        "string" => "hello",
        "array" => ["testing", 1],
        "doc" => {
            "fish" => "in",
            "a" => "barrel",
            "!" => 1
        },
        "bool" => true,
        "null" => (Bson::Null),
        "regexp" => (Bson::RegExp("s[ao]d".to_owned(), "i".to_owned())),
        "code" => (Bson::JavaScriptCode("function(x) { return x._id; }".to_owned())),
        "i32" => 12,
        "i64" => (-55),
        "timestamp" => (Bson::TimeStamp(229999444)),
        "binary" => (Bson::Binary(BinarySubtype::Md5, "thingies".to_owned().into_bytes())),
        "_id" => id,
        "date" => (Bson::UtcDatetime(date))
    };

    let expected = format!("{{ float: 2.4, string: \"hello\", array: [\"testing\", 1], doc: {{ fish: \"in\", a: \"barrel\", !: 1 }}, bool: true, null: null, regexp: /s[ao]d/i, code: function(x) {{ return x._id; }}, i32: 12, i64: -55, timestamp: Timestamp(0, 229999444), binary: BinData(5, \"thingies\"), _id: ObjectId(\"{}\"), date: Date(\"{}\") }}", id_string, date);

    assert_eq!(expected, format!("{}", doc));
}
<|endoftext|>