<commit_before>// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::color;
use crate::errors::*;
use crate::read_write::{
    decode, fixpoint_decode, AttributeReader, DataWriter, Encoding, NodeReader, NodeWriter,
    OpenMode, PositionEncoding, WriteEncoded, WriteLE,
};
use crate::{attribute_extension, AttributeData, AttributeDataType, Point, PointsBatch};
use byteorder::{LittleEndian, ReadBytesExt};
use cgmath::Vector3;
use num_traits::identities::Zero;
use std::collections::{BTreeMap, HashMap};
use std::io::{self, BufReader, ErrorKind, Read};
use std::path::PathBuf;

pub struct RawNodeReader {
    xyz_reader: BufReader<Box<dyn Read>>,
    attribute_readers: HashMap<String, AttributeReader>,
    encoding: Encoding,
}

impl NodeReader for RawNodeReader {
    fn read(&mut self) -> io::Result<Point> {
        let mut point = Point {
            position: Vector3::zero(),
            color: color::RED.to_u8(), // is overwritten
            intensity: None,
        };

        // I tried pulling out this match by taking a function pointer to a 'decode_position'
        // function. This replaces a branch per point vs a function call per point and turned
        // out to be marginally slower.
        match self.encoding {
            Encoding::Plain => {
                point.position.x = self.xyz_reader.read_f64::<LittleEndian>()?;
                point.position.y = self.xyz_reader.read_f64::<LittleEndian>()?;
                point.position.z = self.xyz_reader.read_f64::<LittleEndian>()?;
            }
            Encoding::ScaledToCube(min, edge_length, ref pos) => match pos {
                PositionEncoding::Uint8 => {
                    point.position.x =
                        fixpoint_decode(self.xyz_reader.read_u8()?, min.x, edge_length);
                    point.position.y =
                        fixpoint_decode(self.xyz_reader.read_u8()?, min.y, edge_length);
                    point.position.z =
                        fixpoint_decode(self.xyz_reader.read_u8()?, min.z, edge_length);
                }
                PositionEncoding::Uint16 => {
                    point.position.x = fixpoint_decode(
                        self.xyz_reader.read_u16::<LittleEndian>()?,
                        min.x,
                        edge_length,
                    );
                    point.position.y = fixpoint_decode(
                        self.xyz_reader.read_u16::<LittleEndian>()?,
                        min.y,
                        edge_length,
                    );
                    point.position.z = fixpoint_decode(
                        self.xyz_reader.read_u16::<LittleEndian>()?,
                        min.z,
                        edge_length,
                    );
                }
                PositionEncoding::Float32 => {
                    point.position.x = decode(
                        self.xyz_reader.read_f32::<LittleEndian>()?,
                        min.x,
                        edge_length,
                    );
                    point.position.y = decode(
                        self.xyz_reader.read_f32::<LittleEndian>()?,
                        min.y,
                        edge_length,
                    );
                    point.position.z = decode(
                        self.xyz_reader.read_f32::<LittleEndian>()?,
                        min.z,
                        edge_length,
                    );
                }
                PositionEncoding::Float64 => {
                    point.position.x = decode(
                        self.xyz_reader.read_f64::<LittleEndian>()?,
                        min.x,
                        edge_length,
                    );
                    point.position.y = decode(
                        self.xyz_reader.read_f64::<LittleEndian>()?,
                        min.y,
                        edge_length,
                    );
                    point.position.z = decode(
                        self.xyz_reader.read_f64::<LittleEndian>()?,
                        min.z,
                        edge_length,
                    );
                }
            },
        }

        if let Some(cr) = self.attribute_readers.get_mut("color") {
            point.color.red = cr.reader.read_u8()?;
            point.color.green = cr.reader.read_u8()?;
            point.color.blue = cr.reader.read_u8()?;
        }

        if let Some(ir) = self.attribute_readers.get_mut("intensity") {
            point.intensity = Some(ir.reader.read_f32::<LittleEndian>()?);
        }

        Ok(point)
    }
}

impl RawNodeReader {
    pub fn read_batch(&mut self, num_points: usize) -> io::Result<PointsBatch> {
        let mut batch = PointsBatch {
            position: vec![],
            attributes: BTreeMap::new(),
        };

        match self.encoding {
            Encoding::Plain => (0..num_points).try_for_each(|_| -> io::Result<()> {
                let x = self.xyz_reader.read_f64::<LittleEndian>()?;
                let y = self.xyz_reader.read_f64::<LittleEndian>()?;
                let z = self.xyz_reader.read_f64::<LittleEndian>()?;
                batch.position.push(Vector3::new(x, y, z));
                Ok(())
            })?,
            Encoding::ScaledToCube(min, edge_length, ref pos) => match pos {
                PositionEncoding::Uint8 => (0..num_points).try_for_each(|_| -> io::Result<()> {
                    let x = fixpoint_decode(self.xyz_reader.read_u8()?, min.x, edge_length);
                    let y = fixpoint_decode(self.xyz_reader.read_u8()?, min.y, edge_length);
                    let z = fixpoint_decode(self.xyz_reader.read_u8()?, min.z, edge_length);
                    batch.position.push(Vector3::new(x, y, z));
                    Ok(())
                })?,

                PositionEncoding::Uint16 => {
                    (0..num_points).try_for_each(|_| -> io::Result<()> {
                        let x = fixpoint_decode(
                            self.xyz_reader.read_u16::<LittleEndian>()?,
                            min.x,
                            edge_length,
                        );
                        let y = fixpoint_decode(
                            self.xyz_reader.read_u16::<LittleEndian>()?,
                            min.y,
                            edge_length,
                        );
                        let z = fixpoint_decode(
                            self.xyz_reader.read_u16::<LittleEndian>()?,
                            min.z,
                            edge_length,
                        );
                        batch.position.push(Vector3::new(x, y, z));
                        Ok(())
                    })?
                }

                PositionEncoding::Float32 => {
                    (0..num_points).try_for_each(|_| -> io::Result<()> {
                        let x = decode(
                            self.xyz_reader.read_f32::<LittleEndian>()?,
                            min.x,
                            edge_length,
                        );
                        let y = decode(
                            self.xyz_reader.read_f32::<LittleEndian>()?,
                            min.y,
                            edge_length,
                        );
                        let z = decode(
                            self.xyz_reader.read_f32::<LittleEndian>()?,
                            min.z,
                            edge_length,
                        );
                        batch.position.push(Vector3::new(x, y, z));
                        Ok(())
                    })?
                }

                PositionEncoding::Float64 => {
                    (0..num_points).try_for_each(|_| -> io::Result<()> {
                        let x = decode(
                            self.xyz_reader.read_f64::<LittleEndian>()?,
                            min.x,
                            edge_length,
                        );
                        let y = decode(
                            self.xyz_reader.read_f64::<LittleEndian>()?,
                            min.y,
                            edge_length,
                        );
                        let z = decode(
                            self.xyz_reader.read_f64::<LittleEndian>()?,
                            min.z,
                            edge_length,
                        );
                        batch.position.push(Vector3::new(x, y, z));
                        Ok(())
                    })?
                }
            },
        };

        self.attribute_readers.iter_mut().try_for_each(
            |(key, AttributeReader { data_type, reader })| -> io::Result<()> {
                match data_type {
                    AttributeDataType::U8 => {
                        let mut attr = vec![0; num_points];
                        reader.read_exact(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::U8(attr));
                    }
                    AttributeDataType::I64 => {
                        let mut attr = vec![0; num_points];
                        reader.read_i64_into::<LittleEndian>(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::I64(attr));
                    }
                    AttributeDataType::U64 => {
                        let mut attr = vec![0; num_points];
                        reader.read_u64_into::<LittleEndian>(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::U64(attr));
                    }
                    AttributeDataType::F32 => {
                        let mut attr = vec![0.0; num_points];
                        reader.read_f32_into::<LittleEndian>(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::F32(attr));
                    }
                    AttributeDataType::F64 => {
                        let mut attr = vec![0.0; num_points];
                        reader.read_f64_into::<LittleEndian>(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::F64(attr));
                    }
                    AttributeDataType::U8Vec3 => {
                        let mut attr = Vec::with_capacity(num_points);
                        let mut buffer = vec![0; 3 * num_points];
                        reader.read_exact(&mut buffer)?;
                        for i in 0..num_points {
                            attr.push(Vector3::new(buffer[i], buffer[i + 1], buffer[i + 2]));
                        }
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::U8Vec3(attr));
                    }
                    AttributeDataType::F64Vec3 => {
                        let mut attr = Vec::with_capacity(num_points);
                        let mut buffer = vec![0.0; 3 * num_points];
                        reader.read_f64_into::<LittleEndian>(&mut buffer)?;
                        for i in 0..num_points {
                            attr.push(Vector3::new(buffer[i], buffer[i + 1], buffer[i + 2]));
                        }
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::F64Vec3(attr));
                    }
                };
                Ok(())
            },
        )?;

        let num_points = batch.position.len();

        // If the attributes differ in length, something was wrong with the files.
        if batch
            .attributes
            .values()
            .all(|attr| attr.len() == num_points)
        {
            Ok(batch)
        } else {
            Err(io::Error::new(
                ErrorKind::InvalidData,
                "Attributes differ in length",
            ))
        }
    }
}

impl RawNodeReader {
    pub fn new(
        xyz_reader: Box<dyn Read>,
        attribute_readers: HashMap<String, AttributeReader>,
        encoding: Encoding,
    ) -> Result<Self> {
        let xyz_reader = BufReader::new(xyz_reader);

        Ok(Self {
            xyz_reader,
            attribute_readers,
            encoding,
        })
    }
}

pub struct RawNodeWriter {
    xyz_writer: DataWriter,
    attribute_writers: Vec<DataWriter>,
    stem: PathBuf,
    encoding: Encoding,
    open_mode: OpenMode,
}

impl NodeWriter<PointsBatch> for RawNodeWriter {
    fn new(path: impl Into<PathBuf>, encoding: Encoding, open_mode: OpenMode) -> Self {
        Self::new(path, encoding, open_mode)
    }

    fn write(&mut self, p: &PointsBatch) -> io::Result<()> {
        p.position
            .write_encoded(&self.encoding, &mut self.xyz_writer)?;

        if self.attribute_writers.is_empty() {
            for name in p.attributes.keys() {
                self.attribute_writers.push(DataWriter::new(
                    &self.stem.with_extension(attribute_extension(&name)),
                    self.open_mode,
                )?)
            }
        }

        for (i, data) in p.attributes.values().enumerate() {
            data.write_le(&mut self.attribute_writers[i])?;
        }

        Ok(())
    }
}

impl NodeWriter<Point> for RawNodeWriter {
    fn new(path: impl Into<PathBuf>, encoding: Encoding, open_mode: OpenMode) -> Self {
        Self::new(path, encoding, open_mode)
    }

    fn write(&mut self, p: &Point) -> io::Result<()> {
        p.position
            .write_encoded(&self.encoding, &mut self.xyz_writer)?;

        if self.attribute_writers.is_empty() {
            self.attribute_writers.push(DataWriter::new(
                &self.stem.with_extension(attribute_extension("color")),
                self.open_mode,
            )?);
            if p.intensity.is_some() {
                self.attribute_writers.push(DataWriter::new(
                    &self.stem.with_extension(attribute_extension("intensity")),
                    self.open_mode,
                )?);
            }
        }
        p.color.write_le(&mut self.attribute_writers[0])?;
        if let Some(i) = p.intensity {
            i.write_le(&mut self.attribute_writers[1])?;
        }

        Ok(())
    }
}

impl RawNodeWriter {
    pub fn new(path: impl Into<PathBuf>, encoding: Encoding, open_mode: OpenMode) -> Self {
        let stem: PathBuf = path.into();
        let xyz_writer = DataWriter::new(
            &stem.with_extension(attribute_extension("position")),
            open_mode,
        )
        .unwrap();
        let attribute_writers = Vec::new();
        Self {
            xyz_writer,
            attribute_writers,
            stem,
            encoding,
            open_mode,
        }
    }

    pub fn num_written(&self) -> i64 {
        let bytes_per_coordinate = match &self.encoding {
            Encoding::Plain => std::mem::size_of::<f64>(),
            Encoding::ScaledToCube(_, _, pos_enc) => pos_enc.bytes_per_coordinate(),
        } as i64;
        (self.xyz_writer.bytes_written() as i64 / bytes_per_coordinate / 3)
    }
}
<commit_msg>Fix vec3 reading. (#330)<commit_after>// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::color;
use crate::errors::*;
use crate::read_write::{
    decode, fixpoint_decode, AttributeReader, DataWriter, Encoding, NodeReader, NodeWriter,
    OpenMode, PositionEncoding, WriteEncoded, WriteLE,
};
use crate::{attribute_extension, AttributeData, AttributeDataType, Point, PointsBatch};
use byteorder::{LittleEndian, ReadBytesExt};
use cgmath::Vector3;
use num_traits::identities::Zero;
use std::collections::{BTreeMap, HashMap};
use std::io::{self, BufReader, ErrorKind, Read};
use std::path::PathBuf;

pub struct RawNodeReader {
    xyz_reader: BufReader<Box<dyn Read>>,
    attribute_readers: HashMap<String, AttributeReader>,
    encoding: Encoding,
}

impl NodeReader for RawNodeReader {
    fn read(&mut self) -> io::Result<Point> {
        let mut point = Point {
            position: Vector3::zero(),
            color: color::RED.to_u8(), // is overwritten
            intensity: None,
        };

        // I tried pulling out this match by taking a function pointer to a 'decode_position'
        // function. This replaces a branch per point vs a function call per point and turned
        // out to be marginally slower.
        match self.encoding {
            Encoding::Plain => {
                point.position.x = self.xyz_reader.read_f64::<LittleEndian>()?;
                point.position.y = self.xyz_reader.read_f64::<LittleEndian>()?;
                point.position.z = self.xyz_reader.read_f64::<LittleEndian>()?;
            }
            Encoding::ScaledToCube(min, edge_length, ref pos) => match pos {
                PositionEncoding::Uint8 => {
                    point.position.x =
                        fixpoint_decode(self.xyz_reader.read_u8()?, min.x, edge_length);
                    point.position.y =
                        fixpoint_decode(self.xyz_reader.read_u8()?, min.y, edge_length);
                    point.position.z =
                        fixpoint_decode(self.xyz_reader.read_u8()?, min.z, edge_length);
                }
                PositionEncoding::Uint16 => {
                    point.position.x = fixpoint_decode(
                        self.xyz_reader.read_u16::<LittleEndian>()?,
                        min.x,
                        edge_length,
                    );
                    point.position.y = fixpoint_decode(
                        self.xyz_reader.read_u16::<LittleEndian>()?,
                        min.y,
                        edge_length,
                    );
                    point.position.z = fixpoint_decode(
                        self.xyz_reader.read_u16::<LittleEndian>()?,
                        min.z,
                        edge_length,
                    );
                }
                PositionEncoding::Float32 => {
                    point.position.x = decode(
                        self.xyz_reader.read_f32::<LittleEndian>()?,
                        min.x,
                        edge_length,
                    );
                    point.position.y = decode(
                        self.xyz_reader.read_f32::<LittleEndian>()?,
                        min.y,
                        edge_length,
                    );
                    point.position.z = decode(
                        self.xyz_reader.read_f32::<LittleEndian>()?,
                        min.z,
                        edge_length,
                    );
                }
                PositionEncoding::Float64 => {
                    point.position.x = decode(
                        self.xyz_reader.read_f64::<LittleEndian>()?,
                        min.x,
                        edge_length,
                    );
                    point.position.y = decode(
                        self.xyz_reader.read_f64::<LittleEndian>()?,
                        min.y,
                        edge_length,
                    );
                    point.position.z = decode(
                        self.xyz_reader.read_f64::<LittleEndian>()?,
                        min.z,
                        edge_length,
                    );
                }
            },
        }

        if let Some(cr) = self.attribute_readers.get_mut("color") {
            point.color.red = cr.reader.read_u8()?;
            point.color.green = cr.reader.read_u8()?;
            point.color.blue = cr.reader.read_u8()?;
        }

        if let Some(ir) = self.attribute_readers.get_mut("intensity") {
            point.intensity = Some(ir.reader.read_f32::<LittleEndian>()?);
        }

        Ok(point)
    }
}

impl RawNodeReader {
    pub fn read_batch(&mut self, num_points: usize) -> io::Result<PointsBatch> {
        let mut batch = PointsBatch {
            position: vec![],
            attributes: BTreeMap::new(),
        };

        match self.encoding {
            Encoding::Plain => (0..num_points).try_for_each(|_| -> io::Result<()> {
                let x = self.xyz_reader.read_f64::<LittleEndian>()?;
                let y = self.xyz_reader.read_f64::<LittleEndian>()?;
                let z = self.xyz_reader.read_f64::<LittleEndian>()?;
                batch.position.push(Vector3::new(x, y, z));
                Ok(())
            })?,
            Encoding::ScaledToCube(min, edge_length, ref pos) => match pos {
                PositionEncoding::Uint8 => (0..num_points).try_for_each(|_| -> io::Result<()> {
                    let x = fixpoint_decode(self.xyz_reader.read_u8()?, min.x, edge_length);
                    let y = fixpoint_decode(self.xyz_reader.read_u8()?, min.y, edge_length);
                    let z = fixpoint_decode(self.xyz_reader.read_u8()?, min.z, edge_length);
                    batch.position.push(Vector3::new(x, y, z));
                    Ok(())
                })?,

                PositionEncoding::Uint16 => {
                    (0..num_points).try_for_each(|_| -> io::Result<()> {
                        let x = fixpoint_decode(
                            self.xyz_reader.read_u16::<LittleEndian>()?,
                            min.x,
                            edge_length,
                        );
                        let y = fixpoint_decode(
                            self.xyz_reader.read_u16::<LittleEndian>()?,
                            min.y,
                            edge_length,
                        );
                        let z = fixpoint_decode(
                            self.xyz_reader.read_u16::<LittleEndian>()?,
                            min.z,
                            edge_length,
                        );
                        batch.position.push(Vector3::new(x, y, z));
                        Ok(())
                    })?
                }

                PositionEncoding::Float32 => {
                    (0..num_points).try_for_each(|_| -> io::Result<()> {
                        let x = decode(
                            self.xyz_reader.read_f32::<LittleEndian>()?,
                            min.x,
                            edge_length,
                        );
                        let y = decode(
                            self.xyz_reader.read_f32::<LittleEndian>()?,
                            min.y,
                            edge_length,
                        );
                        let z = decode(
                            self.xyz_reader.read_f32::<LittleEndian>()?,
                            min.z,
                            edge_length,
                        );
                        batch.position.push(Vector3::new(x, y, z));
                        Ok(())
                    })?
                }

                PositionEncoding::Float64 => {
                    (0..num_points).try_for_each(|_| -> io::Result<()> {
                        let x = decode(
                            self.xyz_reader.read_f64::<LittleEndian>()?,
                            min.x,
                            edge_length,
                        );
                        let y = decode(
                            self.xyz_reader.read_f64::<LittleEndian>()?,
                            min.y,
                            edge_length,
                        );
                        let z = decode(
                            self.xyz_reader.read_f64::<LittleEndian>()?,
                            min.z,
                            edge_length,
                        );
                        batch.position.push(Vector3::new(x, y, z));
                        Ok(())
                    })?
                }
            },
        };

        self.attribute_readers.iter_mut().try_for_each(
            |(key, AttributeReader { data_type, reader })| -> io::Result<()> {
                match data_type {
                    AttributeDataType::U8 => {
                        let mut attr = vec![0; num_points];
                        reader.read_exact(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::U8(attr));
                    }
                    AttributeDataType::I64 => {
                        let mut attr = vec![0; num_points];
                        reader.read_i64_into::<LittleEndian>(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::I64(attr));
                    }
                    AttributeDataType::U64 => {
                        let mut attr = vec![0; num_points];
                        reader.read_u64_into::<LittleEndian>(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::U64(attr));
                    }
                    AttributeDataType::F32 => {
                        let mut attr = vec![0.0; num_points];
                        reader.read_f32_into::<LittleEndian>(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::F32(attr));
                    }
                    AttributeDataType::F64 => {
                        let mut attr = vec![0.0; num_points];
                        reader.read_f64_into::<LittleEndian>(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::F64(attr));
                    }
                    AttributeDataType::U8Vec3 => {
                        let mut attr = Vec::with_capacity(num_points);
                        let mut buffer = vec![0; 3 * num_points];
                        reader.read_exact(&mut buffer)?;
                        for i in 0..num_points {
                            attr.push(Vector3::new(
                                buffer[3 * i],
                                buffer[3 * i + 1],
                                buffer[3 * i + 2],
                            ));
                        }
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::U8Vec3(attr));
                    }
                    AttributeDataType::F64Vec3 => {
                        let mut attr = Vec::with_capacity(num_points);
                        let mut buffer = vec![0.0; 3 * num_points];
                        reader.read_f64_into::<LittleEndian>(&mut buffer)?;
                        for i in 0..num_points {
                            attr.push(Vector3::new(
                                buffer[3 * i],
                                buffer[3 * i + 1],
                                buffer[3 * i + 2],
                            ));
                        }
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::F64Vec3(attr));
                    }
                };
                Ok(())
            },
        )?;

        let num_points = batch.position.len();

        // If the attributes differ in length, something was wrong with the files.
        if batch
            .attributes
            .values()
            .all(|attr| attr.len() == num_points)
        {
            Ok(batch)
        } else {
            Err(io::Error::new(
                ErrorKind::InvalidData,
                "Attributes differ in length",
            ))
        }
    }
}

impl RawNodeReader {
    pub fn new(
        xyz_reader: Box<dyn Read>,
        attribute_readers: HashMap<String, AttributeReader>,
        encoding: Encoding,
    ) -> Result<Self> {
        let xyz_reader = BufReader::new(xyz_reader);

        Ok(Self {
            xyz_reader,
            attribute_readers,
            encoding,
        })
    }
}

pub struct RawNodeWriter {
    xyz_writer: DataWriter,
    attribute_writers: Vec<DataWriter>,
    stem: PathBuf,
    encoding: Encoding,
    open_mode: OpenMode,
}

impl NodeWriter<PointsBatch> for RawNodeWriter {
    fn new(path: impl Into<PathBuf>, encoding: Encoding, open_mode: OpenMode) -> Self {
        Self::new(path, encoding, open_mode)
    }

    fn write(&mut self, p: &PointsBatch) -> io::Result<()> {
        p.position
            .write_encoded(&self.encoding, &mut self.xyz_writer)?;

        if self.attribute_writers.is_empty() {
            for name in p.attributes.keys() {
                self.attribute_writers.push(DataWriter::new(
                    &self.stem.with_extension(attribute_extension(&name)),
                    self.open_mode,
                )?)
            }
        }

        for (i, data) in p.attributes.values().enumerate() {
            data.write_le(&mut self.attribute_writers[i])?;
        }

        Ok(())
    }
}

impl NodeWriter<Point> for RawNodeWriter {
    fn new(path: impl Into<PathBuf>, encoding: Encoding, open_mode: OpenMode) -> Self {
        Self::new(path, encoding, open_mode)
    }

    fn write(&mut self, p: &Point) -> io::Result<()> {
        p.position
            .write_encoded(&self.encoding, &mut self.xyz_writer)?;

        if self.attribute_writers.is_empty() {
            self.attribute_writers.push(DataWriter::new(
                &self.stem.with_extension(attribute_extension("color")),
                self.open_mode,
            )?);
            if p.intensity.is_some() {
                self.attribute_writers.push(DataWriter::new(
                    &self.stem.with_extension(attribute_extension("intensity")),
                    self.open_mode,
                )?);
            }
        }
        p.color.write_le(&mut self.attribute_writers[0])?;
        if let Some(i) = p.intensity {
            i.write_le(&mut self.attribute_writers[1])?;
        }

        Ok(())
    }
}

impl RawNodeWriter {
    pub fn new(path: impl Into<PathBuf>, encoding: Encoding, open_mode: OpenMode) -> Self {
        let stem: PathBuf = path.into();
        let xyz_writer = DataWriter::new(
            &stem.with_extension(attribute_extension("position")),
            open_mode,
        )
        .unwrap();
        let attribute_writers = Vec::new();
        Self {
            xyz_writer,
            attribute_writers,
            stem,
            encoding,
            open_mode,
        }
    }

    pub fn num_written(&self) -> i64 {
        let bytes_per_coordinate = match &self.encoding {
            Encoding::Plain => std::mem::size_of::<f64>(),
            Encoding::ScaledToCube(_, _, pos_enc) => pos_enc.bytes_per_coordinate(),
        } as i64;
        (self.xyz_writer.bytes_written() as i64 / bytes_per_coordinate / 3)
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![deny(missing_docs)]

//! An efficient, low-level, bindless graphics API for Rust. See [the
//! blog](http://gfx-rs.github.io/) for explanations and annotated examples.

#[macro_use]
extern crate log;
extern crate draw_state;
extern crate gfx_core;

/// public re-exported traits
pub mod traits {
    pub use gfx_core::{Device, Factory, DeviceFence};
    pub use factory::FactoryExt;
}

// draw state re-exports
pub use draw_state::{preset, state};
pub use draw_state::target::*;

// public re-exports
pub use gfx_core as core;
pub use gfx_core::{Device, SubmitInfo, Resources};
pub use gfx_core::{format, handle, tex};
pub use gfx_core::factory::{Factory, BufferRole, BufferInfo, BufferUsage,
                            SHADER_RESOURCE, UNORDERED_ACCESS, RENDER_TARGET,
                            cast_slice};
pub use gfx_core::{VertexCount, InstanceCount};
pub use gfx_core::Primitive;
pub use gfx_core::{ShaderSet, VertexShader, HullShader, DomainShader,
                   GeometryShader, PixelShader};
pub use gfx_core::draw::{CommandBuffer, InstanceOption};
pub use gfx_core::shade::{ProgramInfo, UniformValue};

pub use encoder::{Encoder, UpdateError};
pub use factory::PipelineStateError;
pub use mesh::{Slice, ToIndexSlice, SliceKind};
pub use pso::{PipelineState};
pub use pso::buffer::{VertexBuffer, InstanceBuffer,
                      ConstantBuffer, Global};
pub use pso::resource::{ShaderResource, UnorderedAccess,
                        Sampler, TextureSampler};
pub use pso::target::{DepthStencilTarget, DepthTarget, StencilTarget,
                      RenderTarget, BlendTarget, BlendRef, Scissor};

/// Render commands encoder
mod encoder;
/// Factory extensions
mod factory;
/// Meshes
mod mesh;
/// Pipeline states
pub mod pso;
/// Shaders
pub mod shade;
/// Convenience macros
pub mod macros;
<commit_msg>More convenient access to core factory errors<commit_after>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![deny(missing_docs)]

//! An efficient, low-level, bindless graphics API for Rust. See [the
//! blog](http://gfx-rs.github.io/) for explanations and annotated examples.

#[macro_use]
extern crate log;
extern crate draw_state;
extern crate gfx_core;

/// public re-exported traits
pub mod traits {
    pub use gfx_core::{Device, Factory, DeviceFence};
    pub use factory::FactoryExt;
}

// draw state re-exports
pub use draw_state::{preset, state};
pub use draw_state::target::*;

// public re-exports
pub use gfx_core as core;
pub use gfx_core::{Device, SubmitInfo, Resources};
pub use gfx_core::{format, handle, tex};
pub use gfx_core::factory::{Factory, BufferRole, BufferInfo, BufferUsage,
                            BufferUpdateError, CombinedError,
                            ResourceViewError, TargetViewError,
                            SHADER_RESOURCE, UNORDERED_ACCESS, RENDER_TARGET,
                            cast_slice};
pub use gfx_core::{VertexCount, InstanceCount};
pub use gfx_core::Primitive;
pub use gfx_core::{ShaderSet, VertexShader, HullShader, DomainShader,
                   GeometryShader, PixelShader};
pub use gfx_core::draw::{CommandBuffer, InstanceOption};
pub use gfx_core::shade::{ProgramInfo, UniformValue};

pub use encoder::{Encoder, UpdateError};
pub use factory::PipelineStateError;
pub use mesh::{Slice, ToIndexSlice, SliceKind};
pub use pso::{PipelineState};
pub use pso::buffer::{VertexBuffer, InstanceBuffer,
                      ConstantBuffer, Global};
pub use pso::resource::{ShaderResource, UnorderedAccess,
                        Sampler, TextureSampler};
pub use pso::target::{DepthStencilTarget, DepthTarget, StencilTarget,
                      RenderTarget, BlendTarget, BlendRef, Scissor};

/// Render commands encoder
mod encoder;
/// Factory extensions
mod factory;
/// Meshes
mod mesh;
/// Pipeline states
pub mod pso;
/// Shaders
pub mod shade;
/// Convenience macros
pub mod macros;
<|endoftext|>


<commit_before>use std::slice;
use std::io::{self, Read};

use byteorder::{BigEndian, ByteOrder};
use snap;

use error::{Result, Error};

pub fn compress(src: &[u8]) -> Result<Vec<u8>> {
    let mut buf = vec![0; snap::max_compress_len(src.len())];

    snap::Encoder::new()
        .compress(src, &mut buf)
        .map(|len| {
                 buf.truncate(len);
                 buf
             })
        .map_err(|err| Error::InvalidSnappy(err))
}

fn uncompress_to(src: &[u8], dst: &mut Vec<u8>) -> Result<()> {
    snap::decompress_len(src)
        .and_then(|min_len| {
            if min_len > 0 {
                let off = dst.len();
                dst.reserve(off + min_len);
                let buf = unsafe {
                    slice::from_raw_parts_mut(dst.as_mut_slice()[off..].as_mut_ptr(), min_len)
                };
                let uncompressed_len = try!(snap::Decoder::new().decompress(src, buf));
                unsafe { dst.set_len(off + uncompressed_len) }
            }
            Ok(())
        })
        .map_err(|err| Error::InvalidSnappy(err))
}

// --------------------------------------------------------------------

const MAGIC: &'static [u8] = &[0x82, b'S', b'N', b'A', b'P', b'P', b'Y', 0];

// ~ reads a i32 valud and "advances" the given slice by four bytes;
// assumes "slice" is a mutable reference to a &[u8].
macro_rules! next_i32 {
    ($slice:expr) => {{
        if $slice.len() < 4 {
            return Err(Error::UnexpectedEOF);
        }
        { let n = BigEndian::read_i32($slice);
          $slice = &$slice[4..];
          n
        }
    }}
}

/// Validates the expected header at the beginning of the
/// stream. Further, checks the version and compatibility of the
/// stream indicating we can parse the stream. Returns the rest of the
/// stream following the validated header.
fn validate_stream(mut stream: &[u8]) -> Result<&[u8]> {
    // ~ check the "header magic"
    if stream.len() < MAGIC.len() {
        return Err(Error::UnexpectedEOF);
    }
    if &stream[..MAGIC.len()] != MAGIC {
        return Err(Error::InvalidSnappy(snap::Error::Header));
    }
    stream = &stream[MAGIC.len()..];
    // ~ let's be assertive and (for the moment) restrict ourselves to
    // version == 1 and compatibility == 1.
    let version = next_i32!(stream);
    if version != 1 {
        return Err(Error::InvalidSnappy(snap::Error::Header));
    }
    let compat = next_i32!(stream);
    if compat != 1 {
        return Err(Error::InvalidSnappy(snap::Error::Header));
    }
    Ok(stream)
}

#[test]
fn test_validate_stream() {
    let header = [0x82, 0x53, 0x4e, 0x41, 0x50, 0x50, 0x59, 0x00, 0, 0, 0, 1, 0, 0, 0, 1, 0x56];
    // ~ this must not result in a panic
    let rest = validate_stream(&header).unwrap();
    // ~ the rest of the input after the parsed header must be
    // correctly delivered
    assert_eq!(rest, &[0x56]);
}

// ~ An implementation of a reader over a stream of snappy compressed
// chunks as produced by org.xerial.snappy.SnappyOutputStream
// (https://github.com/xerial/snappy-java/ version: 1.1.1.*)
pub struct SnappyReader<'a> {
    // the compressed data itself
    compressed_data: &'a [u8],

    // a pointer into `uncompressed_chunk` indicating the next data
    // byte to serve
    uncompressed_pos: usize,
    // the uncompressed chunk of data available for consumption
    uncompressed_chunk: Vec<u8>,
}

impl<'a> SnappyReader<'a> {
    pub fn new(mut stream: &[u8]) -> Result<SnappyReader> {
        stream = try!(validate_stream(stream));
        Ok(SnappyReader {
               compressed_data: stream,
               uncompressed_pos: 0,
               uncompressed_chunk: Vec::new(),
           })
    }

    fn _read(&mut self, buf: &mut [u8]) -> Result<usize> {
        if self.uncompressed_pos < self.uncompressed_chunk.len() {
            self.read_uncompressed(buf)
        } else if try!(self.next_chunk()) {
            self.read_uncompressed(buf)
        } else {
            Ok(0)
        }
    }

    fn read_uncompressed(&mut self, buf: &mut [u8]) -> Result<usize> {
        let n = try!((&self.uncompressed_chunk[self.uncompressed_pos..]).read(buf));
        self.uncompressed_pos += n;
        Ok(n)
    }

    fn next_chunk(&mut self) -> Result<bool> {
        if self.compressed_data.is_empty() {
            return Ok(false);
        }
        self.uncompressed_pos = 0;
        let chunk_size = next_i32!(self.compressed_data);
        if chunk_size <= 0 {
            return Err(Error::InvalidSnappy(snap::Error::UnsupportedChunkLength {
                                                len: chunk_size as u64,
                                                header: false,
                                            }));
        }
        let chunk_size = chunk_size as usize;
        self.uncompressed_chunk.clear();
        try!(uncompress_to(&self.compressed_data[..chunk_size], &mut self.uncompressed_chunk));
        self.compressed_data = &self.compressed_data[chunk_size..];
        Ok(true)
    }

    fn _read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
        let init_len = buf.len();
        // ~ first consume already uncompressed and unconsumed data - if any
        if self.uncompressed_pos < self.uncompressed_chunk.len() {
            let rest = &self.uncompressed_chunk[self.uncompressed_pos..];
            buf.extend_from_slice(rest);
            self.uncompressed_pos += rest.len();
        }
        // ~ now decompress data directly to the output target
        while !self.compressed_data.is_empty() {
            let chunk_size = next_i32!(self.compressed_data);
            if chunk_size <= 0 {
                return Err(Error::InvalidSnappy(snap::Error::UnsupportedChunkLength {
                                                    len: chunk_size as u64,
                                                    header: false,
                                                }));
            }
            let (c1, c2) = self.compressed_data.split_at(chunk_size as usize);
            try!(uncompress_to(c1, buf));
            self.compressed_data = c2;
        }
        Ok(buf.len() - init_len)
    }
}

macro_rules! to_io_error {
    ($expr:expr) => {
        match $expr {
            Ok(n) => Ok(n),
            // ~ pass io errors through directly
            Err(Error::Io(io_error)) => Err(io_error),
            // ~ wrapp our other errors
            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
        }
    }
}

impl<'a> Read for SnappyReader<'a> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        to_io_error!(self._read(buf))
    }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        to_io_error!(self._read_to_end(buf))
    }
}

// --------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use std::str;
    use std::io::Read;

    use error::{Error, Result};
    use super::{compress, uncompress_to, SnappyReader};

    fn uncompress(src: &[u8]) -> Result<Vec<u8>> {
        let mut v = Vec::new();
        match uncompress_to(src, &mut v) {
            Ok(_) => Ok(v),
            Err(e) => Err(e),
        }
    }

    #[test]
    fn test_compress() {
        let msg = "This is test".as_bytes();
        let compressed = compress(msg).unwrap();
        let expected = &[12, 44, 84, 104, 105, 115, 32, 105, 115, 32, 116, 101, 115, 116];
        assert_eq!(&compressed, expected);
    }

    #[test]
    fn test_uncompress() {
        // The vector should uncompress to "This is test"
        let compressed = &[12, 44, 84, 104, 105, 115, 32, 105, 115, 32, 116, 101, 115, 116];
        let uncompressed = String::from_utf8(uncompress(compressed).unwrap()).unwrap();
        assert_eq!(&uncompressed, "This is test");
    }

    #[test]
    fn test_uncompress_invalid_input() {
        // The vector is an invalid snappy message (second byte modified on purpose)
        let compressed = &[12, 42, 84, 104, 105, 115, 32, 105, 115, 32, 116, 101, 115, 116];
        let uncompressed = uncompress(compressed);
        assert!(uncompressed.is_err());
        assert!(if let Some(Error::InvalidSnappy(_)) = uncompressed.err() {
                    true
                } else {
                    false
                });
    }

    static ORIGINAL: &'static str = include_str!("../../test-data/fetch1.txt");
    static COMPRESSED: &'static [u8] = include_bytes!("../../test-data/fetch1.snappy.chunked.4k");

    #[test]
    fn test_snappy_reader_read() {
        let mut buf = Vec::new();
        let mut r = SnappyReader::new(COMPRESSED).unwrap();

        let mut tmp_buf = [0u8; 1024];
        loop {
            match r.read(&mut tmp_buf).unwrap() {
                0 => break,
                n => buf.extend_from_slice(&tmp_buf[..n]),
            }
        }
        assert_eq!(ORIGINAL, str::from_utf8(&buf[..]).unwrap());
    }

    #[test]
    fn test_snappy_reader_read_to_end() {
        let mut buf = Vec::new();
        let mut r = SnappyReader::new(COMPRESSED).unwrap();
        r.read_to_end(&mut buf).unwrap();
        assert_eq!(ORIGINAL, str::from_utf8(&buf[..]).unwrap());
    }
}
<commit_msg>fix review comments<commit_after>use std::slice;
use std::io::{self, Read};

use byteorder::{BigEndian, ByteOrder};
use snap;

use error::{Result, Error};

pub fn compress(src: &[u8]) -> Result<Vec<u8>> {
    let mut buf = vec![0; snap::max_compress_len(src.len())];

    snap::Encoder::new()
        .compress(src, &mut buf)
        .map(|len| {
                 buf.truncate(len);
                 buf
             })
        .map_err(|err| Error::InvalidSnappy(err))
}

fn uncompress_to(src: &[u8], dst: &mut Vec<u8>) -> Result<()> {
    snap::decompress_len(src)
        .and_then(|min_len| {
            if min_len > 0 {
                let off = dst.len();
                dst.reserve(min_len);
                unsafe {
                    let ptr = dst.as_mut_slice()[off..].as_mut_ptr();
                    let buf = slice::from_raw_parts_mut(ptr, min_len);
                    let uncompressed_len = try!(snap::Decoder::new().decompress(src, buf));
                    dst.set_len(off + uncompressed_len)
                }
            }
            Ok(())
        })
        .map_err(|err| Error::InvalidSnappy(err))
}

// --------------------------------------------------------------------

const MAGIC: &'static [u8] = &[0x82, b'S', b'N', b'A', b'P', b'P', b'Y', 0];

// ~ reads a i32 valud and "advances" the given slice by four bytes;
// assumes "slice" is a mutable reference to a &[u8].
macro_rules! next_i32 {
    ($slice:expr) => {{
        if $slice.len() < 4 {
            return Err(Error::UnexpectedEOF);
        }
        { let n = BigEndian::read_i32($slice);
          $slice = &$slice[4..];
          n
        }
    }}
}

/// Validates the expected header at the beginning of the
/// stream. Further, checks the version and compatibility of the
/// stream indicating we can parse the stream. Returns the rest of the
/// stream following the validated header.
fn validate_stream(mut stream: &[u8]) -> Result<&[u8]> {
    // ~ check the "header magic"
    if stream.len() < MAGIC.len() {
        return Err(Error::UnexpectedEOF);
    }
    if &stream[..MAGIC.len()] != MAGIC {
        return Err(Error::InvalidSnappy(snap::Error::Header));
    }
    stream = &stream[MAGIC.len()..];
    // ~ let's be assertive and (for the moment) restrict ourselves to
    // version == 1 and compatibility == 1.
    let version = next_i32!(stream);
    if version != 1 {
        return Err(Error::InvalidSnappy(snap::Error::Header));
    }
    let compat = next_i32!(stream);
    if compat != 1 {
        return Err(Error::InvalidSnappy(snap::Error::Header));
    }
    Ok(stream)
}

#[test]
fn test_validate_stream() {
    let header = [0x82, 0x53, 0x4e, 0x41, 0x50, 0x50, 0x59, 0x00, 0, 0, 0, 1, 0, 0, 0, 1, 0x56];
    // ~ this must not result in a panic
    let rest = validate_stream(&header).unwrap();
    // ~ the rest of the input after the parsed header must be
    // correctly delivered
    assert_eq!(rest, &[0x56]);
}

// ~ An implementation of a reader over a stream of snappy compressed
// chunks as produced by org.xerial.snappy.SnappyOutputStream
// (https://github.com/xerial/snappy-java/ version: 1.1.1.*)
pub struct SnappyReader<'a> {
    // the compressed data itself
    compressed_data: &'a [u8],

    // a pointer into `uncompressed_chunk` indicating the next data
    // byte to serve
    uncompressed_pos: usize,
    // the uncompressed chunk of data available for consumption
    uncompressed_chunk: Vec<u8>,
}

impl<'a> SnappyReader<'a> {
    pub fn new(mut stream: &[u8]) -> Result<SnappyReader> {
        stream = try!(validate_stream(stream));
        Ok(SnappyReader {
               compressed_data: stream,
               uncompressed_pos: 0,
               uncompressed_chunk: Vec::new(),
           })
    }

    fn _read(&mut self, buf: &mut [u8]) -> Result<usize> {
        if self.uncompressed_pos < self.uncompressed_chunk.len() {
            self.read_uncompressed(buf)
        } else if try!(self.next_chunk()) {
            self.read_uncompressed(buf)
        } else {
            Ok(0)
        }
    }

    fn read_uncompressed(&mut self, buf: &mut [u8]) -> Result<usize> {
        let n = try!((&self.uncompressed_chunk[self.uncompressed_pos..]).read(buf));
        self.uncompressed_pos += n;
        Ok(n)
    }

    fn next_chunk(&mut self) -> Result<bool> {
        if self.compressed_data.is_empty() {
            return Ok(false);
        }
        self.uncompressed_pos = 0;
        let chunk_size = next_i32!(self.compressed_data);
        if chunk_size <= 0 {
            return Err(Error::InvalidSnappy(snap::Error::UnsupportedChunkLength {
                                                len: chunk_size as u64,
                                                header: false,
                                            }));
        }
        let chunk_size = chunk_size as usize;
        self.uncompressed_chunk.clear();
        try!(uncompress_to(&self.compressed_data[..chunk_size], &mut self.uncompressed_chunk));
        self.compressed_data = &self.compressed_data[chunk_size..];
        Ok(true)
    }

    fn _read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
        let init_len = buf.len();
        // ~ first consume already uncompressed and unconsumed data - if any
        if self.uncompressed_pos < self.uncompressed_chunk.len() {
            let rest = &self.uncompressed_chunk[self.uncompressed_pos..];
            buf.extend_from_slice(rest);
            self.uncompressed_pos += rest.len();
        }
        // ~ now decompress data directly to the output target
        while !self.compressed_data.is_empty() {
            let chunk_size = next_i32!(self.compressed_data);
            if chunk_size <= 0 {
                return Err(Error::InvalidSnappy(snap::Error::UnsupportedChunkLength {
                                                    len: chunk_size as u64,
                                                    header: false,
                                                }));
            }
            let (c1, c2) = self.compressed_data.split_at(chunk_size as usize);
            try!(uncompress_to(c1, buf));
            self.compressed_data = c2;
        }
        Ok(buf.len() - init_len)
    }
}

macro_rules! to_io_error {
    ($expr:expr) => {
        match $expr {
            Ok(n) => Ok(n),
            // ~ pass io errors through directly
            Err(Error::Io(io_error)) => Err(io_error),
            // ~ wrapp our other errors
            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
        }
    }
}

impl<'a> Read for SnappyReader<'a> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        to_io_error!(self._read(buf))
    }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        to_io_error!(self._read_to_end(buf))
    }
}

// --------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use std::str;
    use std::io::Read;

    use error::{Error, Result};
    use super::{compress, uncompress_to, SnappyReader};

    fn uncompress(src: &[u8]) -> Result<Vec<u8>> {
        let mut v = Vec::new();
        match uncompress_to(src, &mut v) {
            Ok(_) => Ok(v),
            Err(e) => Err(e),
        }
    }

    #[test]
    fn test_compress() {
        let msg = "This is test".as_bytes();
        let compressed = compress(msg).unwrap();
        let expected = &[12, 44, 84, 104, 105, 115, 32, 105, 115, 32, 116, 101, 115, 116];
        assert_eq!(&compressed, expected);
    }

    #[test]
    fn test_uncompress() {
        // The vector should uncompress to "This is test"
        let compressed = &[12, 44, 84, 104, 105, 115, 32, 105, 115, 32, 116, 101, 115, 116];
        let uncompressed = String::from_utf8(uncompress(compressed).unwrap()).unwrap();
        assert_eq!(&uncompressed, "This is test");
    }

    #[test]
    fn test_uncompress_invalid_input() {
        // The vector is an invalid snappy message (second byte modified on purpose)
        let compressed = &[12, 42, 84, 104, 105, 115, 32, 105, 115, 32, 116, 101, 115, 116];
        let uncompressed = uncompress(compressed);
        assert!(uncompressed.is_err());
        assert!(if let Some(Error::InvalidSnappy(_)) = uncompressed.err() {
                    true
                } else {
                    false
                });
    }

    static ORIGINAL: &'static str = include_str!("../../test-data/fetch1.txt");
    static COMPRESSED: &'static [u8] = include_bytes!("../../test-data/fetch1.snappy.chunked.4k");

    #[test]
    fn test_snappy_reader_read() {
        let mut buf = Vec::new();
        let mut r = SnappyReader::new(COMPRESSED).unwrap();

        let mut tmp_buf = [0u8; 1024];
        loop {
            match r.read(&mut tmp_buf).unwrap() {
                0 => break,
                n => buf.extend_from_slice(&tmp_buf[..n]),
            }
        }
        assert_eq!(ORIGINAL, str::from_utf8(&buf[..]).unwrap());
    }

    #[test]
    fn test_snappy_reader_read_to_end() {
        let mut buf = Vec::new();
        let mut r = SnappyReader::new(COMPRESSED).unwrap();
        r.read_to_end(&mut buf).unwrap();
        assert_eq!(ORIGINAL, str::from_utf8(&buf[..]).unwrap());
    }
}
<|endoftext|>


<commit_before>use compiler::{Symbol, Token};
use std::ascii::AsciiExt;
use std::io;
use std::io::{BufRead};
use util::PeekableBuffer;

fn is_operator(c: char) -> bool {
    match c {
        '~' | '&' | '|' | '*' | '/' | '\\' | '+' | '=' | '>' | '<' | ',' | '@' | '%' => true,
        _ => false,
    }
}

fn is_identifier(c: char) -> bool {
    c.is_ascii() && (c.is_alphanumeric() || c == '_')
}

#[derive(Debug)]
enum Error {
    IoError(io::Error),
    End,
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::IoError(err)
    }
}

pub struct Lexer<R: BufRead> {
    buffer: PeekableBuffer<R>,
    token_queue: Vec<Token>,
}

impl<R: BufRead> Iterator for Lexer<R> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        match self.read_token() {
            Ok(t) => Some(t),
            Err(_) => None
        }
    }
}

impl<R: BufRead> Lexer<R> {
    pub fn new(reader: R) -> Lexer<R> {
        Lexer {
            buffer: PeekableBuffer::new(reader),
            token_queue: Vec::new(),
        }
    }

    fn read_token(&mut self) -> Result<Token, Error> {
        if !self.token_queue.is_empty() {
            return Ok(self.token_queue.pop().unwrap());
        }

        loop {
            self.skip_whitespace();
            self.skip_comments();

            if self.buffer.peek().map_or(false, |c| c.is_whitespace()) {
                continue;
            } else {
                break;
            }
        }

        let c = match self.buffer.peek() {
            Some(c) => c,
            None => return Err(Error::End),
        };

        let token = match c {
            '[' => self.read_symbol(Symbol::NewBlock),
            ']' => self.read_symbol(Symbol::EndBlock),
            '(' => self.read_symbol(Symbol::NewTerm),
            ')' => self.read_symbol(Symbol::EndTerm),
            '#' => self.read_symbol(Symbol::Pound),
            '^' => self.read_symbol(Symbol::Exit),
            '.' => self.read_symbol(Symbol::Period),
            '-' => self.read_minus(),
            ':' => self.read_colon(),
            'a'...'z' | 'A'...'Z' => self.read_identifier(),
            '0'...'9' => self.read_number(),
            '\'' => self.read_string(),
            c if is_operator(c) => self.read_operator(),
            c  => panic!("do not understand: {:?}", c)
        };

        Ok(token)
    }

    fn skip_whitespace(&mut self) {
        loop {
            match self.buffer.peek() {
                Some(c) if c.is_whitespace() => self.buffer.consume(),
                _ => break
            }
        }
    }

    fn skip_comments(&mut self) {
        if self.buffer.peek() != Some('"') {
            return;
        }

        self.buffer.consume();
        loop {
            if self.buffer.next() == Some('"') {
                break;
            }
        }
    }

    fn read_symbol(&mut self, symbol: Symbol) -> Token {
        self.buffer.consume();
        From::from(symbol)
    }

    fn read_operator(&mut self) -> Token {
        let c = self.buffer.next().unwrap();
        match c {
           '~' => From::from(Symbol::Not),
           '&' => From::from(Symbol::And),
           '|' => From::from(Symbol::Or),
           '*' => From::from(Symbol::Star),
           '/' => From::from(Symbol::Divide),
           '\\' => From::from(Symbol::Modulus),
           '+' => From::from(Symbol::Plus),
           '=' => From::from(Symbol::Equal),
           '>' => From::from(Symbol::More),
           '<' => From::from(Symbol::Less),
           ',' => From::from(Symbol::Comma),
           '@' => From::from(Symbol::At),
           '%' => From::from(Symbol::Percent),
           _ => unreachable!(),
       }
    }

    fn read_colon(&mut self) -> Token {
        self.buffer.consume();
        if self.buffer.peek() == Some('=') {
            self.buffer.consume();
            From::from(Symbol::Assign)
        } else {
            From::from(Symbol::Colon)
        }
    }

    fn read_identifier(&mut self) -> Token {
        let mut text = String::new();
        loop {
            match self.buffer.peek() {
                Some(c) if is_identifier(c) => {
                    text.push(c);
                    self.buffer.consume();
                }
                _ => break,
            }
        }

        if self.buffer.peek() == Some(':') {
            self.buffer.consume();
            text.push(':');

            let saw_sequence = self.buffer.peek().and_then(|c| {
                Some(c.is_alphabetic() && c.is_ascii())
            }).unwrap_or(false);
            if saw_sequence {
                loop {
                    match self.buffer.peek() {
                        Some(c @ 'a'...'z') | Some(c @ 'A'...'Z') | Some(c @ '0'...'9') | Some(c @ ':') => {
                            text.push(c);
                            self.buffer.consume();
                        }
                        _ => break,
                    }
                }

                Token(Symbol::KeywordSequence, Some(text))
            } else {
                Token(Symbol::Keyword, Some(text))
            }
        } else if text == "primitive" {
            From::from(Symbol::Primitive)
        } else {
            Token(Symbol::Identifier, Some(text))
        }
    }

    fn read_string(&mut self) -> Token {
        let mut text = String::new();

        self.buffer.consume();
        loop {
            match self.buffer.next() {
                Some('\'') => break,
                Some(c) => text.push(c),
                None => break
            }
        }

        Token(Symbol::String, Some(text))
    }

    fn read_number(&mut self) -> Token {
        let mut text = String::new();

        loop {
            match self.buffer.peek() {
                Some(c @ '0'...'9') => {
                    text.push(c);
                    self.buffer.consume();
                }
                _ => break,
            }
        }

        let saw_decimal = self.buffer.peek().map_or(false, |c| c == '.');
        if saw_decimal {
            self.buffer.consume();
            let saw_digit = self.buffer.peek().map_or(false, |c| c.is_digit(10));
            if saw_digit {
                text.push('.');

                loop {
                    match self.buffer.peek() {
                        Some(c @ '0'...'9') => {
                            text.push(c);
                            self.buffer.consume();
                        }
                        _ => break,
                    }
                }

                Token(Symbol::Double, Some(text))
            } else {
                self.token_queue.push(From::from(Symbol::Period));
                Token(Symbol::Integer, Some(text))
            }
        } else {
            Token(Symbol::Integer, Some(text))
        }
    }

    fn read_minus(&mut self) -> Token {
        self.buffer.consume();
        let mut count = 1;

        loop {
            if self.buffer.peek() == Some('-') {
                self.buffer.consume();
                count += 1;

                if count == 4 {
                    return From::from(Symbol::Separator)
                }
            } else {
                break;
            }
        }

        count -= 1;
        for _ in (0..count) {
            self.token_queue.push(From::from(Symbol::Minus))
        }

        From::from(Symbol::Minus)
    }
}

#[cfg(test)]
mod test {
    use super::Lexer;
    use compiler::{Symbol, Token};

    #[test]
    fn test_skipping_whitespace() {
        let source = "\n Hello \n Test".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Hello".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Test".to_string())));
    }

    #[test]
    fn test_skipping_comments() {
        let source = "\"Test\" Hello \"123\" Test".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Hello".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Test".to_string())));
    }

    #[test]
    fn test_identifier() {
        let source = "Hello".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Hello".to_string())));
    }

    #[test]
    fn test_keyword() {
        let source = "foo:".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Keyword, Some("foo:".to_string())));
    }

    #[test]
    fn test_two_keyword_sequence() {
        let source = "foo:bar:".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::KeywordSequence, Some("foo:bar:".to_string())));
    }

    #[test]
    fn test_three_keyword_sequence() {
        let source = "foo:bar:baz:".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::KeywordSequence, Some("foo:bar:baz:".to_string())));
    }

    #[test]
    fn test_primitive() {
        let source = "primitive".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Primitive, None));
    }

    #[test]
    fn test_minus() {
        let source = "-".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
    }

    #[test]
    fn test_two_minus() {
        let source = "--".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
    }

    #[test]
    fn test_three_minus() {
        let source = "---".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
    }

    #[test]
    fn test_separator() {
        let source = "-----".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Separator, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
    }

    #[test]
    fn test_integer() {
        let source = "1".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Integer, Some("1".to_string())));
    }

    #[test]
    fn test_integer_and_period() {
        let source = "1.".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Integer, Some("1".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Period, None));
    }

    #[test]
    fn test_double() {
        let source = "3.14".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Double, Some("3.14".to_string())));
    }

    #[test]
    fn test_colon() {
        let source = ":".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Colon, None));
    }

    #[test]
    fn test_assignment() {
        let source = "foo := 'Hello'".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("foo".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Assign, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::String, Some("Hello".to_string())));
    }

    #[test]
    fn test_simple_symbols() {
        let source = "[]()#^.".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::NewBlock, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::EndBlock, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::NewTerm, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::EndTerm, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Pound, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Exit, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Period, None));
    }

    #[test]
    fn test_simple_operators() {
        let source = "~&|*/\\+=<>,@%".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Not, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::And, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Or, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Star, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Divide, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Modulus, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Plus, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Equal, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Less, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::More, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Comma, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::At, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Percent, None));
    }

    #[test]
    fn test_lexer() {
        let source = "
        Hello = (
            \"The 'run' method is called when initializing the system\"
            run = ('Hello, World from SOM' println)
        )
        ".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Hello".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Equal, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::NewTerm, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some(("run".to_string()))));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Equal, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::NewTerm, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::String, Some("Hello, World from SOM".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("println".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::EndTerm, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::EndTerm, None));
    }
}
<commit_msg>Use a VecDeque for the token queue<commit_after>use compiler::{Symbol, Token};
use std::ascii::AsciiExt;
use std::collections::VecDeque;
use std::io;
use std::io::{BufRead};
use util::PeekableBuffer;

fn is_operator(c: char) -> bool {
    match c {
        '~' | '&' | '|' | '*' | '/' | '\\' | '+' | '=' | '>' | '<' | ',' | '@' | '%' => true,
        _ => false,
    }
}

fn is_identifier(c: char) -> bool {
    c.is_ascii() && (c.is_alphanumeric() || c == '_')
}

#[derive(Debug)]
enum Error {
    IoError(io::Error),
    End,
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::IoError(err)
    }
}

pub struct Lexer<R: BufRead> {
    buffer: PeekableBuffer<R>,
    token_queue: VecDeque<Token>,
}

impl<R: BufRead> Iterator for Lexer<R> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        match self.read_token() {
            Ok(t) => Some(t),
            Err(_) => None
        }
    }
}

impl<R: BufRead> Lexer<R> {
    pub fn new(reader: R) -> Lexer<R> {
        Lexer {
            buffer: PeekableBuffer::new(reader),
            token_queue: VecDeque::new(),
        }
    }

    fn read_token(&mut self) -> Result<Token, Error> {
        if !self.token_queue.is_empty() {
            return Ok(self.token_queue.pop_front().unwrap());
        }

        loop {
            self.skip_whitespace();
            self.skip_comments();

            if self.buffer.peek().map_or(false, |c| c.is_whitespace()) {
                continue;
            } else {
                break;
            }
        }

        let c = match self.buffer.peek() {
            Some(c) => c,
            None => return Err(Error::End),
        };

        let token = match c {
            '[' => self.read_symbol(Symbol::NewBlock),
            ']' => self.read_symbol(Symbol::EndBlock),
            '(' => self.read_symbol(Symbol::NewTerm),
            ')' => self.read_symbol(Symbol::EndTerm),
            '#' => self.read_symbol(Symbol::Pound),
            '^' => self.read_symbol(Symbol::Exit),
            '.' => self.read_symbol(Symbol::Period),
            '-' => self.read_minus(),
            ':' => self.read_colon(),
            'a'...'z' | 'A'...'Z' => self.read_identifier(),
            '0'...'9' => self.read_number(),
            '\'' => self.read_string(),
            c if is_operator(c) => self.read_operator(),
            c  => panic!("do not understand: {:?}", c)
        };

        Ok(token)
    }

    fn skip_whitespace(&mut self) {
        loop {
            match self.buffer.peek() {
                Some(c) if c.is_whitespace() => self.buffer.consume(),
                _ => break
            }
        }
    }

    fn skip_comments(&mut self) {
        if self.buffer.peek() != Some('"') {
            return;
        }

        self.buffer.consume();
        loop {
            if self.buffer.next() == Some('"') {
                break;
            }
        }
    }

    fn read_symbol(&mut self, symbol: Symbol) -> Token {
        self.buffer.consume();
        From::from(symbol)
    }

    fn read_operator(&mut self) -> Token {
        let c = self.buffer.next().unwrap();
        match c {
           '~' => From::from(Symbol::Not),
           '&' => From::from(Symbol::And),
           '|' => From::from(Symbol::Or),
           '*' => From::from(Symbol::Star),
           '/' => From::from(Symbol::Divide),
           '\\' => From::from(Symbol::Modulus),
           '+' => From::from(Symbol::Plus),
           '=' => From::from(Symbol::Equal),
           '>' => From::from(Symbol::More),
           '<' => From::from(Symbol::Less),
           ',' => From::from(Symbol::Comma),
           '@' => From::from(Symbol::At),
           '%' => From::from(Symbol::Percent),
           _ => unreachable!(),
       }
    }

    fn read_colon(&mut self) -> Token {
        self.buffer.consume();
        if self.buffer.peek() == Some('=') {
            self.buffer.consume();
            From::from(Symbol::Assign)
        } else {
            From::from(Symbol::Colon)
        }
    }

    fn read_identifier(&mut self) -> Token {
        let mut text = String::new();
        loop {
            match self.buffer.peek() {
                Some(c) if is_identifier(c) => {
                    text.push(c);
                    self.buffer.consume();
                }
                _ => break,
            }
        }

        if self.buffer.peek() == Some(':') {
            self.buffer.consume();
            text.push(':');

            let saw_sequence = self.buffer.peek().and_then(|c| {
                Some(c.is_alphabetic() && c.is_ascii())
            }).unwrap_or(false);
            if saw_sequence {
                loop {
                    match self.buffer.peek() {
                        Some(c @ 'a'...'z') | Some(c @ 'A'...'Z') | Some(c @ '0'...'9') | Some(c @ ':') => {
                            text.push(c);
                            self.buffer.consume();
                        }
                        _ => break,
                    }
                }

                Token(Symbol::KeywordSequence, Some(text))
            } else {
                Token(Symbol::Keyword, Some(text))
            }
        } else if text == "primitive" {
            From::from(Symbol::Primitive)
        } else {
            Token(Symbol::Identifier, Some(text))
        }
    }

    fn read_string(&mut self) -> Token {
        let mut text = String::new();

        self.buffer.consume();
        loop {
            match self.buffer.next() {
                Some('\'') => break,
                Some(c) => text.push(c),
                None => break
            }
        }

        Token(Symbol::String, Some(text))
    }

    fn read_number(&mut self) -> Token {
        let mut text = String::new();

        loop {
            match self.buffer.peek() {
                Some(c @ '0'...'9') => {
                    text.push(c);
                    self.buffer.consume();
                }
                _ => break,
            }
        }

        let saw_decimal = self.buffer.peek().map_or(false, |c| c == '.');
        if saw_decimal {
            self.buffer.consume();
            let saw_digit = self.buffer.peek().map_or(false, |c| c.is_digit(10));
            if saw_digit {
                text.push('.');

                loop {
                    match self.buffer.peek() {
                        Some(c @ '0'...'9') => {
                            text.push(c);
                            self.buffer.consume();
                        }
                        _ => break,
                    }
                }

                Token(Symbol::Double, Some(text))
            } else {
                self.token_queue.push_back(From::from(Symbol::Period));
                Token(Symbol::Integer, Some(text))
            }
        } else {
            Token(Symbol::Integer, Some(text))
        }
    }

    fn read_minus(&mut self) -> Token {
        self.buffer.consume();
        let mut count = 1;

        loop {
            if self.buffer.peek() == Some('-') {
                self.buffer.consume();
                count += 1;

                if count == 4 {
                    return From::from(Symbol::Separator)
                }
            } else {
                break;
            }
        }

        count -= 1;
        for _ in (0..count) {
            self.token_queue.push_back(From::from(Symbol::Minus))
        }

        From::from(Symbol::Minus)
    }
}

#[cfg(test)]
mod test {
    use super::Lexer;
    use compiler::{Symbol, Token};

    #[test]
    fn test_skipping_whitespace() {
        let source = "\n Hello \n Test".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Hello".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Test".to_string())));
    }

    #[test]
    fn test_skipping_comments() {
        let source = "\"Test\" Hello \"123\" Test".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Hello".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Test".to_string())));
    }

    #[test]
    fn test_identifier() {
        let source = "Hello".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Hello".to_string())));
    }

    #[test]
    fn test_keyword() {
        let source = "foo:".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Keyword, Some("foo:".to_string())));
    }

    #[test]
    fn test_two_keyword_sequence() {
        let source = "foo:bar:".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::KeywordSequence, Some("foo:bar:".to_string())));
    }

    #[test]
    fn test_three_keyword_sequence() {
        let source = "foo:bar:baz:".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::KeywordSequence, Some("foo:bar:baz:".to_string())));
    }

    #[test]
    fn test_primitive() {
        let source = "primitive".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Primitive, None));
    }

    #[test]
    fn test_minus() {
        let source = "-".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
    }

    #[test]
    fn test_two_minus() {
        let source = "--".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
    }

    #[test]
    fn test_three_minus() {
        let source = "---".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
    }

    #[test]
    fn test_separator() {
        let source = "-----".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Separator, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Minus, None));
    }

    #[test]
    fn test_integer() {
        let source = "1".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Integer, Some("1".to_string())));
    }

    #[test]
    fn test_integer_and_period() {
        let source = "1.".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Integer, Some("1".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Period, None));
    }

    #[test]
    fn test_double() {
        let source = "3.14".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Double, Some("3.14".to_string())));
    }

    #[test]
    fn test_colon() {
        let source = ":".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Colon, None));
    }

    #[test]
    fn test_assignment() {
        let source = "foo := 'Hello'".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("foo".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Assign, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::String, Some("Hello".to_string())));
    }

    #[test]
    fn test_simple_symbols() {
        let source = "[]()#^.".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::NewBlock, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::EndBlock, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::NewTerm, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::EndTerm, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Pound, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Exit, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Period, None));
    }

    #[test]
    fn test_simple_operators() {
        let source = "~&|*/\\+=<>,@%".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Not, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::And, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Or, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Star, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Divide, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Modulus, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Plus, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Equal, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Less, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::More, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Comma, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::At, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Percent, None));
    }

    #[test]
    fn test_lexer() {
        let source = "
        Hello = (
            \"The 'run' method is called when initializing the system\"
            run = ('Hello, World from SOM' println)
        )
        ".as_bytes();
        let mut lexer = Lexer::new(source);
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("Hello".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Equal, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::NewTerm, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some(("run".to_string()))));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Equal, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::NewTerm, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::String, Some("Hello, World from SOM".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::Identifier, Some("println".to_string())));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::EndTerm, None));
        assert_eq!(lexer.read_token().unwrap(), Token(Symbol::EndTerm, None));
    }
}
<|endoftext|>


<commit_before>//! Render combinatory blocks.
//!
//! Render blocks are logical rendering units that have inputs and outputs. Inputs can represent
//! vertex attributes, user-specified values, built-ins or previous blocks’ outputs.

use cheddar::Module;
use glsl::syntax::TranslationUnit;
use std::path::PathBuf;
use warmy::{Load, Loaded, Res, Storage};
use warmy::methods::JSON;

use crate::render::input::Input;
use crate::render::output::Output;
use crate::resource::key::Key;
use crate::resource::error::Error;

/// A render block, allowing for combining blocks in order to create more complex rendering
/// computations.
#[derive(Clone, Debug)]
struct Block {
  inputs: Vec<Input>,
  outputs: Vec<Output>,
  code_path: PathBuf
}

impl Block {
  /// Create a new block out of inputs, outputs and a GLSL module.
  pub fn new<I, O>(inputs: I, outputs: O, code_path: PathBuf) -> Self
  where I: Iterator<Item = Input>,
        O: Iterator<Item = Output> {
    Block {
      inputs: inputs.collect(),
      outputs: outputs.collect(),
      code_path
    }
  }
}

// /// Turn a set of inputs into GLSL declarations. Basically, inputs are either:
// ///
// ///   - Functional inputs – i.e. arguments to functions. This is typically the case for vertex
// ///     shaders.
// ///   - Constant parameters – i.e. values that won’t change for a render command.
<commit_msg>Pre refactor.<commit_after>//! Render combinatory blocks.
//!
//! Render blocks are logical rendering units that have inputs and outputs. Inputs can represent
//! vertex attributes, user-specified values, built-ins or previous blocks’ outputs.

use cheddar::Module;
use glsl::syntax::ExternalDeclaration;
use std::path::PathBuf;
use warmy::{Load, Loaded, Res, Storage};

use crate::render::input::{Input, Role};
use crate::render::output::Output;
use crate::resource::key::Key;
use crate::resource::error::Error;

/// A render block, allowing for combining blocks in order to create more complex rendering
/// computations.
#[derive(Clone, Debug)]
struct Block {
  inputs: Vec<Input>,
  outputs: Vec<Output>,
  code_path: PathBuf
}

impl Block {
  /// Create a new block out of inputs, outputs and a GLSL module.
  pub fn new<I, O>(inputs: I, outputs: O, code_path: PathBuf) -> Self
  where I: Iterator<Item = Input>,
        O: Iterator<Item = Output> {
    Block {
      inputs: inputs.collect(),
      outputs: outputs.collect(),
      code_path
    }
  }
}
<|endoftext|>