<commit_before>use math::*;

pub type MeshIndex = u32;

/// The raw data representing a mesh in memory.
///
/// Meshes are represented as list of vertex positions and a list of faces.
/// Each face is represented as 3 indices into the vertex array.
#[derive(Debug)]
pub struct Mesh {
    vertex_data: Vec<f32>,
    indices:     Vec<MeshIndex>,

    position: VertexAttribute,
    normal:   Option<VertexAttribute>,
    texcoord: Vec<VertexAttribute>,
}

impl Mesh {
    pub fn vertex_data(&self) -> &[f32] {
        &*self.vertex_data
    }

    pub fn indices(&self) -> &[MeshIndex] {
        &*self.indices
    }

    pub fn position(&self) -> VertexAttribute {
        self.position
    }

    pub fn normal(&self) -> Option<VertexAttribute> {
        self.normal
    }

    pub fn texcoord(&self) -> &[VertexAttribute] {
        &*self.texcoord
    }
}

/// Represents a single vertex in a mesh with all of its supported attributes.
#[derive(Debug, Clone)]
pub struct Vertex {
    pub position: Point,
    pub normal: Option<Vector3>,

    /// Support an arbitrary number of texture units. The actual maximum is dependent on hardware
    /// and so is not limited by polygon directly. If the number of
    pub texcoord: Vec<Vector2>,
}

impl Vertex {
    pub fn new(position: Point) -> Vertex {
        Vertex {
            position: position,
            normal: None,
            texcoord: Vec::new(),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct VertexAttribute {
    pub offset: usize,
    pub stride: usize,
}

#[derive(Debug, Clone, Copy)]
pub enum BuildMeshError {
    IndexOutOfBounds {
        vertex_count: MeshIndex,
        index: MeshIndex,
    },

    /// Indicates that one or more attributes had a count that did not match the total number of
    /// vertices.
    IncorrectAttributeCount,
}

#[derive(Debug, Clone)]
pub struct MeshBuilder {
    position_data: Vec<Point>,
    normal_data: Vec<Vector3>,
    texcoord_data: Vec<Vec<Vector3>>,

    indices:  Vec<u32>,
}

// TODO: I'd like to support building the mesh by specifying all of each attribute at once, since
// that seems like a common use case for game development. We still want to support building it
// vert-by-vert because that works best in other situations (e.g. COLLADA files).
impl MeshBuilder {
    pub fn new() -> MeshBuilder {
        MeshBuilder {
            position_data: Vec::new(),
            normal_data:   Vec::new(),
            texcoord_data: Vec::new(),
            indices:       Vec::new(),
        }
    }

    pub fn add_vertex(mut self, vertex: Vertex) -> MeshBuilder {
        self.position_data.push(vertex.position);

        if let Some(normal) = vertex.normal {
            self.normal_data.push(normal);
        }

        // TODO: Handle texcoord data.

        self
    }

    pub fn add_index(mut self, index: MeshIndex) -> MeshBuilder {
        self.indices.push(index);
        self
    }

    pub fn set_position_data(mut self, position_data: &[Point]) -> MeshBuilder {
        self.position_data.clear();
        self.position_data.extend(position_data);
        self
    }

    pub fn set_normal_data(mut self, normal_data: &[Vector3]) -> MeshBuilder {
        self.normal_data.clear();
        self.normal_data.extend(normal_data);
        self
    }

    pub fn set_texcoord_data(mut self, _texcoord_data: &[Vector2], _texcoord_index: MeshIndex) -> MeshBuilder {
        // TODO

        self
    }

    pub fn set_indices(mut self, indices: &[u32]) -> MeshBuilder {
        self.indices.clear();
        self.indices.extend(indices);
        self
    }

    pub fn build(self) -> Result<Mesh, BuildMeshError> {
        // Validate the mesh data.

        // The vertex count is defined by the position data, since position is the only required
        // vertex attribute.
        let vertex_count = self.position_data.len();

        // TODO: Validate texcoord data.
        if self.normal_data.len() != 0 && self.normal_data.len() != vertex_count {
            return Err(BuildMeshError::IncorrectAttributeCount);
        }

        // Make sure all indices at least point to a valid vertex.
        for index in self.indices.iter().cloned() {
            if index >= vertex_count as MeshIndex {
                return Err(BuildMeshError::IndexOutOfBounds {
                    vertex_count: vertex_count as MeshIndex,
                    index: index,
                });
            }
        }

        // TODO: Make sure all normals are normalized?

        // Create the mesh.
        let mut vertex_data =
            Vec::<f32>::with_capacity(
                self.position_data.len() * 4
              + self.normal_data.len() * 3);
        vertex_data.extend(Point::as_ref(&*self.position_data));
        vertex_data.extend(Vector3::as_ref(&*self.normal_data));

        // TODO: Add texcoord data.

        Ok(Mesh {
            vertex_data: vertex_data,
            indices: self.indices,

            position: VertexAttribute {
                offset: 0,
                stride: 4,
            },

            normal: if self.normal_data.len() > 0 {
                Some(VertexAttribute {
                    offset: self.position_data.len() * 4,
                    stride: 3,
                })
            } else {
                None
            },

            texcoord: Vec::new(),
        })
    }
}
<commit_msg>feature(polygon::MeshBuilder): support texcoord vertex attributes<commit_after>use math::*;

pub type MeshIndex = u32;

/// The raw data representing a mesh in memory.
///
/// Meshes are represented as list of vertex positions and a list of faces.
/// Each face is represented as 3 indices into the vertex array.
#[derive(Debug)]
pub struct Mesh {
    vertex_data: Vec<f32>,
    indices:     Vec<MeshIndex>,

    position: VertexAttribute,
    normal:   Option<VertexAttribute>,
    texcoord: Vec<VertexAttribute>,
}

impl Mesh {
    pub fn vertex_data(&self) -> &[f32] {
        &*self.vertex_data
    }

    pub fn indices(&self) -> &[MeshIndex] {
        &*self.indices
    }

    pub fn position(&self) -> VertexAttribute {
        self.position
    }

    pub fn normal(&self) -> Option<VertexAttribute> {
        self.normal
    }

    pub fn texcoord(&self) -> &[VertexAttribute] {
        &*self.texcoord
    }
}

/// Represents a single vertex in a mesh with all of its supported attributes.
#[derive(Debug, Clone)]
pub struct Vertex {
    pub position: Point,
    pub normal: Option<Vector3>,

    /// Support an arbitrary number of texture units. The actual maximum is dependent on hardware
    /// and so is not limited by polygon directly. If the number of
    pub texcoord: Vec<Vector2>,
}

impl Vertex {
    pub fn new(position: Point) -> Vertex {
        Vertex {
            position: position,
            normal: None,
            texcoord: Vec::new(),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct VertexAttribute {
    pub offset: usize,
    pub stride: usize,
}

#[derive(Debug, Clone, Copy)]
pub enum BuildMeshError {
    IndexOutOfBounds {
        vertex_count: MeshIndex,
        index: MeshIndex,
    },

    /// Indicates that one or more attributes had a count that did not match the total number of
    /// vertices.
    IncorrectAttributeCount,
}

#[derive(Debug, Clone)]
pub struct MeshBuilder {
    position_data: Vec<Point>,
    normal_data: Vec<Vector3>,
    texcoord_data: Vec<Vec<Vector2>>,

    indices:  Vec<u32>,
}

// TODO: I'd like to support building the mesh by specifying all of each attribute at once, since
// that seems like a common use case for game development. We still want to support building it
// vert-by-vert because that works best in other situations (e.g. COLLADA files).
impl MeshBuilder {
    pub fn new() -> MeshBuilder {
        MeshBuilder {
            position_data: Vec::new(),
            normal_data:   Vec::new(),
            texcoord_data: Vec::new(),
            indices:       Vec::new(),
        }
    }

    pub fn add_vertex(&mut self, vertex: Vertex) {
        self.position_data.push(vertex.position);

        if let Some(normal) = vertex.normal {
            self.normal_data.push(normal);
        }

        // Ensure there is a list for each texcoord.
        while self.texcoord_data.len() < vertex.texcoord.len() {
            self.texcoord_data.push(Vec::new());
        }

        // Add each texcoord to its corresponding list.
        let iter = vertex.texcoord.iter().zip(self.texcoord_data.iter_mut());
        for (texcoord, texcoord_data) in iter {
            texcoord_data.push(*texcoord);
        }
    }

    pub fn add_index(mut self, index: MeshIndex) -> MeshBuilder {
        self.indices.push(index);
        self
    }

    pub fn set_position_data(mut self, position_data: &[Point]) -> MeshBuilder {
        self.position_data.clear();
        self.position_data.extend(position_data);
        self
    }

    pub fn set_normal_data(mut self, normal_data: &[Vector3]) -> MeshBuilder {
        self.normal_data.clear();
        self.normal_data.extend(normal_data);
        self
    }

    pub fn set_texcoord_data(mut self, texcoord_data: &[&[Vector2]]) -> MeshBuilder {
        if texcoord_data.len() == 0 {
            return self;
        }

        for (src, dest) in texcoord_data.iter().zip(self.texcoord_data.iter_mut()) {
            dest.clear();
            dest.extend(*src);
        }

        self
    }

    pub fn set_indices(mut self, indices: &[u32]) -> MeshBuilder {
        self.indices.clear();
        self.indices.extend(indices);
        self
    }

    pub fn build(self) -> Result<Mesh, BuildMeshError> {
        // The vertex count is defined by the position data, since position is the only required
        // vertex attribute.
        let vertex_count = self.position_data.len();

        // Check that there is enough attribute data for each one.
        if self.normal_data.len() != 0 && self.normal_data.len() != vertex_count {
            return Err(BuildMeshError::IncorrectAttributeCount);
        }

        for texcoord_set in &self.texcoord_data {
            if texcoord_set.len() != vertex_count {
                return Err(BuildMeshError::IncorrectAttributeCount);
            }
        }

        // Make sure all indices at least point to a valid vertex.
        for index in self.indices.iter().cloned() {
            if index >= vertex_count as MeshIndex {
                return Err(BuildMeshError::IndexOutOfBounds {
                    vertex_count: vertex_count as MeshIndex,
                    index: index,
                });
            }
        }

        // TODO: Check for degenerate triangles? Actually, should that be a failure or a warning?

        let float_count = {
            let mut float_count =
                self.position_data.len() * 4
              + self.normal_data.len() * 3;
            for texcoord_set in &self.texcoord_data {
                float_count += texcoord_set.len() * 2;
            }

            float_count
        };

        // Create the mesh.
        let mut vertex_data = Vec::<f32>::with_capacity(float_count);

        // Setup position data.
        let position_attrib = VertexAttribute {
            offset: 0,
            stride: 4,
        };
        vertex_data.extend(Point::as_ref(&*self.position_data));

        // Setup normal data.
        let normal_attrib = if self.normal_data.len() > 0 {
            let attrib = VertexAttribute {
                offset: vertex_data.len(),
                stride: 3,
            };
            vertex_data.extend(Vector3::as_ref(&*self.normal_data));

            Some(attrib)
        } else {
            None
        };

        // Setup texcoord data.
        let mut texcoord_attribs = Vec::new();
        for texcoord_set in &self.texcoord_data {
            texcoord_attribs.push(VertexAttribute {
                offset: vertex_data.len(),
                stride: 2,
            });
            vertex_data.extend(Vector2::as_ref(&*texcoord_set));
        }

        // By our powers combined! We are! A mesh.
        Ok(Mesh {
            vertex_data: vertex_data,
            indices: self.indices,

            position: position_attrib,
            normal: normal_attrib,
            texcoord: texcoord_attribs,
        })
    }
}
<|endoftext|>


<commit_before>use math::*;

pub type MeshIndex = u32;

/// The raw data representing a mesh in memory.
///
/// Meshes are represented as list of vertex positions and a list of faces.
/// Each face is represented as 3 indices into the vertex array.
#[derive(Debug, Clone)]
pub struct Mesh {
    vertex_data: Vec<f32>,
    indices:     Vec<MeshIndex>,

    position: VertexAttribute,
    normal:   Option<VertexAttribute>,
    texcoord: Vec<VertexAttribute>,
}

impl Mesh {
    pub fn vertex_data(&self) -> &[f32] {
        &*self.vertex_data
    }

    pub fn indices(&self) -> &[MeshIndex] {
        &*self.indices
    }

    pub fn position(&self) -> VertexAttribute {
        self.position
    }

    pub fn normal(&self) -> Option<VertexAttribute> {
        self.normal
    }

    pub fn texcoord(&self) -> &[VertexAttribute] {
        &*self.texcoord
    }
}

/// Represents a single vertex in a mesh with all of its supported attributes.
#[derive(Debug, Clone)]
pub struct Vertex {
    pub position: Point,
    pub normal: Option<Vector3>,

    /// Support an arbitrary number of texture units. The actual maximum is dependent on hardware
    /// and so is not limited by polygon directly. If the number of
    pub texcoord: Vec<Vector2>,
}

impl Vertex {
    pub fn new(position: Point) -> Vertex {
        Vertex {
            position: position,
            normal: None,
            texcoord: Vec::new(),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct VertexAttribute {
    pub offset: usize,
    pub stride: usize,
}

#[derive(Debug, Clone, Copy)]
pub enum BuildMeshError {
    IndexOutOfBounds {
        vertex_count: MeshIndex,
        index: MeshIndex,
    },

    /// Indicates that one or more attributes had a count that did not match the total number of
    /// vertices.
    IncorrectAttributeCount,
}

#[derive(Debug, Clone)]
pub struct MeshBuilder {
    position_data: Vec<Point>,
    normal_data: Vec<Vector3>,
    texcoord_data: Vec<Vec<Vector2>>,

    indices:  Vec<u32>,
}

/// Provides a safe interface for building a mesh from raw vertex data.
///
/// `MeshBuilder` supports two methods for specifying mesh data: Providing a series of `Vertex`
/// objects, or directly setting the data for each vertex attribute at once. Once all vertex data
/// has been set calling `build()` will validate the mesh data and produce a `Mesh` object.
///
/// The mesh builder performs validity tests when building the mesh and mesh compilation can fail
/// if the mesh data is invalid in some way. The following validity tests are performed:
///
/// - Check for different data count for different attributes (e.g. if the position attribute data
///   for a different number of elements than the normal attribute).
/// - Any of the indicies would be out of bounds for the given vertex data.
impl MeshBuilder {
    pub fn new() -> MeshBuilder {
        MeshBuilder {
            position_data: Vec::new(),
            normal_data:   Vec::new(),
            texcoord_data: Vec::new(),
            indices:       Vec::new(),
        }
    }

    pub fn add_vertex(&mut self, vertex: Vertex) {
        self.position_data.push(vertex.position);

        if let Some(normal) = vertex.normal {
            self.normal_data.push(normal);
        }

        // Ensure there is a list for each texcoord.
        while self.texcoord_data.len() < vertex.texcoord.len() {
            self.texcoord_data.push(Vec::new());
        }

        // Add each texcoord to its corresponding list.
        let iter = vertex.texcoord.iter().zip(self.texcoord_data.iter_mut());
        for (texcoord, texcoord_data) in iter {
            texcoord_data.push(*texcoord);
        }
    }

    pub fn add_index(mut self, index: MeshIndex) -> MeshBuilder {
        self.indices.push(index);
        self
    }

    pub fn set_position_data(mut self, position_data: &[Point]) -> MeshBuilder {
        self.position_data.clear();
        self.position_data.extend(position_data);
        self
    }

    pub fn set_normal_data(mut self, normal_data: &[Vector3]) -> MeshBuilder {
        self.normal_data.clear();
        self.normal_data.extend(normal_data);
        self
    }

    pub fn set_texcoord_data(mut self, texcoord_data: &[&[Vector2]]) -> MeshBuilder {
        if texcoord_data.len() == 0 {
            return self;
        }

        for (src, dest) in texcoord_data.iter().zip(self.texcoord_data.iter_mut()) {
            dest.clear();
            dest.extend(*src);
        }

        self
    }

    pub fn set_indices(mut self, indices: &[u32]) -> MeshBuilder {
        self.indices.clear();
        self.indices.extend(indices);
        self
    }

    pub fn build(self) -> Result<Mesh, BuildMeshError> {
        // The vertex count is defined by the position data, since position is the only required
        // vertex attribute.
        let vertex_count = self.position_data.len();

        // Check that there is enough attribute data for each one.
        if self.normal_data.len() != 0 && self.normal_data.len() != vertex_count {
            return Err(BuildMeshError::IncorrectAttributeCount);
        }

        for texcoord_set in &self.texcoord_data {
            if texcoord_set.len() != vertex_count {
                return Err(BuildMeshError::IncorrectAttributeCount);
            }
        }

        // Make sure all indices at least point to a valid vertex.
        for index in self.indices.iter().cloned() {
            if index >= vertex_count as MeshIndex {
                return Err(BuildMeshError::IndexOutOfBounds {
                    vertex_count: vertex_count as MeshIndex,
                    index: index,
                });
            }
        }

        // TODO: Check for degenerate triangles? Actually, should that be a failure or a warning?

        let float_count = {
            let mut float_count =
                self.position_data.len() * 4
              + self.normal_data.len() * 3;
            for texcoord_set in &self.texcoord_data {
                float_count += texcoord_set.len() * 2;
            }

            float_count
        };

        // Create the mesh.
        let mut vertex_data = Vec::<f32>::with_capacity(float_count);

        // Setup position data.
        let position_attrib = VertexAttribute {
            offset: 0,
            stride: 4,
        };
        vertex_data.extend(Point::as_ref(&*self.position_data));

        // Setup normal data.
        let normal_attrib = if self.normal_data.len() > 0 {
            let attrib = VertexAttribute {
                offset: vertex_data.len(),
                stride: 3,
            };
            vertex_data.extend(Vector3::as_ref(&*self.normal_data));

            Some(attrib)
        } else {
            None
        };

        // Setup texcoord data.
        let mut texcoord_attribs = Vec::new();
        for texcoord_set in &self.texcoord_data {
            texcoord_attribs.push(VertexAttribute {
                offset: vertex_data.len(),
                stride: 2,
            });
            vertex_data.extend(Vector2::as_ref(&*texcoord_set));
        }

        // By our powers combined! We are! A mesh.
        Ok(Mesh {
            vertex_data: vertex_data,
            indices: self.indices,

            position: position_attrib,
            normal: normal_attrib,
            texcoord: texcoord_attribs,
        })
    }
}
<commit_msg>Rework MeshBuilder To Make Setting UVs Easier<commit_after>use math::*;

pub type MeshIndex = u32;

/// The raw data representing a mesh in memory.
///
/// Meshes are represented as list of vertex positions and a list of faces.
/// Each face is represented as 3 indices into the vertex array.
#[derive(Debug, Clone)]
pub struct Mesh {
    vertex_data: Vec<f32>,
    indices:     Vec<MeshIndex>,

    position: VertexAttribute,
    normal:   Option<VertexAttribute>,
    texcoord: Vec<VertexAttribute>,
}

impl Mesh {
    pub fn vertex_data(&self) -> &[f32] {
        &*self.vertex_data
    }

    pub fn indices(&self) -> &[MeshIndex] {
        &*self.indices
    }

    pub fn position(&self) -> VertexAttribute {
        self.position
    }

    pub fn normal(&self) -> Option<VertexAttribute> {
        self.normal
    }

    pub fn texcoord(&self) -> &[VertexAttribute] {
        &*self.texcoord
    }
}

/// Represents a single vertex in a mesh with all of its supported attributes.
#[derive(Debug, Clone)]
pub struct Vertex {
    pub position: Point,
    pub normal: Option<Vector3>,

    /// Support an arbitrary number of texture units. The actual maximum is dependent on hardware
    /// and so is not limited by polygon directly. If the number of
    pub texcoord: Vec<Vector2>,
}

impl Vertex {
    pub fn new(position: Point) -> Vertex {
        Vertex {
            position: position,
            normal: None,
            texcoord: Vec::new(),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub struct VertexAttribute {
    pub offset: usize,
    pub stride: usize,
}

#[derive(Debug, Clone, Copy)]
pub enum BuildMeshError {
    IndexOutOfBounds {
        vertex_count: MeshIndex,
        index: MeshIndex,
    },

    /// Indicates that one or more attributes had a count that did not match the total number of
    /// vertices.
    IncorrectAttributeCount,
}

/// Provides a safe interface for building a mesh from raw vertex data.
///
/// `MeshBuilder` supports two methods for specifying mesh data: Providing a series of `Vertex`
/// objects, or directly setting the data for each vertex attribute at once. Once all vertex data
/// has been set calling `build()` will validate the mesh data and produce a `Mesh` object.
///
/// The mesh builder performs validity tests when building the mesh and mesh compilation can fail
/// if the mesh data is invalid in some way. The following validity tests are performed:
///
/// - Check for different data count for different attributes (e.g. if the position attribute data
///   for a different number of elements than the normal attribute).
/// - Any of the indicies would be out of bounds for the given vertex data.
#[derive(Debug, Clone)]
pub struct MeshBuilder {
    position_data: Vec<Point>,
    normal_data: Vec<Vector3>,
    texcoord_data: Vec<Vector2>,

    indices:  Vec<u32>,
}

impl MeshBuilder {
    pub fn new() -> MeshBuilder {
        MeshBuilder {
            position_data: Vec::new(),
            normal_data:   Vec::new(),
            texcoord_data: Vec::new(),
            indices:       Vec::new(),
        }
    }

    pub fn add_vertex(&mut self, vertex: Vertex) {
        self.position_data.push(vertex.position);

        if let Some(normal) = vertex.normal {
            self.normal_data.push(normal);
        }

        assert!(vertex.texcoord.len() <= 1, "More than one texcoord per vertex is currently not supported");

        // Add each texcoord to its corresponding list.
        if vertex.texcoord.len() > 0 {
            self.texcoord_data.push(vertex.texcoord[0])
        }
    }

    pub fn add_index(mut self, index: MeshIndex) -> MeshBuilder {
        self.indices.push(index);
        self
    }

    pub fn set_position_data(mut self, position_data: &[Point]) -> MeshBuilder {
        self.position_data.clear();
        self.position_data.extend(position_data);
        self
    }

    pub fn set_normal_data(mut self, normal_data: &[Vector3]) -> MeshBuilder {
        self.normal_data.clear();
        self.normal_data.extend(normal_data);
        self
    }

    pub fn set_texcoord_data(mut self, texcoord_data: &[Vector2]) -> MeshBuilder {
        self.texcoord_data.clear();
        self.texcoord_data.extend(texcoord_data);
        self
    }

    pub fn set_indices(mut self, indices: &[u32]) -> MeshBuilder {
        self.indices.clear();
        self.indices.extend(indices);
        self
    }

    pub fn build(self) -> Result<Mesh, BuildMeshError> {
        // The vertex count is defined by the position data, since position is the only required
        // vertex attribute.
        let vertex_count = self.position_data.len();

        // Check that there is enough attribute data for each one.
        if self.normal_data.len() != 0 && self.normal_data.len() != vertex_count {
            return Err(BuildMeshError::IncorrectAttributeCount);
        }

        if self.texcoord_data.len() != vertex_count {
            return Err(BuildMeshError::IncorrectAttributeCount);
        }

        // Make sure all indices at least point to a valid vertex.
        for index in self.indices.iter().cloned() {
            if index >= vertex_count as MeshIndex {
                return Err(BuildMeshError::IndexOutOfBounds {
                    vertex_count: vertex_count as MeshIndex,
                    index: index,
                });
            }
        }

        // TODO: Check for degenerate triangles? Actually, should that be a failure or a warning?

        let float_count =
            self.position_data.len() * 4
          + self.normal_data.len() * 3
          + self.texcoord_data.len() * 2;

        // Create the mesh.
        let mut vertex_data = Vec::<f32>::with_capacity(float_count);

        // Setup position data.
        let position_attrib = VertexAttribute {
            offset: 0,
            stride: 4,
        };
        vertex_data.extend(Point::as_ref(&*self.position_data));

        // Setup normal data.
        let normal_attrib = if self.normal_data.len() > 0 {
            let attrib = VertexAttribute {
                offset: vertex_data.len(),
                stride: 3,
            };
            vertex_data.extend(Vector3::as_ref(&*self.normal_data));

            Some(attrib)
        } else {
            None
        };

        // Setup texcoord data.
        let mut texcoord_attribs = Vec::new();
        {
            texcoord_attribs.push(VertexAttribute {
                offset: vertex_data.len(),
                stride: 2,
            });
            vertex_data.extend(Vector2::as_ref(&*self.texcoord_data));
        }

        // By our powers combined! We are! A mesh.
        Ok(Mesh {
            vertex_data: vertex_data,
            indices: self.indices,

            position: position_attrib,
            normal: normal_attrib,
            texcoord: texcoord_attribs,
        })
    }
}
<|endoftext|>


<commit_before>#![feature(collections_bound)]

use std::collections::Bound;
use std::str::from_utf8;
use std::str::Utf8Error;
use std::num::ParseIntError;
use std::num::ParseFloatError;
use std::error::Error;
use std::fmt;

pub struct Argument {
    pub pos: usize,
    pub len: usize,
}

pub struct Parser<'a> {
    data: &'a[u8],
    pub argv: Vec<Argument>
}

pub enum ParseError {
    Incomplete,
    BadProtocol,
    InvalidArgument,
}

impl fmt::Debug for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        return write!(f, "{}", self.description());
    }
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        return self.description().fmt(f);
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::Incomplete => "Incomplete data",
            ParseError::BadProtocol => "Invalid data",
            ParseError::InvalidArgument => "Invalid argument",
        }
    }

    fn cause(&self) -> Option<&Error> { None }
}

impl From<Utf8Error> for ParseError {
    fn from(_: Utf8Error) -> ParseError { ParseError::InvalidArgument }
}

impl From<ParseIntError> for ParseError {
    fn from(_: ParseIntError) -> ParseError { ParseError::InvalidArgument }
}

impl From<ParseFloatError> for ParseError {
    fn from(_: ParseFloatError) -> ParseError { ParseError::InvalidArgument }
}

impl<'a> Parser<'a> {
    pub fn new(data: &[u8], argv: Vec<Argument>) -> Parser {
        return Parser {
            data: data,
            argv: argv,
        };
    }

    pub fn get_f64_bound(&self, pos: usize) -> Result<Bound<f64>, ParseError> {
        let s = try!(self.get_str(pos));
        if s == "inf" || s == "+inf" || s == "-inf" {
            return Ok(Bound::Unbounded);
        }
        if s.starts_with("(") {
            return Ok(Bound::Excluded(try!(s[1..].parse::<f64>())));
        }
        return Ok(Bound::Included(try!(s.parse::<f64>())));
    }

    // TODO: get<T>
    pub fn get_f64(&self, pos: usize) -> Result<f64, ParseError> {
        let s = try!(self.get_str(pos));
        return Ok(try!(s.parse::<f64>()));
    }

    pub fn get_i64(&self, pos: usize) -> Result<i64, ParseError> {
        let s = try!(self.get_str(pos));
        return Ok(try!(s.parse::<i64>()));
    }

    pub fn get_str(&self, pos: usize) -> Result<&str, ParseError> {
        let data = try!(self.get_slice(pos));
        Ok(try!(from_utf8(&data)))
    }

    pub fn get_vec(&self, pos: usize) -> Result<Vec<u8>, ParseError> {
        let data = try!(self.get_slice(pos));
        return Ok(data.to_vec());
    }

    pub fn get_slice(&self, pos: usize) -> Result<&[u8], ParseError> {
        if pos >= self.argv.len() {
            return Err(ParseError::InvalidArgument);
        }
        let arg = &self.argv[pos];
        return Ok(&self.data[arg.pos..arg.pos+arg.len]);
    }
}

impl<'a> fmt::Debug for Parser<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        return write!(f, "{}", self.description());
    }
}

impl<'a> fmt::Display for Parser<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        return self.description().fmt(f);
    }
}

impl<'a> Error for Parser<'a> {
    fn description(&self) -> &str {
        return "parser";
    }
    fn cause(&self) -> Option<&Error> { None }
}

fn parse_int(input: &[u8], len: usize) -> Result<(usize, usize), ParseError> {
    let mut i = 0;
    let mut argc = 0;
    while input[i] as char != '\r' {
        let c = input[i] as char;
        if c < '0' || c > '9' {
            return Err(ParseError::BadProtocol);
        }
        argc *= 10;
        argc += input[i] as usize - '0' as usize;
        i += 1;
        if i == len {
            return Err(ParseError::Incomplete);
        }
    }
    i += 1;
    if input[i] as char != '\n' {
        return Err(ParseError::BadProtocol);
    }
    return Ok((argc, i + 1));
}

pub fn parse(input: &[u8], len: usize) -> Result<Parser, ParseError> {
    if input[0] as char != '*' {
        return Err(ParseError::BadProtocol);
    } else {
        let mut pos = 1;
        let (argc, intlen) = try!(parse_int(&input[pos..len], len - pos));
        pos += intlen;
        let mut argv = Vec::new();
        for i in 0..argc {
            if input[pos] as char != '$' {
                return Err(ParseError::BadProtocol);
            }
            pos += 1;
            let (arglen, arglenlen) = try!(parse_int(&input[pos..len], len - pos));
            pos += arglenlen;
            let arg = Argument {
                pos: pos,
                len: arglen,
            };
            argv.push(arg);
            pos += arglen + 2;
            if pos > len || (pos == len && i != argc - 1) {
                return Err(ParseError::Incomplete);
            }
        }
        Ok(Parser::new(input, argv))
    }
}

#[cfg(test)]
mod test_parser {
    use super::{parse, ParseError};

    #[test]
    fn parse_valid() {
        let message = b"*2\r\n$3\r\nfoo\r\n$4\r\nbarz\r\n";
        let r = parse(message, message.len());
        assert!(r.is_ok());
        let parser = r.unwrap();
        assert_eq!(parser.argv.len(), 2);
        assert_eq!(parser.get_str(0).unwrap(), "foo");
        assert_eq!(parser.get_str(1).unwrap(), "barz");
    }

    #[test]
    fn parse_incomplete() {
        let message = b"*2\r\n$3\r\nfoo";
        let r = parse(message, message.len());
        assert!(r.is_err());
        match r.unwrap_err() {
            ParseError::Incomplete => {},
            _ => assert!(false)
        }
    }

    #[test]
    fn parse_invalid() {
        let message = b"-2\r\n$3\r\nfoo";
        let r = parse(message, message.len());
        assert!(r.is_err());
        match r.unwrap_err() {
            ParseError::BadProtocol => {},
            _ => assert!(false)
        }
    }
}
<commit_msg>Document parser functions<commit_after>#![feature(collections_bound)]

use std::collections::Bound;
use std::str::from_utf8;
use std::str::Utf8Error;
use std::num::ParseIntError;
use std::num::ParseFloatError;
use std::error::Error;
use std::fmt;

/// A command argument
#[derive(Debug)]
pub struct Argument {
    /// The position in the array
    pub pos: usize,
    /// The length in the array
    pub len: usize,
}

/// A protocol parser
#[derive(Debug)]
pub struct Parser<'a> {
    /// The data itself
    data: &'a[u8],
    /// The arguments location and length
    pub argv: Vec<Argument>
}

/// Error parsing
#[derive(Debug)]
pub enum ParseError {
    /// The received buffer is valid but needs more data
    Incomplete,
    /// The received buffer is invalid
    BadProtocol,
    /// Expected one type of argument and received another
    InvalidArgument,
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        return self.description().fmt(f);
    }
}

impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::Incomplete => "Incomplete data",
            ParseError::BadProtocol => "Invalid data",
            ParseError::InvalidArgument => "Invalid argument",
        }
    }

    fn cause(&self) -> Option<&Error> { None }
}

impl From<Utf8Error> for ParseError {
    fn from(_: Utf8Error) -> ParseError { ParseError::InvalidArgument }
}

impl From<ParseIntError> for ParseError {
    fn from(_: ParseIntError) -> ParseError { ParseError::InvalidArgument }
}

impl From<ParseFloatError> for ParseError {
    fn from(_: ParseFloatError) -> ParseError { ParseError::InvalidArgument }
}

impl<'a> Parser<'a> {
    /// Creates a new parser with the data and arguments provided
    pub fn new(data: &[u8], argv: Vec<Argument>) -> Parser {
        return Parser {
            data: data,
            argv: argv,
        };
    }

    /// Gets a `Bound` from a parameter.
    ///
    /// # Examples
    ///
    /// ```
    /// # #![feature(collections_bound)]
    /// # use std::collections::Bound;
    /// # use parser::{Parser, Argument};
    /// let parser = Parser::new(b"+inf", vec![Argument { pos: 0, len: 4 }]);
    /// assert_eq!(parser.get_f64_bound(0).unwrap(), Bound::Unbounded);
    /// ```
    ///
    /// ```
    /// # #![feature(collections_bound)]
    /// # use std::collections::Bound;
    /// # use parser::{Parser, Argument};
    /// let parser = Parser::new(b"1.23", vec![Argument { pos: 0, len: 4 }]);
    /// assert_eq!(parser.get_f64_bound(0).unwrap(), Bound::Included(1.23));
    /// ```
    ///
    /// ```
    /// # #![feature(collections_bound)]
    /// # use std::collections::Bound;
    /// # use parser::{Parser, Argument};
    /// let parser = Parser::new(b"(1.23", vec![Argument { pos: 0, len: 5 }]);
    /// assert_eq!(parser.get_f64_bound(0).unwrap(), Bound::Excluded(1.23));
    /// ```
    pub fn get_f64_bound(&self, pos: usize) -> Result<Bound<f64>, ParseError> {
        let s = try!(self.get_str(pos));
        if s == "inf" || s == "+inf" || s == "-inf" {
            return Ok(Bound::Unbounded);
        }
        if s.starts_with("(") {
            return Ok(Bound::Excluded(try!(s[1..].parse::<f64>())));
        }
        return Ok(Bound::Included(try!(s.parse::<f64>())));
    }

    // TODO: get<T>

    /// Gets an f64 from a parameter
    ///
    /// # Examples
    ///
    /// ```
    /// # use parser::{Parser, Argument};
    /// let parser = Parser::new(b"1.23", vec![Argument { pos: 0, len: 4 }]);
    /// assert_eq!(parser.get_f64(0).unwrap(), 1.23);
    /// ```
    pub fn get_f64(&self, pos: usize) -> Result<f64, ParseError> {
        let s = try!(self.get_str(pos));
        return Ok(try!(s.parse::<f64>()));
    }

    /// Gets an i64 from a parameter
    ///
    /// # Examples
    ///
    /// ```
    /// # use parser::{Parser, Argument};
    /// let parser = Parser::new(b"-123", vec![Argument { pos: 0, len: 4 }]);
    /// assert_eq!(parser.get_i64(0).unwrap(), -123);
    /// ```
    pub fn get_i64(&self, pos: usize) -> Result<i64, ParseError> {
        let s = try!(self.get_str(pos));
        return Ok(try!(s.parse::<i64>()));
    }

    /// Gets an str from a parameter
    ///
    /// # Examples
    ///
    /// ```
    /// # use parser::{Parser, Argument};
    /// let parser = Parser::new(b"foo", vec![Argument { pos: 0, len: 3 }]);
    /// assert_eq!(parser.get_str(0).unwrap(), "foo");
    /// ```
    pub fn get_str(&self, pos: usize) -> Result<&str, ParseError> {
        let data = try!(self.get_slice(pos));
        Ok(try!(from_utf8(&data)))
    }

    /// Gets a Vec<u8> from a parameter
    ///
    /// # Examples
    ///
    /// ```
    /// # use parser::{Parser, Argument};
    /// let parser = Parser::new(b"foo", vec![Argument { pos: 0, len: 3 }]);
    /// assert_eq!(parser.get_vec(0).unwrap(), b"foo".to_vec());
    /// ```
    pub fn get_vec(&self, pos: usize) -> Result<Vec<u8>, ParseError> {
        let data = try!(self.get_slice(pos));
        return Ok(data.to_vec());
    }

    /// Gets a &[u8] from a parameter
    ///
    /// # Examples
    ///
    /// ```
    /// # use parser::{Parser, Argument};
    /// let parser = Parser::new(b"foo", vec![Argument { pos: 0, len: 3 }]);
    /// assert_eq!(parser.get_slice(0).unwrap(), b"foo");
    /// ```
    pub fn get_slice(&self, pos: usize) -> Result<&[u8], ParseError> {
        if pos >= self.argv.len() {
            return Err(ParseError::InvalidArgument);
        }
        let arg = &self.argv[pos];
        return Ok(&self.data[arg.pos..arg.pos+arg.len]);
    }
}

/// Parses the length of the paramenter in the slice
/// Upon success, it returns a tuple with the length of the argument and the
/// length of the parsed length.
fn parse_int(input: &[u8], len: usize) -> Result<(usize, usize), ParseError> {
    let mut i = 0;
    let mut argc = 0;
    while input[i] as char != '\r' {
        let c = input[i] as char;
        if c < '0' || c > '9' {
            return Err(ParseError::BadProtocol);
        }
        argc *= 10;
        argc += input[i] as usize - '0' as usize;
        i += 1;
        if i == len {
            return Err(ParseError::Incomplete);
        }
    }
    i += 1;
    if input[i] as char != '\n' {
        return Err(ParseError::BadProtocol);
    }
    return Ok((argc, i + 1));
}

/// Creates a parser from a buffer.
///
/// # Examples
///
/// ```
/// # use parser::parse;
/// let s = b"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$2\r\n10\r\n";
/// let parser = parse(s, 32).unwrap();
/// assert_eq!(parser.get_str(0).unwrap(), "SET");
/// assert_eq!(parser.get_str(1).unwrap(), "mykey");
/// assert_eq!(parser.get_i64(2).unwrap(), 10);
/// ```
pub fn parse(input: &[u8], len: usize) -> Result<Parser, ParseError> {
    if input[0] as char != '*' {
        return Err(ParseError::BadProtocol);
    } else {
        let mut pos = 1;
        let (argc, intlen) = try!(parse_int(&input[pos..len], len - pos));
        pos += intlen;
        let mut argv = Vec::new();
        for i in 0..argc {
            if input[pos] as char != '$' {
                return Err(ParseError::BadProtocol);
            }
            pos += 1;
            let (arglen, arglenlen) = try!(parse_int(&input[pos..len], len - pos));
            pos += arglenlen;
            let arg = Argument {
                pos: pos,
                len: arglen,
            };
            argv.push(arg);
            pos += arglen + 2;
            if pos > len || (pos == len && i != argc - 1) {
                return Err(ParseError::Incomplete);
            }
        }
        Ok(Parser::new(input, argv))
    }
}

#[cfg(test)]
mod test_parser {
    use super::{parse, ParseError};

    #[test]
    fn parse_valid() {
        let message = b"*2\r\n$3\r\nfoo\r\n$4\r\nbarz\r\n";
        let r = parse(message, message.len());
        assert!(r.is_ok());
        let parser = r.unwrap();
        assert_eq!(parser.argv.len(), 2);
        assert_eq!(parser.get_str(0).unwrap(), "foo");
        assert_eq!(parser.get_str(1).unwrap(), "barz");
    }

    #[test]
    fn parse_incomplete() {
        let message = b"*2\r\n$3\r\nfoo";
        let r = parse(message, message.len());
        assert!(r.is_err());
        match r.unwrap_err() {
            ParseError::Incomplete => {},
            _ => assert!(false)
        }
    }

    #[test]
    fn parse_invalid() {
        let message = b"-2\r\n$3\r\nfoo";
        let r = parse(message, message.len());
        assert!(r.is_err());
        match r.unwrap_err() {
            ParseError::BadProtocol => {},
            _ => assert!(false)
        }
    }
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate c2_chacha;
extern crate stream_cipher;
extern crate test;

use c2_chacha::simd::machine::x86;
use c2_chacha::simd::Machine;
use c2_chacha::ChaChaState;
use test::Bencher;

macro_rules! mach_bench {
    ($MachName:ident, $feature:expr, $enable:expr) => {
        #[allow(non_snake_case)]
        #[bench]
        pub fn $MachName(b: &mut Bencher) {
            if !$enable {
                return;
            }
            let m = unsafe { x86::$MachName::instance() };
            let z = m
                .vec::<<x86::$MachName as Machine>::u64x2, _>([0x0, 0x0])
                .into();
            let mut state = ChaChaState { b: z, c: z, d: z };
            let mut out = [0; 256];
            #[target_feature(enable = $feature)]
            unsafe fn runner<M: Machine>(m: M, state: &mut ChaChaState, out: &mut [u8; 256]) {
                c2_chacha::refill_wide_impl(m, state, 40 * 20 / 2, out)
            }
            b.iter(|| unsafe { runner(m, &mut state, &mut out) });
            b.bytes = 10240;
        }
    };
}

mach_bench!(SSE2, "sse2", is_x86_feature_detected!("sse2"));
mach_bench!(SSSE3, "ssse3", is_x86_feature_detected!("ssse3"));
mach_bench!(SSE41, "sse4.1", is_x86_feature_detected!("sse4.1"));
mach_bench!(AVX, "avx", is_x86_feature_detected!("avx"));
mach_bench!(AVX2, "avx2", is_x86_feature_detected!("avx2"));
<commit_msg>remove broken machine-type benchmarks<commit_after><|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use erased_serde::Serialize;
use indicatif::{ProgressBar, ProgressStyle};
use std::any::Any;
use std::fs::{self, File};
use std::io::{self, Write};
use std::path::PathBuf;
use std::time::Instant;
use structopt::StructOpt;

use opentitanlib::app::command::CommandDispatch;
use opentitanlib::app::TransportWrapper;
use opentitanlib::io::spi::{SpiParams, Transfer};
use opentitanlib::spiflash::SpiFlash;
use opentitanlib::transport::Capability;

/// Read and parse an SFDP table.
#[derive(Debug, StructOpt)]
pub struct SpiSfdp {
    #[structopt(
        short,
        long,
        help = "Display raw SFDP bytes rather than the parsed struct."
    )]
    raw: Option<usize>,
}

// Print a hexdump of a buffer to `writer`.
// The hexdump includes the offset, hex bytes and printable ASCII characters.
//
//  00000000: 53 46 44 50 06 01 02 ff 00 06 01 10 30 00 00 ff  SFDP........0...
//  00000010: c2 00 01 04 10 01 00 ff 84 00 01 02 c0 00 00 ff  ................
//  00000020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff  ................
//  00000030: e5 20 fb ff ff ff ff 3f 44 eb 08 6b 08 3b 04 bb  . .....?D..k.;..
//
// Note: This format can be consumed by `xxd -r` and converted back into binary.
fn hexdump(mut writer: impl Write, buf: &[u8]) -> Result<()> {
    for (i, chunk) in buf.chunks(16).enumerate() {
        let mut ascii = [b'.'; 16];
        write!(writer, "{:08x}:", i * 16)?;
        for (j, byte) in chunk.iter().copied().enumerate() {
            write!(writer, " {:02x}", byte)?;
            // For printable ASCII chars, place them in the ascii buffer.
            if byte == b' ' || byte.is_ascii_graphic() {
                ascii[j] = byte;
            }
        }
        // Align and print the ascii buffer.
        let j = chunk.len();
        for _ in 0..(16 - j) {
            write!(writer, "   ")?;
        }
        writeln!(writer, "  {}", std::str::from_utf8(&ascii[0..j]).unwrap())?;
    }
    Ok(())
}

// Helper function to create a progress bar in the same form for each of
// the commands which will use it.
fn progress_bar(total: u64) -> ProgressBar {
    let progress = ProgressBar::new(total);
    progress.set_style(
        ProgressStyle::default_bar()
            .template("[{elapsed_precise}] [{wide_bar}] {bytes}/{total_bytes} ({eta})"),
    );
    progress
}

impl CommandDispatch for SpiSfdp {
    fn run(
        &self,
        context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        transport.capabilities()?.request(Capability::SPI).ok()?;
        let context = context.downcast_ref::<SpiCommand>().unwrap();
        let spi = context.params.create(transport)?;

        if let Some(length) = self.raw {
            let mut buffer = vec![0u8; length];
            spi.run_transaction(&mut [
                Transfer::Write(&[SpiFlash::READ_SFDP, 0, 0, 0, 0]),
                Transfer::Read(&mut buffer),
            ])?;
            hexdump(io::stdout(), &buffer)?;
            Ok(None)
        } else {
            let sfdp = SpiFlash::read_sfdp(&*spi)?;
            Ok(Some(Box::new(sfdp)))
        }
    }
}

/// Read the JEDEC ID of a SPI EEPROM.
#[derive(Debug, StructOpt)]
pub struct SpiReadId {
    #[structopt(
        short = "n",
        long,
        default_value = "3",
        help = "Number of JEDEC ID bytes to read."
    )]
    length: usize,
}

#[derive(Debug, serde::Serialize)]
pub struct SpiReadIdResponse {
    jedec_id: Vec<u8>,
}

impl CommandDispatch for SpiReadId {
    fn run(
        &self,
        context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        transport.capabilities()?.request(Capability::SPI).ok()?;
        let context = context.downcast_ref::<SpiCommand>().unwrap();
        let spi = context.params.create(transport)?;
        let jedec_id = SpiFlash::read_jedec_id(&*spi, self.length)?;
        Ok(Some(Box::new(SpiReadIdResponse { jedec_id })))
    }
}

/// Read data from a SPI EEPROM.
#[derive(Debug, StructOpt)]
pub struct SpiRead {
    #[structopt(short, long, default_value = "0", help = "Start offset.")]
    start: u32,
    #[structopt(
        short = "n",
        long,
        default_value = "4096",
        help = "Number of bytes to read."
    )]
    length: usize,
    #[structopt(short, long, help = "Hexdump the data.")]
    hexdump: bool,
    #[structopt(name = "FILE", default_value = "-")]
    filename: PathBuf,
}

#[derive(Debug, serde::Serialize)]
pub struct SpiReadResponse {
    length: usize,
    bytes_per_second: f64,
}

impl SpiRead {
    fn write_file(&self, mut writer: impl Write, buffer: &[u8]) -> Result<()> {
        if self.hexdump {
            hexdump(writer, buffer)?;
        } else {
            writer.write_all(buffer)?;
        }
        Ok(())
    }
}

impl CommandDispatch for SpiRead {
    fn run(
        &self,
        context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        transport.capabilities()?.request(Capability::SPI).ok()?;
        let context = context.downcast_ref::<SpiCommand>().unwrap();
        let spi = context.params.create(transport)?;
        let mut flash = SpiFlash::from_spi(&*spi)?;
        flash.set_address_mode_auto(&*spi)?;

        let mut buffer = vec![0u8; self.length];
        let progress = progress_bar(self.length as u64);
        let t0 = Instant::now();
        flash.read_with_progress(&*spi, self.start, &mut buffer, |_, chunk| {
            progress.inc(chunk as u64);
        })?;
        progress.finish();
        let duration = t0.elapsed().as_secs_f64();

        if self.filename.to_str() == Some("-") {
            self.write_file(io::stdout(), &buffer)?;
            Ok(None)
        } else {
            let file = File::create(&self.filename)?;
            self.write_file(file, &buffer)?;
            Ok(Some(Box::new(SpiReadResponse {
                length: buffer.len(),
                bytes_per_second: buffer.len() as f64 / duration,
            })))
        }
    }
}

/// Erase sectors of a SPI EEPROM.
#[derive(Debug, StructOpt)]
pub struct SpiErase {
    #[structopt(short, long, help = "Start offset.")]
    start: u32,
    #[structopt(short = "n", long, help = "Number of bytes to erase.")]
    length: u32,
}

#[derive(Debug, serde::Serialize)]
pub struct SpiEraseResponse {
    length: u32,
    bytes_per_second: f64,
}

impl CommandDispatch for SpiErase {
    fn run(
        &self,
        context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        transport.capabilities()?.request(Capability::SPI).ok()?;
        let context = context.downcast_ref::<SpiCommand>().unwrap();
        let spi = context.params.create(transport)?;
        let mut flash = SpiFlash::from_spi(&*spi)?;
        flash.set_address_mode_auto(&*spi)?;

        let progress = progress_bar(self.length as u64);
        let t0 = Instant::now();
        flash.erase_with_progress(&*spi, self.start, self.length, |_, chunk| {
            progress.inc(chunk as u64);
        })?;
        progress.finish();
        let duration = t0.elapsed().as_secs_f64();

        Ok(Some(Box::new(SpiEraseResponse {
            length: self.length,
            bytes_per_second: self.length as f64 / duration,
        })))
    }
}

/// Program data into a SPI EEPROM.
#[derive(Debug, StructOpt)]
pub struct SpiProgram {
    #[structopt(short, long, default_value = "0", help = "Start offset.")]
    start: u32,
    #[structopt(name = "FILE")]
    filename: PathBuf,
}

#[derive(Debug, serde::Serialize)]
pub struct SpiProgramResponse {
    length: usize,
    bytes_per_second: f64,
}

impl CommandDispatch for SpiProgram {
    fn run(
        &self,
        context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        transport.capabilities()?.request(Capability::SPI).ok()?;
        let context = context.downcast_ref::<SpiCommand>().unwrap();
        let spi = context.params.create(transport)?;
        let mut flash = SpiFlash::from_spi(&*spi)?;
        flash.set_address_mode_auto(&*spi)?;

        let buffer = fs::read(&self.filename)?;
        let progress = progress_bar(buffer.len() as u64);
        let t0 = Instant::now();
        flash.program_with_progress(&*spi, self.start, &buffer, |_, chunk| {
            progress.inc(chunk as u64);
        })?;
        progress.finish();
        let duration = t0.elapsed().as_secs_f64();

        Ok(Some(Box::new(SpiProgramResponse {
            length: buffer.len(),
            bytes_per_second: buffer.len() as f64 / duration,
        })))
    }
}

/// Commands for interacting with a SPI EEPROM.
#[derive(Debug, StructOpt, CommandDispatch)]
pub enum InternalSpiCommand {
    Sfdp(SpiSfdp),
    ReadId(SpiReadId),
    Read(SpiRead),
    Erase(SpiErase),
    Program(SpiProgram),
}

#[derive(Debug, StructOpt)]
pub struct SpiCommand {
    #[structopt(flatten)]
    params: SpiParams,

    #[structopt(subcommand)]
    command: InternalSpiCommand,
}

impl CommandDispatch for SpiCommand {
    fn run(
        &self,
        _context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        // None of the SPI commands care about the prior context, but they do
        // care about the `bus` parameter in the current node.
        self.command.run(self, transport)
    }
}
<commit_msg>[opentitantool] Make spi read-id read 15 bytes by default<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use erased_serde::Serialize;
use indicatif::{ProgressBar, ProgressStyle};
use std::any::Any;
use std::fs::{self, File};
use std::io::{self, Write};
use std::path::PathBuf;
use std::time::Instant;
use structopt::StructOpt;

use opentitanlib::app::command::CommandDispatch;
use opentitanlib::app::TransportWrapper;
use opentitanlib::io::spi::{SpiParams, Transfer};
use opentitanlib::spiflash::SpiFlash;
use opentitanlib::transport::Capability;

/// Read and parse an SFDP table.
#[derive(Debug, StructOpt)]
pub struct SpiSfdp {
    #[structopt(
        short,
        long,
        help = "Display raw SFDP bytes rather than the parsed struct."
    )]
    raw: Option<usize>,
}

// Print a hexdump of a buffer to `writer`.
// The hexdump includes the offset, hex bytes and printable ASCII characters.
//
//  00000000: 53 46 44 50 06 01 02 ff 00 06 01 10 30 00 00 ff  SFDP........0...
//  00000010: c2 00 01 04 10 01 00 ff 84 00 01 02 c0 00 00 ff  ................
//  00000020: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff  ................
//  00000030: e5 20 fb ff ff ff ff 3f 44 eb 08 6b 08 3b 04 bb  . .....?D..k.;..
//
// Note: This format can be consumed by `xxd -r` and converted back into binary.
fn hexdump(mut writer: impl Write, buf: &[u8]) -> Result<()> {
    for (i, chunk) in buf.chunks(16).enumerate() {
        let mut ascii = [b'.'; 16];
        write!(writer, "{:08x}:", i * 16)?;
        for (j, byte) in chunk.iter().copied().enumerate() {
            write!(writer, " {:02x}", byte)?;
            // For printable ASCII chars, place them in the ascii buffer.
            if byte == b' ' || byte.is_ascii_graphic() {
                ascii[j] = byte;
            }
        }
        // Align and print the ascii buffer.
        let j = chunk.len();
        for _ in 0..(16 - j) {
            write!(writer, "   ")?;
        }
        writeln!(writer, "  {}", std::str::from_utf8(&ascii[0..j]).unwrap())?;
    }
    Ok(())
}

// Helper function to create a progress bar in the same form for each of
// the commands which will use it.
fn progress_bar(total: u64) -> ProgressBar {
    let progress = ProgressBar::new(total);
    progress.set_style(
        ProgressStyle::default_bar()
            .template("[{elapsed_precise}] [{wide_bar}] {bytes}/{total_bytes} ({eta})"),
    );
    progress
}

impl CommandDispatch for SpiSfdp {
    fn run(
        &self,
        context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        transport.capabilities()?.request(Capability::SPI).ok()?;
        let context = context.downcast_ref::<SpiCommand>().unwrap();
        let spi = context.params.create(transport)?;

        if let Some(length) = self.raw {
            let mut buffer = vec![0u8; length];
            spi.run_transaction(&mut [
                Transfer::Write(&[SpiFlash::READ_SFDP, 0, 0, 0, 0]),
                Transfer::Read(&mut buffer),
            ])?;
            hexdump(io::stdout(), &buffer)?;
            Ok(None)
        } else {
            let sfdp = SpiFlash::read_sfdp(&*spi)?;
            Ok(Some(Box::new(sfdp)))
        }
    }
}

/// Read the JEDEC ID of a SPI EEPROM.
#[derive(Debug, StructOpt)]
pub struct SpiReadId {
    #[structopt(
        short = "n",
        long,
        default_value = "15",
        help = "Number of JEDEC ID bytes to read."
    )]
    length: usize,
}

#[derive(Debug, serde::Serialize)]
pub struct SpiReadIdResponse {
    jedec_id: Vec<u8>,
}

impl CommandDispatch for SpiReadId {
    fn run(
        &self,
        context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        transport.capabilities()?.request(Capability::SPI).ok()?;
        let context = context.downcast_ref::<SpiCommand>().unwrap();
        let spi = context.params.create(transport)?;
        let jedec_id = SpiFlash::read_jedec_id(&*spi, self.length)?;
        Ok(Some(Box::new(SpiReadIdResponse { jedec_id })))
    }
}

/// Read data from a SPI EEPROM.
#[derive(Debug, StructOpt)]
pub struct SpiRead {
    #[structopt(short, long, default_value = "0", help = "Start offset.")]
    start: u32,
    #[structopt(
        short = "n",
        long,
        default_value = "4096",
        help = "Number of bytes to read."
    )]
    length: usize,
    #[structopt(short, long, help = "Hexdump the data.")]
    hexdump: bool,
    #[structopt(name = "FILE", default_value = "-")]
    filename: PathBuf,
}

#[derive(Debug, serde::Serialize)]
pub struct SpiReadResponse {
    length: usize,
    bytes_per_second: f64,
}

impl SpiRead {
    fn write_file(&self, mut writer: impl Write, buffer: &[u8]) -> Result<()> {
        if self.hexdump {
            hexdump(writer, buffer)?;
        } else {
            writer.write_all(buffer)?;
        }
        Ok(())
    }
}

impl CommandDispatch for SpiRead {
    fn run(
        &self,
        context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        transport.capabilities()?.request(Capability::SPI).ok()?;
        let context = context.downcast_ref::<SpiCommand>().unwrap();
        let spi = context.params.create(transport)?;
        let mut flash = SpiFlash::from_spi(&*spi)?;
        flash.set_address_mode_auto(&*spi)?;

        let mut buffer = vec![0u8; self.length];
        let progress = progress_bar(self.length as u64);
        let t0 = Instant::now();
        flash.read_with_progress(&*spi, self.start, &mut buffer, |_, chunk| {
            progress.inc(chunk as u64);
        })?;
        progress.finish();
        let duration = t0.elapsed().as_secs_f64();

        if self.filename.to_str() == Some("-") {
            self.write_file(io::stdout(), &buffer)?;
            Ok(None)
        } else {
            let file = File::create(&self.filename)?;
            self.write_file(file, &buffer)?;
            Ok(Some(Box::new(SpiReadResponse {
                length: buffer.len(),
                bytes_per_second: buffer.len() as f64 / duration,
            })))
        }
    }
}

/// Erase sectors of a SPI EEPROM.
#[derive(Debug, StructOpt)]
pub struct SpiErase {
    #[structopt(short, long, help = "Start offset.")]
    start: u32,
    #[structopt(short = "n", long, help = "Number of bytes to erase.")]
    length: u32,
}

#[derive(Debug, serde::Serialize)]
pub struct SpiEraseResponse {
    length: u32,
    bytes_per_second: f64,
}

impl CommandDispatch for SpiErase {
    fn run(
        &self,
        context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        transport.capabilities()?.request(Capability::SPI).ok()?;
        let context = context.downcast_ref::<SpiCommand>().unwrap();
        let spi = context.params.create(transport)?;
        let mut flash = SpiFlash::from_spi(&*spi)?;
        flash.set_address_mode_auto(&*spi)?;

        let progress = progress_bar(self.length as u64);
        let t0 = Instant::now();
        flash.erase_with_progress(&*spi, self.start, self.length, |_, chunk| {
            progress.inc(chunk as u64);
        })?;
        progress.finish();
        let duration = t0.elapsed().as_secs_f64();

        Ok(Some(Box::new(SpiEraseResponse {
            length: self.length,
            bytes_per_second: self.length as f64 / duration,
        })))
    }
}

/// Program data into a SPI EEPROM.
#[derive(Debug, StructOpt)]
pub struct SpiProgram {
    #[structopt(short, long, default_value = "0", help = "Start offset.")]
    start: u32,
    #[structopt(name = "FILE")]
    filename: PathBuf,
}

#[derive(Debug, serde::Serialize)]
pub struct SpiProgramResponse {
    length: usize,
    bytes_per_second: f64,
}

impl CommandDispatch for SpiProgram {
    fn run(
        &self,
        context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        transport.capabilities()?.request(Capability::SPI).ok()?;
        let context = context.downcast_ref::<SpiCommand>().unwrap();
        let spi = context.params.create(transport)?;
        let mut flash = SpiFlash::from_spi(&*spi)?;
        flash.set_address_mode_auto(&*spi)?;

        let buffer = fs::read(&self.filename)?;
        let progress = progress_bar(buffer.len() as u64);
        let t0 = Instant::now();
        flash.program_with_progress(&*spi, self.start, &buffer, |_, chunk| {
            progress.inc(chunk as u64);
        })?;
        progress.finish();
        let duration = t0.elapsed().as_secs_f64();

        Ok(Some(Box::new(SpiProgramResponse {
            length: buffer.len(),
            bytes_per_second: buffer.len() as f64 / duration,
        })))
    }
}

/// Commands for interacting with a SPI EEPROM.
#[derive(Debug, StructOpt, CommandDispatch)]
pub enum InternalSpiCommand {
    Sfdp(SpiSfdp),
    ReadId(SpiReadId),
    Read(SpiRead),
    Erase(SpiErase),
    Program(SpiProgram),
}

#[derive(Debug, StructOpt)]
pub struct SpiCommand {
    #[structopt(flatten)]
    params: SpiParams,

    #[structopt(subcommand)]
    command: InternalSpiCommand,
}

impl CommandDispatch for SpiCommand {
    fn run(
        &self,
        _context: &dyn Any,
        transport: &TransportWrapper,
    ) -> Result<Option<Box<dyn Serialize>>> {
        // None of the SPI commands care about the prior context, but they do
        // care about the `bus` parameter in the current node.
        self.command.run(self, transport)
    }
}
<|endoftext|>


<commit_before>#![allow(unused_variables)]

extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::input::keyboard::Key;
use piston::input::Button::Keyboard;
use piston::window::WindowSettings;
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

struct App {
    window: Rc<RefCell<GlutinWindow>>,
    graphics: Rc<RefCell<GlGraphics>>,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    virtual_cursor_pos: [f64; 2],
    bg_color: [f32; 4]
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0f64;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.virtual_cursor_pos = [x, y];
                },
                _ => { }
            }
        } else if let &Input::Resize(x, y) = input {
            self.resize((x, y))
        }
    }

    fn update(&mut self, args: &UpdateArgs) {
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        let mut gl_graphics = self.graphics.borrow_mut();

        // draw the background color
        {
            gl_graphics.draw(args.viewport(), |_, gl| clear(self.bg_color, gl));
        }

        // draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color, square, c.transform, gl));
        }

        // draw the cursor dot
        {
            let dot = ellipse::circle(self.virtual_cursor_pos[0], self.virtual_cursor_pos[1], 5.0);
            gl_graphics.draw(args.viewport(), |c, gl| ellipse([0.0, 1.0, 0.0, 1.0], dot, c.transform, gl));
        }
    }

    fn resize(&mut self, new_size: (u32, u32)) {
        self.translator.set_size(new_size.into());
    }
}

struct Character {
    color: [f32; 4],
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: [f32; 4], tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0f64, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("calaxite", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .unwrap_or_else(|e| panic!("Could not create window: {}", e));

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = RebindBuilder::new(WINDOW_SIZE)
        .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
        .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
        .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
        .build_translator();

    let character = Character::new([1.0, 0.0, 0.0, 1.0],
                                   [30.0, (WINDOW_SIZE.1 as f64) * 0.85],
                                   50.0);

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        translator: translator,
        character: character,
        virtual_cursor_pos: [0.0, 0.0],
        bg_color: [0.0, 0.0, 0.0, 1.0] // black background
    };

    for e in app.window.clone().events() {
        match e {
            Event::Render(r) => { app.render(&r); },
            Event::Update(u) => { app.update(&u); },
            Event::Input(i)  => { app.input(&i); },
            _ => { }
        }
    }
}
<commit_msg>Properly detect window resizing to that the InputTranslator's viewport size is properly maintained<commit_after>#![allow(unused_variables)]

extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::input::keyboard::Key;
use piston::input::Button::Keyboard;
use piston::window::{Window, WindowSettings};
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

type RcWindow = Rc<RefCell<GlutinWindow>>;

struct App {
    window: RcWindow,
    graphics: Rc<RefCell<GlGraphics>>,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    virtual_cursor_pos: [f64; 2],
    bg_color: [f32; 4]
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0f64;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.virtual_cursor_pos = [x, y];
                },
                _ => { }
            }
        }
    }

    fn update(&mut self, args: &UpdateArgs, window: RcWindow) {
        // we need to pass the window into update (and set the size here) because
        // using the update event from the window events is currently broken.
        self.translator.set_size(window.borrow().size());

        // update the character's velocity
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        let mut gl_graphics = self.graphics.borrow_mut();

        // draw the background color
        {
            gl_graphics.draw(args.viewport(), |_, gl| clear(self.bg_color, gl));
        }

        // draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color, square, c.transform, gl));
        }

        // draw the cursor dot
        {
            let dot = ellipse::circle(self.virtual_cursor_pos[0], self.virtual_cursor_pos[1], 5.0);
            gl_graphics.draw(args.viewport(), |c, gl| ellipse([0.0, 1.0, 0.0, 1.0], dot, c.transform, gl));
        }
    }
}

struct Character {
    color: [f32; 4],
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: [f32; 4], tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0f64, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("calaxite", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .unwrap_or_else(|e| panic!("Could not create window: {}", e));

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = RebindBuilder::new(WINDOW_SIZE)
        .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
        .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
        .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
        .build_translator();

    let character = Character::new([1.0, 0.0, 0.0, 1.0],
                                   [30.0, (WINDOW_SIZE.1 as f64) * 0.85],
                                   50.0);

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        translator: translator,
        character: character,
        virtual_cursor_pos: [0.0, 0.0],
        bg_color: [0.0, 0.0, 0.0, 1.0] // black background
    };

    for e in  app.window.clone().events() {
        let app_window = app.window.clone();
        match e {
            Event::Render(r) => { app.render(&r); },
            Event::Update(u) => { app.update(&u, app_window); },
            Event::Input(i) => { app.input(&i); },
            _ => { }
        }
    }
}
<|endoftext|>


<commit_before>use std::hash::Hash;

use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};

use time::{Tm, Timespec};

use log::{HTTPLogRecord, LogProcessor, HTTPStatus};

pub mod printer;
mod utils;

struct ObjectStats {
    requests: u64,
    request_time: u64,
    sent_bytes: u64
}

type StatsItem<'r, T> = (&'r T, &'r ObjectStats);
type StatsMap<T> = HashMap<T, ObjectStats>;

pub struct LogStats {
    start: Option<Tm>,
    end: Option<Tm>,
    start_sec: Option<Timespec>,
    end_sec: Option<Timespec>,
    total: ObjectStats,
    clients: StatsMap<String>,
    hosts: StatsMap<String>,
    methods: StatsMap<String>,
    paths: StatsMap<String>,
    statuses: StatsMap<HTTPStatus>,
    referers: StatsMap<String>,
    user_agents: StatsMap<String>,
    hours: StatsMap<u8>,
    dates: StatsMap<String>,
    users: StatsMap<String>,
}

impl LogStats {
    pub fn new() -> LogStats {
        LogStats{
            start: None,
            end: None,
            start_sec: None,
            end_sec: None,
            total: ObjectStats{
                requests: 0,
                request_time: 0,
                sent_bytes: 0
                },
            clients: HashMap::with_capacity(100),
            hosts: HashMap::with_capacity(1),
            methods: HashMap::with_capacity(3), // GET, POST, HEAD
            paths: HashMap::with_capacity(20),
            statuses: HashMap::with_capacity(10),
            referers: HashMap::with_capacity(100),
            user_agents: HashMap::with_capacity(100),
            hours: HashMap::with_capacity(24),
            dates: HashMap::with_capacity(2),
            users: HashMap::with_capacity(1),
            }
    }
}

impl LogProcessor for LogStats {
    #[inline]
    fn process(&mut self, record: HTTPLogRecord) {
        update_interval(self, &record.local_time);
        update_totals(&mut self.total, &record);
        update(&mut self.clients, record.remote_addr.into_string(), &record);
        update(&mut self.hosts, record.host.into_string(), &record);
        update(&mut self.methods, record.method.into_string(), &record);
        update(&mut self.paths, record.path.into_string(), &record);
        update(&mut self.statuses, record.status, &record);
        update(&mut self.referers, record.referer.into_string(), &record);
        update(&mut self.user_agents, record.user_agent.into_string(), &record);
        update(&mut self.hours, record.local_time.tm_hour as u8, &record);
        update(&mut self.dates, record.local_time.strftime("%Y-%m-%d").unwrap(),
            &record);
        update(&mut self.users, record.user.into_string(), &record);
    }
}

#[inline]
fn update_interval(stats: &mut LogStats, current: &Tm) {
    let timespec = current.to_utc().to_timespec();
    match stats.start_sec {
        Some(start_sec) if start_sec <= timespec => {},
        _ => {
            stats.start_sec = Some(timespec);
            stats.start = Some(current.clone());
        }
    }
    match stats.end_sec {
        Some(end_sec) if end_sec >= timespec => {},
        _ => {
            stats.end_sec = Some(timespec);
            stats.end = Some(current.clone());
        }
    }
}

#[inline]
fn update_totals(totals: &mut ObjectStats,  record: &HTTPLogRecord) {
    totals.requests += 1;
    totals.request_time += record.request_time;
    totals.sent_bytes += record.sent_bytes;
}

#[inline]
fn update<T: Eq + Hash>(mapping: &mut StatsMap<T>, key: T,
        record: &HTTPLogRecord) {
    match mapping.entry(key) {
        Vacant(entry) => {
            entry.set(ObjectStats{requests: 1,
                                  request_time: record.request_time,
                                  sent_bytes: record.sent_bytes,
                                  });
        },
        Occupied(mut entry) => {
            let stats = entry.get_mut();
            stats.requests += 1;
            stats.request_time += record.request_time;
            stats.sent_bytes += record.sent_bytes;
        }
    };
}
<commit_msg>Rename hashmap => hash_map<commit_after>use std::hash::Hash;

use std::collections::HashMap;
use std::collections::hash_map::{Occupied, Vacant};

use time::{Tm, Timespec};

use log::{HTTPLogRecord, LogProcessor, HTTPStatus};

pub mod printer;
mod utils;

struct ObjectStats {
    requests: u64,
    request_time: u64,
    sent_bytes: u64
}

type StatsItem<'r, T> = (&'r T, &'r ObjectStats);
type StatsMap<T> = HashMap<T, ObjectStats>;

pub struct LogStats {
    start: Option<Tm>,
    end: Option<Tm>,
    start_sec: Option<Timespec>,
    end_sec: Option<Timespec>,
    total: ObjectStats,
    clients: StatsMap<String>,
    hosts: StatsMap<String>,
    methods: StatsMap<String>,
    paths: StatsMap<String>,
    statuses: StatsMap<HTTPStatus>,
    referers: StatsMap<String>,
    user_agents: StatsMap<String>,
    hours: StatsMap<u8>,
    dates: StatsMap<String>,
    users: StatsMap<String>,
}

impl LogStats {
    pub fn new() -> LogStats {
        LogStats{
            start: None,
            end: None,
            start_sec: None,
            end_sec: None,
            total: ObjectStats{
                requests: 0,
                request_time: 0,
                sent_bytes: 0
                },
            clients: HashMap::with_capacity(100),
            hosts: HashMap::with_capacity(1),
            methods: HashMap::with_capacity(3), // GET, POST, HEAD
            paths: HashMap::with_capacity(20),
            statuses: HashMap::with_capacity(10),
            referers: HashMap::with_capacity(100),
            user_agents: HashMap::with_capacity(100),
            hours: HashMap::with_capacity(24),
            dates: HashMap::with_capacity(2),
            users: HashMap::with_capacity(1),
            }
    }
}

impl LogProcessor for LogStats {
    #[inline]
    fn process(&mut self, record: HTTPLogRecord) {
        update_interval(self, &record.local_time);
        update_totals(&mut self.total, &record);
        update(&mut self.clients, record.remote_addr.into_string(), &record);
        update(&mut self.hosts, record.host.into_string(), &record);
        update(&mut self.methods, record.method.into_string(), &record);
        update(&mut self.paths, record.path.into_string(), &record);
        update(&mut self.statuses, record.status, &record);
        update(&mut self.referers, record.referer.into_string(), &record);
        update(&mut self.user_agents, record.user_agent.into_string(), &record);
        update(&mut self.hours, record.local_time.tm_hour as u8, &record);
        update(&mut self.dates, record.local_time.strftime("%Y-%m-%d").unwrap(),
            &record);
        update(&mut self.users, record.user.into_string(), &record);
    }
}

#[inline]
fn update_interval(stats: &mut LogStats, current: &Tm) {
    let timespec = current.to_utc().to_timespec();
    match stats.start_sec {
        Some(start_sec) if start_sec <= timespec => {},
        _ => {
            stats.start_sec = Some(timespec);
            stats.start = Some(current.clone());
        }
    }
    match stats.end_sec {
        Some(end_sec) if end_sec >= timespec => {},
        _ => {
            stats.end_sec = Some(timespec);
            stats.end = Some(current.clone());
        }
    }
}

#[inline]
fn update_totals(totals: &mut ObjectStats,  record: &HTTPLogRecord) {
    totals.requests += 1;
    totals.request_time += record.request_time;
    totals.sent_bytes += record.sent_bytes;
}

#[inline]
fn update<T: Eq + Hash>(mapping: &mut StatsMap<T>, key: T,
        record: &HTTPLogRecord) {
    match mapping.entry(key) {
        Vacant(entry) => {
            entry.set(ObjectStats{requests: 1,
                                  request_time: record.request_time,
                                  sent_bytes: record.sent_bytes,
                                  });
        },
        Occupied(mut entry) => {
            let stats = entry.get_mut();
            stats.requests += 1;
            stats.request_time += record.request_time;
            stats.sent_bytes += record.sent_bytes;
        }
    };
}
<|endoftext|>


<commit_before>use crate::argv::*;
use crate::error::ErrorKind;
use crate::test::helper::get_root_dir;
use std::env;
use std::path::Path;

// TODO: This test only consider the repository name 'rhysd/git-brws.git'

#[test]
fn args_with_no_option() {
    match Parsed::parse_iter(&["git-brws"]).unwrap() {
        Parsed::OpenPage(c) => {
            assert!(
                &[
                    "https://github.com/rhysd/git-brws.git",
                    "ssh://git@github.com:22/rhysd/git-brws.git",
                    "git@github.com:rhysd/git-brws.git",
                    // On GitHub, omitting '.git' is ok for Git URLs
                    "https://github.com/rhysd/git-brws",
                ]
                .contains(&c.repo_url.as_str()),
                "{:?}",
                c.repo_url,
            );
            assert_eq!(c.branch, None);
            assert_eq!(c.cwd, env::current_dir().unwrap().canonicalize().unwrap());
            assert!(c.args.is_empty());
            assert!(!c.stdout);
            assert!(!c.website);
            assert!(!c.blame);
        }
        r => assert!(false, "Failed to parse args with no option: {:?}", r),
    };

    match Parsed::parse_iter(&["git-brws", "foo", "bar"]).unwrap() {
        Parsed::OpenPage(c) => {
            assert_eq!(c.args.len(), 2);
        }
        p => assert!(false, "{:?}", p),
    };
}

#[test]
fn multiple_options() {
    let dir = Path::new(file!()).parent().unwrap();
    match Parsed::parse_iter(&[
        "git-brws",
        "-u",
        "-r",
        "foo/bar",
        "--dir",
        dir.to_str().unwrap(),
        "-b",
        "dev",
        "-w",
        "--blame",
    ])
    .unwrap()
    {
        Parsed::OpenPage(c) => {
            assert_eq!(c.repo_url, "https://github.com/foo/bar.git");
            assert_eq!(c.branch, Some("dev".to_string()));
            assert_eq!(c.cwd, dir.canonicalize().unwrap());
            assert_eq!(c.args.len(), 0);
            assert!(c.stdout);
            assert!(c.website);
            assert!(c.blame);
        }
        p => assert!(false, "{:?}", p),
    };
}

#[test]
fn fix_ssh_repo_url() {
    for (url, expected) in &[
        // GitHub SSH protocols with SCP form.
        // .git file extension and ssh:// and port number can be omitted
        (
            "git@github.com:user/repo.git",
            "ssh://git@github.com:22/user/repo.git",
        ),
        (
            "ssh://git@github.com/user/repo.git",
            "ssh://git@github.com/user/repo.git",
        ),
        (
            "ssh://git@github.com:22/user/repo.git",
            "ssh://git@github.com:22/user/repo.git",
        ),
        // Azure DevOps URLs
        (
            "team@vs-ssh.visualstudio.com:v3/team/repo/repo",
            "ssh://team@vs-ssh.visualstudio.com:22/v3/team/repo/repo.git",
        ),
        (
            "git@ssh.dev.azure.com:v3/team/repo/repo",
            "ssh://git@ssh.dev.azure.com:22/v3/team/repo/repo.git",
        ),
        // Port number is not omitted
        (
            "git@github.somewhere.com:123:user/repo.git",
            "ssh://git@github.somewhere.com:123/user/repo.git",
        ),
        // Issue #24. When a user name is integer
        (
            "git@github.com:1234/repo.git",
            "ssh://git@github.com:22/1234/repo.git",
        ),
        (
            "git@github.com:0x7FFFFFFFFFFFFFFF/repo.git",
            "ssh://git@github.com:22/0x7FFFFFFFFFFFFFFF/repo.git",
        ),
        (
            "git@github.somewhere.com:123:456/repo.git",
            "ssh://git@github.somewhere.com:123/456/repo.git",
        ),
    ] {
        match Parsed::parse_iter(&["git-brws", "-r", url]).unwrap() {
            Parsed::OpenPage(c) => {
                assert_eq!(c.repo_url, *expected);
            }
            p => assert!(
                false,
                "Parse must be succeeded but actually results in {:?}",
                p
            ),
        }
    }
}

#[test]
fn repo_formatting() {
    let p = |r| Parsed::parse_iter(&["git-brws", "-r", r]).unwrap();
    match p("bitbucket.org/foo/bar") {
        Parsed::OpenPage(c) => assert_eq!(c.repo_url, "https://bitbucket.org/foo/bar.git"),
        p => assert!(false, "{:?}", p),
    }
    match p("https://gitlab.com/foo/bar") {
        Parsed::OpenPage(c) => assert_eq!(c.repo_url, "https://gitlab.com/foo/bar.git"),
        p => assert!(false, "{:?}", p),
    }
    match p("foo/bar") {
        Parsed::OpenPage(c) => assert_eq!(c.repo_url, "https://github.com/foo/bar.git"),
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn valid_remote_name() {
    match Parsed::parse_iter(&["git-brws", "-R", "origin"]).unwrap() {
        Parsed::OpenPage(c) => assert!(
            [
                "https://github.com/rhysd/git-brws.git",
                // On GitHub, omitting '.git' is ok for Git URLs
                "https://github.com/rhysd/git-brws",
                "ssh://git@github.com:22/rhysd/git-brws.git"
            ]
            .contains(&c.repo_url.as_str()),
            "Unexpected remote URL for 'origin' remote: {}. For pull request, please ignore this test is failing",
            c.repo_url,
        ),
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn invalid_remote_name() {
    match Parsed::parse_iter(&["git-brws", "-R", "this-remote-is-never-existing"])
        .unwrap_err()
        .kind()
    {
        ErrorKind::GitObjectNotFound { kind, object, .. } => {
            assert_eq!(*kind, "remote");
            assert_eq!(object, "this-remote-is-never-existing");
        }
        e => assert!(false, "Unexpected error: {}", e),
    }
}

#[test]
fn help_option() {
    match Parsed::parse_iter(&["git-brws", "-h"]).unwrap() {
        Parsed::Help(s) => {
            assert!(s.starts_with("Usage:"));
        }
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn version_option() {
    match Parsed::parse_iter(&["git-brws", "-v"]).unwrap() {
        Parsed::Version(s) => {
            assert!(!s.is_empty());
        }
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn unknown_options() {
    assert!(Parsed::parse_iter(&["git-brws", "--unknown"]).is_err());
}

// For checking #9
#[test]
fn specify_repo_outside_repository() {
    let root = get_root_dir();
    match Parsed::parse_iter(&["git-brws", "-d", root.to_str().unwrap(), "-r", "foo/bar"]).unwrap()
    {
        Parsed::OpenPage(c) => {
            assert_eq!(c.cwd, root);
            assert_eq!(&c.repo_url, "https://github.com/foo/bar.git");
        }
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn search_repo_from_github_by_name() {
    skip_if_no_token_for_search!();

    // Add user:rhysd to ensure to get expected result. But actually repository name is usually
    // passed like `-r react` as use case.
    let parsed = Parsed::parse_iter(&["git-brws", "-r", "user:rhysd vim.wasm"]).unwrap();
    match parsed {
        Parsed::OpenPage(c) => {
            assert_eq!(&c.repo_url, "https://github.com/rhysd/vim.wasm.git");
        }
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn repo_specified_but_argument_is_not_empty() {
    let err = Parsed::parse_iter(&["git-brws", "-r", "foo", "HEAD"]).unwrap_err();
    match err.kind() {
        ErrorKind::ArgsNotAllowed { ref args, .. } => {
            assert!(format!("{}", err).contains("\"HEAD\""), "{:?}", args);
        }
        e => assert!(false, "Unexpected error: {}", e),
    }
}
<commit_msg>add tests for -c option<commit_after>use crate::argv::*;
use crate::error::ErrorKind;
use crate::test::helper::get_root_dir;
use std::env;
use std::path::Path;

// TODO: This test only consider the repository name 'rhysd/git-brws.git'

#[test]
fn args_with_no_option() {
    match Parsed::parse_iter(&["git-brws"]).unwrap() {
        Parsed::OpenPage(c) => {
            assert!(
                &[
                    "https://github.com/rhysd/git-brws.git",
                    "ssh://git@github.com:22/rhysd/git-brws.git",
                    "git@github.com:rhysd/git-brws.git",
                    // On GitHub, omitting '.git' is ok for Git URLs
                    "https://github.com/rhysd/git-brws",
                ]
                .contains(&c.repo_url.as_str()),
                "{:?}",
                c.repo_url,
            );
            assert_eq!(c.branch, None);
            assert_eq!(c.cwd, env::current_dir().unwrap().canonicalize().unwrap());
            assert!(c.args.is_empty());
            assert!(!c.stdout);
            assert!(!c.website);
            assert!(!c.blame);
        }
        r => assert!(false, "Failed to parse args with no option: {:?}", r),
    };

    match Parsed::parse_iter(&["git-brws", "foo", "bar"]).unwrap() {
        Parsed::OpenPage(c) => {
            assert_eq!(c.args.len(), 2);
        }
        p => assert!(false, "{:?}", p),
    };
}

#[test]
fn multiple_options() {
    let dir = Path::new(file!()).parent().unwrap();
    match Parsed::parse_iter(&[
        "git-brws",
        "-u",
        "-r",
        "foo/bar",
        "--dir",
        dir.to_str().unwrap(),
        "-b",
        "dev",
        "-w",
        "--blame",
    ])
    .unwrap()
    {
        Parsed::OpenPage(c) => {
            assert_eq!(c.repo_url, "https://github.com/foo/bar.git");
            assert_eq!(c.branch, Some("dev".to_string()));
            assert_eq!(c.cwd, dir.canonicalize().unwrap());
            assert_eq!(c.args.len(), 0);
            assert!(c.stdout);
            assert!(c.website);
            assert!(c.blame);
        }
        p => assert!(false, "{:?}", p),
    };
}

#[test]
fn fix_ssh_repo_url() {
    for (url, expected) in &[
        // GitHub SSH protocols with SCP form.
        // .git file extension and ssh:// and port number can be omitted
        (
            "git@github.com:user/repo.git",
            "ssh://git@github.com:22/user/repo.git",
        ),
        (
            "ssh://git@github.com/user/repo.git",
            "ssh://git@github.com/user/repo.git",
        ),
        (
            "ssh://git@github.com:22/user/repo.git",
            "ssh://git@github.com:22/user/repo.git",
        ),
        // Azure DevOps URLs
        (
            "team@vs-ssh.visualstudio.com:v3/team/repo/repo",
            "ssh://team@vs-ssh.visualstudio.com:22/v3/team/repo/repo.git",
        ),
        (
            "git@ssh.dev.azure.com:v3/team/repo/repo",
            "ssh://git@ssh.dev.azure.com:22/v3/team/repo/repo.git",
        ),
        // Port number is not omitted
        (
            "git@github.somewhere.com:123:user/repo.git",
            "ssh://git@github.somewhere.com:123/user/repo.git",
        ),
        // Issue #24. When a user name is integer
        (
            "git@github.com:1234/repo.git",
            "ssh://git@github.com:22/1234/repo.git",
        ),
        (
            "git@github.com:0x7FFFFFFFFFFFFFFF/repo.git",
            "ssh://git@github.com:22/0x7FFFFFFFFFFFFFFF/repo.git",
        ),
        (
            "git@github.somewhere.com:123:456/repo.git",
            "ssh://git@github.somewhere.com:123/456/repo.git",
        ),
    ] {
        match Parsed::parse_iter(&["git-brws", "-r", url]).unwrap() {
            Parsed::OpenPage(c) => {
                assert_eq!(c.repo_url, *expected);
            }
            p => assert!(
                false,
                "Parse must be succeeded but actually results in {:?}",
                p
            ),
        }
    }
}

#[test]
fn repo_formatting() {
    let p = |r| Parsed::parse_iter(&["git-brws", "-r", r]).unwrap();
    match p("bitbucket.org/foo/bar") {
        Parsed::OpenPage(c) => assert_eq!(c.repo_url, "https://bitbucket.org/foo/bar.git"),
        p => assert!(false, "{:?}", p),
    }
    match p("https://gitlab.com/foo/bar") {
        Parsed::OpenPage(c) => assert_eq!(c.repo_url, "https://gitlab.com/foo/bar.git"),
        p => assert!(false, "{:?}", p),
    }
    match p("foo/bar") {
        Parsed::OpenPage(c) => assert_eq!(c.repo_url, "https://github.com/foo/bar.git"),
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn valid_remote_name() {
    match Parsed::parse_iter(&["git-brws", "-R", "origin"]).unwrap() {
        Parsed::OpenPage(c) => assert!(
            [
                "https://github.com/rhysd/git-brws.git",
                // On GitHub, omitting '.git' is ok for Git URLs
                "https://github.com/rhysd/git-brws",
                "ssh://git@github.com:22/rhysd/git-brws.git"
            ]
            .contains(&c.repo_url.as_str()),
            "Unexpected remote URL for 'origin' remote: {}. For pull request, please ignore this test is failing",
            c.repo_url,
        ),
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn invalid_remote_name() {
    match Parsed::parse_iter(&["git-brws", "-R", "this-remote-is-never-existing"])
        .unwrap_err()
        .kind()
    {
        ErrorKind::GitObjectNotFound { kind, object, .. } => {
            assert_eq!(*kind, "remote");
            assert_eq!(object, "this-remote-is-never-existing");
        }
        e => assert!(false, "Unexpected error: {}", e),
    }
}

#[test]
fn help_option() {
    match Parsed::parse_iter(&["git-brws", "-h"]).unwrap() {
        Parsed::Help(s) => {
            assert!(s.starts_with("Usage:"));
        }
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn version_option() {
    match Parsed::parse_iter(&["git-brws", "-v"]).unwrap() {
        Parsed::Version(s) => {
            assert!(!s.is_empty());
        }
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn unknown_options() {
    assert!(Parsed::parse_iter(&["git-brws", "--unknown"]).is_err());
}

// For checking #9
#[test]
fn specify_repo_outside_repository() {
    let root = get_root_dir();
    match Parsed::parse_iter(&["git-brws", "-d", root.to_str().unwrap(), "-r", "foo/bar"]).unwrap()
    {
        Parsed::OpenPage(c) => {
            assert_eq!(c.cwd, root);
            assert_eq!(&c.repo_url, "https://github.com/foo/bar.git");
        }
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn search_repo_from_github_by_name() {
    skip_if_no_token_for_search!();

    // Add user:rhysd to ensure to get expected result. But actually repository name is usually
    // passed like `-r react` as use case.
    let parsed = Parsed::parse_iter(&["git-brws", "-r", "user:rhysd vim.wasm"]).unwrap();
    match parsed {
        Parsed::OpenPage(c) => {
            assert_eq!(&c.repo_url, "https://github.com/rhysd/vim.wasm.git");
        }
        p => assert!(false, "{:?}", p),
    }
}

#[test]
fn repo_specified_but_argument_is_not_empty() {
    let err = Parsed::parse_iter(&["git-brws", "-r", "foo", "HEAD"]).unwrap_err();
    match err.kind() {
        ErrorKind::ArgsNotAllowed { ref args, .. } => {
            assert!(format!("{}", err).contains("\"HEAD\""), "{:?}", args);
        }
        e => assert!(false, "Unexpected error: {}", e),
    }
}

#[test]
fn current_branch_flag() {
    match Parsed::parse_iter(&["git-brws", "-c"]).unwrap() {
        Parsed::OpenPage(c) => {
            assert!(c.branch.is_some());
        }
        p => panic!("{:?}", p),
    }

    // When --current-branch and --branch are both specified, --branch is prioritized.
    // Note: -r is necessary to avoid "no such branch 'dev'" error.
    match Parsed::parse_iter(&["git-brws", "-r", "foo/bar", "-c", "-b", "dev"]).unwrap() {
        Parsed::OpenPage(c) => {
            assert_eq!(c.branch, Some("dev".to_string()));
        }
        p => panic!("{:?}", p),
    }
}
<|endoftext|>