<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::with_capacity(num_points);
                        reader.read_exact(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::U8(attr));
                    }
                    AttributeDataType::I64 => {
                        let mut attr = Vec::with_capacity(num_points);
                        reader.read_i64_into::<LittleEndian>(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::I64(attr));
                    }
                    AttributeDataType::U64 => {
                        let mut attr = Vec::with_capacity(num_points);
                        reader.read_u64_into::<LittleEndian>(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::U64(attr));
                    }
                    AttributeDataType::F32 => {
                        let mut attr = Vec::with_capacity(num_points);
                        reader.read_f32_into::<LittleEndian>(&mut attr)?;
                        batch
                            .attributes
                            .insert(key.to_owned(), AttributeData::F32(attr));
                    }
                    AttributeDataType::F64 => {
                        let mut attr = Vec::with_capacity(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::with_capacity(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::with_capacity(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>Properly initialize buffer. (#328)<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[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)
    }
}
<|endoftext|>


<commit_before>//! Tests for the registry

use std::sync::{Arc, Mutex, Condvar};

use rustc_serialize::json::{Json, ToJson};

use registry;
use registry::AccessFlags;

lazy_static! {
    static ref ACCESS_PAIR: Arc<(Mutex<bool>, Condvar)>
        = Arc::new((Mutex::new(false), Condvar::new()));
}

// Constants for use with registry-accessing tests

pub const BOOL: bool = true;
pub const U64: u64 = 42;
pub const I64: i64 = -1;
pub const F64: f64 = 21.5;

pub const TEXT: &'static str = "Hello way-cooler";
pub const POINT: Point = Point { x: 12, y: 12 };

pub const READONLY: &'static str = "read only text";
pub const WRITEONLY: &'static str = "write only text";
pub const NO_PERMS: &'static str = "<look ma no perms>";

pub const PROP_GET_RESULT: &'static str = "get property";

pub fn get_prop() -> Json {
    PROP_GET_RESULT.to_json()
}

pub fn get_panic_prop() -> Json {
    panic!("get_panic_prop panic")
}

pub fn set_prop(_json: Json) {
    println!("set_prop being called!");
}

pub fn set_panic_prop(_json: Json) {
    panic!("set_panic_prop panic")
}

/// [0, 1, 2]
pub fn u64s() -> Vec<Json> {
    vec![Json::U64(0), Json::U64(1), Json::U64(2)]
}

/// Wait for the registry to initialize
pub fn wait_for_registry() {
    // First star for lazy static, second for Arc
    let &(ref lock, ref cond) = &**ACCESS_PAIR;
    let mut started = lock.lock().expect("Unable to lock ACCESS_PAIR lock!");
    while !*started {
        started = cond.wait(started)
            .expect("Oh boy, I can't wait for the registry to start!");
    }
}

json_convertible! {
    /// Has an x and y field
    #[derive(Debug, Clone, Eq, PartialEq)]
    struct Point {
        x: i32,
        y: i32
    }
}

unsafe impl Sync for Point {}

#[test]
fn add_keys() {
    let values = vec![
        ("bool",  AccessFlags::all(), BOOL.to_json()),
        ("u64",   AccessFlags::all(), U64.to_json()),
        ("i64",   AccessFlags::all(), I64.to_json()),
        ("f64",   AccessFlags::all(), F64.to_json()),
        ("text",  AccessFlags::all(), TEXT.to_json()),
        ("point", AccessFlags::all(), POINT.to_json()),
        ("null",  AccessFlags::all(), Json::Null),
        ("u64s",  AccessFlags::all(), Json::Array(u64s())),

        ("readonly",  AccessFlags::READ(),  READONLY.to_json()),
        ("writeonly", AccessFlags::WRITE(), WRITEONLY.to_json()),
        ("noperms",   AccessFlags::empty(), NO_PERMS.to_json())
    ];

    for (name, flags, json) in values.into_iter() {
        assert!(registry::insert_json(name.to_string(), flags, json).is_none(),
            "Unable to initialize objects in registry");
    }

    // Read/write property
    assert!(registry::insert_property("prop".to_string(),
                              Some(Arc::new(get_prop)),
                              Some(Arc::new(set_prop)))
        .is_none(), "Unable to initialize property in registry");

    // Readonly/writeonly properties
    assert!(registry::insert_property("get_prop".to_string(),
                              Some(Arc::new(get_prop)),
                              None)
        .is_none(), "Unable to initialize property in registry");
    assert!(registry::insert_property("set_prop".to_string(),
                              None,
                              Some(Arc::new(set_prop)))
        .is_none(), "Unable to initialize property in registry");

    // Readonly/writeonly panicking properties
    assert!(registry::insert_property("get_panic_prop".to_string(),
                              Some(Arc::new(get_panic_prop)),
                              None)
        .is_none(), "Unable to initialize property in registry");
    assert!(registry::insert_property("set_panic_prop".to_string(),
                              None,
                              Some(Arc::new(set_panic_prop)))
        .is_none(), "Unable to initialize property in registry");

    // Allow waiting threads to continue
    let &(ref lock, ref cond) = &**ACCESS_PAIR;
    let mut started = lock.lock().expect("Couldn't unlock threads");
    *started = true;
    cond.notify_all();
}

#[test]
fn contains_keys() {
    wait_for_registry();

    let keys = [
        "bool", "u64", "i64", "f64", "null", "text", "point",
        "u64s", "readonly", "writeonly", "prop", "noperms",
        "get_prop", "set_prop", "get_panic_prop", "set_panic_prop",
    ];
    for key in keys.into_iter() {
        assert!(registry::contains_key(key),
                "Could not find key {}", key);
    }
}

#[test]
fn objects_and_keys_equal() {
    wait_for_registry();

    let values = vec![
        ("bool",  AccessFlags::all(), BOOL.to_json()),
        ("u64",   AccessFlags::all(), U64.to_json()),
        ("i64",   AccessFlags::all(), I64.to_json()),
        ("f64",   AccessFlags::all(), F64.to_json()),
        ("text",  AccessFlags::all(), TEXT.to_json()),
        ("point", AccessFlags::all(), POINT.to_json()),
        ("null",  AccessFlags::all(), Json::Null),
        ("u64s",  AccessFlags::all(), Json::Array(u64s())),

        ("readonly",  AccessFlags::READ(),  READONLY.to_json()),
        ("writeonly", AccessFlags::WRITE(), WRITEONLY.to_json()),
        ("noperms",   AccessFlags::empty(), NO_PERMS.to_json())
    ];

    for (name, flags, json) in values.into_iter() {
        let (found_flags, found) = registry::get_data(name)
            .expect(&format!("Unable to get key {}", name))
            .resolve();
        assert_eq!(found_flags, flags);
        assert_eq!(*found, json);
    }
}

#[test]
fn key_perms() {
    wait_for_registry();

    let perms = vec![
        ("bool",      AccessFlags::all()),
        ("readonly",  AccessFlags::READ()),
        ("writeonly", AccessFlags::WRITE()),
        ("prop",      AccessFlags::all()),
        //("prop_read", AccessFlags::READ()),
        //("prop_write", AccessFlags::WRITE())
    ];

    for (name, flags) in perms.into_iter() {
        let found_data = registry::get_data(name)
            .expect(&format!("Could not get data for {}", name));

        let (found_flags, _) = found_data.resolve();
        println!("Testing flags for {}", name);
        assert_eq!(found_flags, flags);
    }
}

#[test]
fn property_get() {
    wait_for_registry();
    let prop_read = registry::get_data("get_prop")
        .expect("Couldn't get prop_read");

    assert_eq!(*prop_read.resolve().1, PROP_GET_RESULT.to_json());
}

#[test]
#[should_panic(expected="get_panic_prop panic")]
fn panicking_property_get() {
    wait_for_registry();
    let prop_read = registry::get_data("get_panic_prop")
        .expect("Couldn't get prop_read");

    assert_eq!(*prop_read.resolve().1, PROP_GET_RESULT.to_json());
}

#[test]
fn property_set() {
    wait_for_registry();
    registry::set_json("set_prop".to_string(), Json::Null)
            .expect("Unable to set data").call(Json::Null);
}

#[test]
#[should_panic(expected="set_panic_prop panic")]
fn panicking_property_set() {
    wait_for_registry();
    registry::set_json("set_panic_prop".to_string(), Json::Null)
            .expect("Unable to set data").call(Json::Null);
}
<commit_msg>Test for get_prop flags<commit_after>//! Tests for the registry

use std::sync::{Arc, Mutex, Condvar};

use rustc_serialize::json::{Json, ToJson};

use registry;
use registry::AccessFlags;

lazy_static! {
    static ref ACCESS_PAIR: Arc<(Mutex<bool>, Condvar)>
        = Arc::new((Mutex::new(false), Condvar::new()));
}

// Constants for use with registry-accessing tests

pub const BOOL: bool = true;
pub const U64: u64 = 42;
pub const I64: i64 = -1;
pub const F64: f64 = 21.5;

pub const TEXT: &'static str = "Hello way-cooler";
pub const POINT: Point = Point { x: 12, y: 12 };

pub const READONLY: &'static str = "read only text";
pub const WRITEONLY: &'static str = "write only text";
pub const NO_PERMS: &'static str = "<look ma no perms>";

pub const PROP_GET_RESULT: &'static str = "get property";

pub fn get_prop() -> Json {
    PROP_GET_RESULT.to_json()
}

pub fn get_panic_prop() -> Json {
    panic!("get_panic_prop panic")
}

pub fn set_prop(_json: Json) {
    println!("set_prop being called!");
}

pub fn set_panic_prop(_json: Json) {
    panic!("set_panic_prop panic")
}

/// [0, 1, 2]
pub fn u64s() -> Vec<Json> {
    vec![Json::U64(0), Json::U64(1), Json::U64(2)]
}

/// Wait for the registry to initialize
pub fn wait_for_registry() {
    // First star for lazy static, second for Arc
    let &(ref lock, ref cond) = &**ACCESS_PAIR;
    let mut started = lock.lock().expect("Unable to lock ACCESS_PAIR lock!");
    while !*started {
        started = cond.wait(started)
            .expect("Oh boy, I can't wait for the registry to start!");
    }
}

json_convertible! {
    /// Has an x and y field
    #[derive(Debug, Clone, Eq, PartialEq)]
    struct Point {
        x: i32,
        y: i32
    }
}

unsafe impl Sync for Point {}

#[test]
fn add_keys() {
    let values = vec![
        ("bool",  AccessFlags::all(), BOOL.to_json()),
        ("u64",   AccessFlags::all(), U64.to_json()),
        ("i64",   AccessFlags::all(), I64.to_json()),
        ("f64",   AccessFlags::all(), F64.to_json()),
        ("text",  AccessFlags::all(), TEXT.to_json()),
        ("point", AccessFlags::all(), POINT.to_json()),
        ("null",  AccessFlags::all(), Json::Null),
        ("u64s",  AccessFlags::all(), Json::Array(u64s())),

        ("readonly",  AccessFlags::READ(),  READONLY.to_json()),
        ("writeonly", AccessFlags::WRITE(), WRITEONLY.to_json()),
        ("noperms",   AccessFlags::empty(), NO_PERMS.to_json())
    ];

    for (name, flags, json) in values.into_iter() {
        assert!(registry::insert_json(name.to_string(), flags, json).is_none(),
            "Unable to initialize objects in registry");
    }

    // Read/write property
    assert!(registry::insert_property("prop".to_string(),
                              Some(Arc::new(get_prop)),
                              Some(Arc::new(set_prop)))
        .is_none(), "Unable to initialize property in registry");

    // Readonly/writeonly properties
    assert!(registry::insert_property("get_prop".to_string(),
                              Some(Arc::new(get_prop)),
                              None)
        .is_none(), "Unable to initialize property in registry");
    assert!(registry::insert_property("set_prop".to_string(),
                              None,
                              Some(Arc::new(set_prop)))
        .is_none(), "Unable to initialize property in registry");

    // Readonly/writeonly panicking properties
    assert!(registry::insert_property("get_panic_prop".to_string(),
                              Some(Arc::new(get_panic_prop)),
                              None)
        .is_none(), "Unable to initialize property in registry");
    assert!(registry::insert_property("set_panic_prop".to_string(),
                              None,
                              Some(Arc::new(set_panic_prop)))
        .is_none(), "Unable to initialize property in registry");

    // Allow waiting threads to continue
    let &(ref lock, ref cond) = &**ACCESS_PAIR;
    let mut started = lock.lock().expect("Couldn't unlock threads");
    *started = true;
    cond.notify_all();
}

#[test]
fn contains_keys() {
    wait_for_registry();

    let keys = [
        "bool", "u64", "i64", "f64", "null", "text", "point",
        "u64s", "readonly", "writeonly", "prop", "noperms",
        "get_prop", "set_prop", "get_panic_prop", "set_panic_prop",
    ];
    for key in keys.into_iter() {
        assert!(registry::contains_key(key),
                "Could not find key {}", key);
    }
}

#[test]
fn objects_and_keys_equal() {
    wait_for_registry();

    let values = vec![
        ("bool",  AccessFlags::all(), BOOL.to_json()),
        ("u64",   AccessFlags::all(), U64.to_json()),
        ("i64",   AccessFlags::all(), I64.to_json()),
        ("f64",   AccessFlags::all(), F64.to_json()),
        ("text",  AccessFlags::all(), TEXT.to_json()),
        ("point", AccessFlags::all(), POINT.to_json()),
        ("null",  AccessFlags::all(), Json::Null),
        ("u64s",  AccessFlags::all(), Json::Array(u64s())),

        ("readonly",  AccessFlags::READ(),  READONLY.to_json()),
        ("writeonly", AccessFlags::WRITE(), WRITEONLY.to_json()),
        ("noperms",   AccessFlags::empty(), NO_PERMS.to_json())
    ];

    for (name, flags, json) in values.into_iter() {
        let (found_flags, found) = registry::get_data(name)
            .expect(&format!("Unable to get key {}", name))
            .resolve();
        assert_eq!(found_flags, flags);
        assert_eq!(*found, json);
    }
}

#[test]
fn key_perms() {
    wait_for_registry();

    let perms = vec![
        ("bool",      AccessFlags::all()),
        ("readonly",  AccessFlags::READ()),
        ("writeonly", AccessFlags::WRITE()),
        ("prop",      AccessFlags::all()),
        ("get_prop", AccessFlags::READ()),
        //("set_prop", AccessFlags::WRITE())
    ];

    for (name, flags) in perms.into_iter() {
        let found_data = registry::get_data(name)
            .expect(&format!("Could not get data for {}", name));

        let (found_flags, _) = found_data.resolve();
        println!("Testing flags for {}", name);
        assert_eq!(found_flags, flags);
    }
}

#[test]
fn property_get() {
    wait_for_registry();
    let prop_read = registry::get_data("get_prop")
        .expect("Couldn't get prop_read");

    assert_eq!(*prop_read.resolve().1, PROP_GET_RESULT.to_json());
}

#[test]
#[should_panic(expected="get_panic_prop panic")]
fn panicking_property_get() {
    wait_for_registry();
    let prop_read = registry::get_data("get_panic_prop")
        .expect("Couldn't get prop_read");

    assert_eq!(*prop_read.resolve().1, PROP_GET_RESULT.to_json());
}

#[test]
fn property_set() {
    wait_for_registry();
    registry::set_json("set_prop".to_string(), Json::Null)
            .expect("Unable to set data").call(Json::Null);
}

#[test]
#[should_panic(expected="set_panic_prop panic")]
fn panicking_property_set() {
    wait_for_registry();
    registry::set_json("set_panic_prop".to_string(), Json::Null)
            .expect("Unable to set data").call(Json::Null);
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// A very basic BlobImageRenderer that can only render a checkerboard pattern.

use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
use webrender::api::*;

// Serialize/deserialize the blob.

pub fn serialize_blob(color: ColorU) -> Vec<u8> {
    vec![color.r, color.g, color.b, color.a]
}

fn deserialize_blob(blob: &[u8]) -> Result<ColorU, ()> {
    let mut iter = blob.iter();
    return match (iter.next(), iter.next(), iter.next(), iter.next()) {
        (Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(ColorU::new(r, g, b, a)),
        (Some(&a), None, None, None) => Ok(ColorU::new(a, a, a, a)),
        _ => Err(()),
    };
}

// This is the function that applies the deserialized drawing commands and generates
// actual image data.
fn render_blob(
    color: ColorU,
    descriptor: &BlobImageDescriptor,
    tile: Option<TileOffset>,
) -> BlobImageResult {
    // Allocate storage for the result. Right now the resource cache expects the
    // tiles to have have no stride or offset.
    let mut texels = Vec::with_capacity((descriptor.width * descriptor.height * 4) as usize);

    // Generate a per-tile pattern to see it in the demo. For a real use case it would not
    // make sense for the rendered content to depend on its tile.
    let tile_checker = match tile {
        Some(tile) => (tile.x % 2 == 0) != (tile.y % 2 == 0),
        None => true,
    };

    for y in 0 .. descriptor.height {
        for x in 0 .. descriptor.width {
            // Apply the tile's offset. This is important: all drawing commands should be
            // translated by this offset to give correct results with tiled blob images.
            let x2 = x + descriptor.offset.x as u32;
            let y2 = y + descriptor.offset.y as u32;

            // Render a simple checkerboard pattern
            let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) {
                1
            } else {
                0
            };
            // ..nested in the per-tile checkerboard pattern
            let tc = if tile_checker { 0 } else { (1 - checker) * 40 };

            match descriptor.format {
                ImageFormat::BGRA8 => {
                    texels.push(color.b * checker + tc);
                    texels.push(color.g * checker + tc);
                    texels.push(color.r * checker + tc);
                    texels.push(color.a * checker + tc);
                }
                ImageFormat::R8 => {
                    texels.push(color.a * checker + tc);
                }
                _ => {
                    return Err(BlobImageError::Other(
                        format!("Unsupported image format {:?}", descriptor.format),
                    ));
                }
            }
        }
    }

    Ok(RasterizedBlobImage {
        data: texels,
        width: descriptor.width,
        height: descriptor.height,
    })
}

pub struct BlobCallbacks {
    pub request: Box<Fn(&BlobImageRequest) + Send + 'static>,
    pub resolve: Box<Fn() + Send + 'static>,
}

impl BlobCallbacks {
    pub fn new() -> Self {
        BlobCallbacks { request: Box::new(|_|()), resolve: Box::new(|| (())) }
    }
}

pub struct CheckerboardRenderer {
    image_cmds: HashMap<ImageKey, ColorU>,
    callbacks: Arc<Mutex<BlobCallbacks>>,

    // The images rendered in the current frame (not kept here between frames).
    rendered_images: HashMap<BlobImageRequest, BlobImageResult>,
}

impl CheckerboardRenderer {
    pub fn new(callbacks: Arc<Mutex<BlobCallbacks>>) -> Self {
        CheckerboardRenderer {
            callbacks,
            image_cmds: HashMap::new(),
            rendered_images: HashMap::new(),
        }
    }
}

impl BlobImageRenderer for CheckerboardRenderer {
    fn add(&mut self, key: ImageKey, cmds: BlobImageData, _: Option<TileSize>) {
        self.image_cmds
            .insert(key, deserialize_blob(&cmds[..]).unwrap());
    }

    fn update(&mut self, key: ImageKey, cmds: BlobImageData, _dirty_rect: Option<DeviceUintRect>) {
        // Here, updating is just replacing the current version of the commands with
        // the new one (no incremental updates).
        self.image_cmds
            .insert(key, deserialize_blob(&cmds[..]).unwrap());
    }

    fn delete(&mut self, key: ImageKey) {
        self.image_cmds.remove(&key);
    }

    fn request(
        &mut self,
        _resources: &BlobImageResources,
        request: BlobImageRequest,
        descriptor: &BlobImageDescriptor,
        _dirty_rect: Option<DeviceUintRect>,
    ) {
        (self.callbacks.lock().unwrap().request)(&request);
        assert!(!self.rendered_images.contains_key(&request));
        // This method is where we kick off our rendering jobs.
        // It should avoid doing work on the calling thread as much as possible.
        // In this example we will use the thread pool to render individual tiles.

        // Gather the input data to send to a worker thread.
        let cmds = self.image_cmds.get(&request.key).unwrap();

        let result = render_blob(*cmds, descriptor, request.tile);

        self.rendered_images.insert(request, result);
    }

    fn resolve(&mut self, request: BlobImageRequest) -> BlobImageResult {
        (self.callbacks.lock().unwrap().resolve)();
        self.rendered_images.remove(&request).unwrap()
    }

    fn delete_font(&mut self, _key: FontKey) {}

    fn delete_font_instance(&mut self, _key: FontInstanceKey) {}
}
<commit_msg>wrench/blob: Only paint the dirty rect<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// A very basic BlobImageRenderer that can only render a checkerboard pattern.

use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
use webrender::api::*;

// Serialize/deserialize the blob.

pub fn serialize_blob(color: ColorU) -> Vec<u8> {
    vec![color.r, color.g, color.b, color.a]
}

fn deserialize_blob(blob: &[u8]) -> Result<ColorU, ()> {
    let mut iter = blob.iter();
    return match (iter.next(), iter.next(), iter.next(), iter.next()) {
        (Some(&r), Some(&g), Some(&b), Some(&a)) => Ok(ColorU::new(r, g, b, a)),
        (Some(&a), None, None, None) => Ok(ColorU::new(a, a, a, a)),
        _ => Err(()),
    };
}

// This is the function that applies the deserialized drawing commands and generates
// actual image data.
fn render_blob(
    color: ColorU,
    descriptor: &BlobImageDescriptor,
    tile: Option<TileOffset>,
    dirty_rect: Option<DeviceUintRect>,
) -> BlobImageResult {
    // Allocate storage for the result. Right now the resource cache expects the
    // tiles to have have no stride or offset.
    let mut texels = vec![0u8; (descriptor.width * descriptor.height * descriptor.format.bytes_per_pixel()) as usize];

    // Generate a per-tile pattern to see it in the demo. For a real use case it would not
    // make sense for the rendered content to depend on its tile.
    let tile_checker = match tile {
        Some(tile) => (tile.x % 2 == 0) != (tile.y % 2 == 0),
        None => true,
    };

    let dirty_rect = dirty_rect.unwrap_or(DeviceUintRect::new(
        DeviceUintPoint::new(0, 0),
        DeviceUintSize::new(descriptor.width, descriptor.height)));

    for y in dirty_rect.min_y() .. dirty_rect.max_y() {
        for x in dirty_rect.min_x() .. dirty_rect.max_x() {
            // Apply the tile's offset. This is important: all drawing commands should be
            // translated by this offset to give correct results with tiled blob images.
            let x2 = x + descriptor.offset.x as u32;
            let y2 = y + descriptor.offset.y as u32;

            // Render a simple checkerboard pattern
            let checker = if (x2 % 20 >= 10) != (y2 % 20 >= 10) {
                1
            } else {
                0
            };
            // ..nested in the per-tile checkerboard pattern
            let tc = if tile_checker { 0 } else { (1 - checker) * 40 };

            match descriptor.format {
                ImageFormat::BGRA8 => {
                    texels[((y * descriptor.width + x) * 4 + 0) as usize] = color.b * checker + tc;
                    texels[((y * descriptor.width + x) * 4 + 1) as usize] = color.g * checker + tc;
                    texels[((y * descriptor.width + x) * 4 + 2) as usize] = color.r * checker + tc;
                    texels[((y * descriptor.width + x) * 4 + 3) as usize] = color.a * checker + tc;
                }
                ImageFormat::R8 => {
                    texels[(y * descriptor.width + x) as usize] = color.a * checker + tc;
                }
                _ => {
                    return Err(BlobImageError::Other(
                        format!("Unsupported image format {:?}", descriptor.format),
                    ));
                }
            }
        }
    }

    Ok(RasterizedBlobImage {
        data: texels,
        width: descriptor.width,
        height: descriptor.height,
    })
}

pub struct BlobCallbacks {
    pub request: Box<Fn(&BlobImageRequest) + Send + 'static>,
    pub resolve: Box<Fn() + Send + 'static>,
}

impl BlobCallbacks {
    pub fn new() -> Self {
        BlobCallbacks { request: Box::new(|_|()), resolve: Box::new(|| (())) }
    }
}

pub struct CheckerboardRenderer {
    image_cmds: HashMap<ImageKey, ColorU>,
    callbacks: Arc<Mutex<BlobCallbacks>>,

    // The images rendered in the current frame (not kept here between frames).
    rendered_images: HashMap<BlobImageRequest, BlobImageResult>,
}

impl CheckerboardRenderer {
    pub fn new(callbacks: Arc<Mutex<BlobCallbacks>>) -> Self {
        CheckerboardRenderer {
            callbacks,
            image_cmds: HashMap::new(),
            rendered_images: HashMap::new(),
        }
    }
}

impl BlobImageRenderer for CheckerboardRenderer {
    fn add(&mut self, key: ImageKey, cmds: BlobImageData, _: Option<TileSize>) {
        self.image_cmds
            .insert(key, deserialize_blob(&cmds[..]).unwrap());
    }

    fn update(&mut self, key: ImageKey, cmds: BlobImageData, _dirty_rect: Option<DeviceUintRect>) {
        // Here, updating is just replacing the current version of the commands with
        // the new one (no incremental updates).
        self.image_cmds
            .insert(key, deserialize_blob(&cmds[..]).unwrap());
    }

    fn delete(&mut self, key: ImageKey) {
        self.image_cmds.remove(&key);
    }

    fn request(
        &mut self,
        _resources: &BlobImageResources,
        request: BlobImageRequest,
        descriptor: &BlobImageDescriptor,
        dirty_rect: Option<DeviceUintRect>,
    ) {
        (self.callbacks.lock().unwrap().request)(&request);
        assert!(!self.rendered_images.contains_key(&request));
        // This method is where we kick off our rendering jobs.
        // It should avoid doing work on the calling thread as much as possible.
        // In this example we will use the thread pool to render individual tiles.

        // Gather the input data to send to a worker thread.
        let cmds = self.image_cmds.get(&request.key).unwrap();

        let result = render_blob(*cmds, descriptor, request.tile, dirty_rect);

        self.rendered_images.insert(request, result);
    }

    fn resolve(&mut self, request: BlobImageRequest) -> BlobImageResult {
        (self.callbacks.lock().unwrap().resolve)();
        self.rendered_images.remove(&request).unwrap()
    }

    fn delete_font(&mut self, _key: FontKey) {}

    fn delete_font_instance(&mut self, _key: FontInstanceKey) {}
}
<|endoftext|>


<commit_before>//! Create cycles in a timely dataflow graph.

use std::rc::Rc;
use std::cell::RefCell;

use {Data, Push};

use progress::{Timestamp, Operate, PathSummary};
use progress::frontier::Antichain;
use progress::nested::{Source, Target};
use progress::ChangeBatch;

use progress::nested::product::Product;
use progress::nested::Summary::Local;

use dataflow::channels::Content;
use dataflow::channels::pushers::{Counter, Tee};

use dataflow::{Stream, Scope, ScopeParent};
use dataflow::scopes::Child;

/// Creates a `Stream` and a `Handle` to later bind the source of that `Stream`.
pub trait LoopVariable<'a, G: ScopeParent, T: Timestamp> {
    /// Creates a `Stream` and a `Handle` to later bind the source of that `Stream`.
    ///
    /// The resulting `Stream` will have its data defined by a future call to `connect_loop` with
    /// its `Handle` passed as an argument. Data will be passed through the stream will have their
    /// timestampsas advanced by `summary`, and will be dropped if the result exceeds `limit`.
    ///
    /// #Examples
    /// ```
    /// use timely::dataflow::operators::{LoopVariable, ConnectLoop, ToStream, Concat, Inspect};
    ///
    /// timely::example(|scope| {
    ///     // circulate 0..10 for 100 iterations.
    ///     let (handle, cycle) = scope.loop_variable(100, 1);
    ///     (0..10).to_stream(scope)
    ///            .concat(&cycle)
    ///            .inspect(|x| println!("seen: {:?}", x))
    ///            .connect_loop(handle);
    /// });
    /// ```
    fn loop_variable<D: Data>(&mut self, limit: T, summary: T::Summary) -> (Handle<G::Timestamp, T, D>, Stream<Child<'a, G, T>, D>);
}

impl<'a, G: ScopeParent, T: Timestamp> LoopVariable<'a, G, T> for Child<'a, G, T> {
    fn loop_variable<D: Data>(&mut self, limit: T, summary: T::Summary) -> (Handle<G::Timestamp, T, D>, Stream<Child<'a, G, T>, D>) {

        let (targets, registrar) = Tee::<Product<G::Timestamp, T>, D>::new();

        let feedback_output = Counter::new(targets);
        let produced = feedback_output.produced().clone();

        let feedback_input =  Counter::new(Observer {
            limit: limit, summary: summary.clone(), targets: feedback_output
        });
        let consumed = feedback_input.produced().clone();

        let index = self.add_operator(Operator {
            consumed_messages:  consumed,
            produced_messages:  produced,
            summary:            Local(summary),
        });

        let helper = Handle {
            index:  index,
            target: feedback_input,
        };

        (helper, Stream::new(Source { index: index, port: 0 }, registrar, self.clone()))
    }
}

// implementation of the feedback vertex, essentially, as an observer
struct Observer<TOuter: Timestamp, TInner: Timestamp, D:Data> {
    limit:      TInner,
    summary:    TInner::Summary,
    targets:    Counter<Product<TOuter, TInner>, D, Tee<Product<TOuter, TInner>, D>>,
}

impl<TOuter: Timestamp, TInner: Timestamp, D: Data> Push<(Product<TOuter, TInner>, Content<D>)> for Observer<TOuter, TInner, D> {
    #[inline]
    fn push(&mut self, message: &mut Option<(Product<TOuter, TInner>, Content<D>)>) {
        let active = if let Some((ref mut time, _)) = *message {
            if let Some(new_time) = self.summary.results_in(&time.inner) {
                time.inner = new_time;
                time.inner.less_equal(&self.limit)
            }
            else {
                false
            }
        }
        else { true };

        if active { self.targets.push(message); }
    }
}

/// Connect a `Stream` to the input of a loop variable.
pub trait ConnectLoop<G: ScopeParent, T: Timestamp, D: Data> {
    /// Connect a `Stream` to be the input of a loop variable.
    ///
    /// #Examples
    /// ```
    /// use timely::dataflow::operators::{LoopVariable, ConnectLoop, ToStream, Concat, Inspect};
    ///
    /// timely::example(|scope| {
    ///     // circulate 0..10 for 100 iterations.
    ///     let (handle, cycle) = scope.loop_variable(100, 1);
    ///     (0..10).to_stream(scope)
    ///            .concat(&cycle)
    ///            .inspect(|x| println!("seen: {:?}", x))
    ///            .connect_loop(handle);
    /// });
    /// ```
    fn connect_loop(&self, Handle<G::Timestamp, T, D>);
}

impl<'a, G: ScopeParent, T: Timestamp, D: Data> ConnectLoop<G, T, D> for Stream<Child<'a, G, T>, D> {
    fn connect_loop(&self, helper: Handle<G::Timestamp, T, D>) {
        let channel_id = self.scope().new_identifier();
        self.connect_to(Target { index: helper.index, port: 0 }, helper.target, channel_id);
    }
}

/// A handle used to bind the source of a loop variable.
pub struct Handle<TOuter: Timestamp, TInner: Timestamp, D: Data> {
    index:  usize,
    target: Counter<Product<TOuter, TInner>, D, Observer<TOuter, TInner, D>>
}

// the scope that the progress tracker interacts with
struct Operator<T:Timestamp> {
    consumed_messages:  Rc<RefCell<ChangeBatch<T>>>,
    produced_messages:  Rc<RefCell<ChangeBatch<T>>>,
    summary:            T::Summary,
}

impl<T:Timestamp> Operate<T> for Operator<T> {
    fn name(&self) -> String { "Feedback".to_owned() }
    fn inputs(&self) -> usize { 1 }
    fn outputs(&self) -> usize { 1 }

    fn get_internal_summary(&mut self) -> (Vec<Vec<Antichain<T::Summary>>>, Vec<ChangeBatch<T>>) {
        (vec![vec![Antichain::from_elem(self.summary.clone())]], vec![ChangeBatch::new()])
    }

    fn pull_internal_progress(&mut self, messages_consumed: &mut [ChangeBatch<T>],
                                        _frontier_progress: &mut [ChangeBatch<T>],
                                         messages_produced: &mut [ChangeBatch<T>]) -> bool {

        self.consumed_messages.borrow_mut().drain_into(&mut messages_consumed[0]);
        self.produced_messages.borrow_mut().drain_into(&mut messages_produced[0]);
        false
    }

    fn notify_me(&self) -> bool { false }
}
<commit_msg>LoopVariable doc typo fix<commit_after>//! Create cycles in a timely dataflow graph.

use std::rc::Rc;
use std::cell::RefCell;

use {Data, Push};

use progress::{Timestamp, Operate, PathSummary};
use progress::frontier::Antichain;
use progress::nested::{Source, Target};
use progress::ChangeBatch;

use progress::nested::product::Product;
use progress::nested::Summary::Local;

use dataflow::channels::Content;
use dataflow::channels::pushers::{Counter, Tee};

use dataflow::{Stream, Scope, ScopeParent};
use dataflow::scopes::Child;

/// Creates a `Stream` and a `Handle` to later bind the source of that `Stream`.
pub trait LoopVariable<'a, G: ScopeParent, T: Timestamp> {
    /// Creates a `Stream` and a `Handle` to later bind the source of that `Stream`.
    ///
    /// The resulting `Stream` will have its data defined by a future call to `connect_loop` with
    /// its `Handle` passed as an argument. Data passed through the stream will have their
    /// timestamps advanced by `summary`, and will be dropped if the result exceeds `limit`.
    ///
    /// #Examples
    /// ```
    /// use timely::dataflow::operators::{LoopVariable, ConnectLoop, ToStream, Concat, Inspect};
    ///
    /// timely::example(|scope| {
    ///     // circulate 0..10 for 100 iterations.
    ///     let (handle, cycle) = scope.loop_variable(100, 1);
    ///     (0..10).to_stream(scope)
    ///            .concat(&cycle)
    ///            .inspect(|x| println!("seen: {:?}", x))
    ///            .connect_loop(handle);
    /// });
    /// ```
    fn loop_variable<D: Data>(&mut self, limit: T, summary: T::Summary) -> (Handle<G::Timestamp, T, D>, Stream<Child<'a, G, T>, D>);
}

impl<'a, G: ScopeParent, T: Timestamp> LoopVariable<'a, G, T> for Child<'a, G, T> {
    fn loop_variable<D: Data>(&mut self, limit: T, summary: T::Summary) -> (Handle<G::Timestamp, T, D>, Stream<Child<'a, G, T>, D>) {

        let (targets, registrar) = Tee::<Product<G::Timestamp, T>, D>::new();

        let feedback_output = Counter::new(targets);
        let produced = feedback_output.produced().clone();

        let feedback_input =  Counter::new(Observer {
            limit: limit, summary: summary.clone(), targets: feedback_output
        });
        let consumed = feedback_input.produced().clone();

        let index = self.add_operator(Operator {
            consumed_messages:  consumed,
            produced_messages:  produced,
            summary:            Local(summary),
        });

        let helper = Handle {
            index:  index,
            target: feedback_input,
        };

        (helper, Stream::new(Source { index: index, port: 0 }, registrar, self.clone()))
    }
}

// implementation of the feedback vertex, essentially, as an observer
struct Observer<TOuter: Timestamp, TInner: Timestamp, D:Data> {
    limit:      TInner,
    summary:    TInner::Summary,
    targets:    Counter<Product<TOuter, TInner>, D, Tee<Product<TOuter, TInner>, D>>,
}

impl<TOuter: Timestamp, TInner: Timestamp, D: Data> Push<(Product<TOuter, TInner>, Content<D>)> for Observer<TOuter, TInner, D> {
    #[inline]
    fn push(&mut self, message: &mut Option<(Product<TOuter, TInner>, Content<D>)>) {
        let active = if let Some((ref mut time, _)) = *message {
            if let Some(new_time) = self.summary.results_in(&time.inner) {
                time.inner = new_time;
                time.inner.less_equal(&self.limit)
            }
            else {
                false
            }
        }
        else { true };

        if active { self.targets.push(message); }
    }
}

/// Connect a `Stream` to the input of a loop variable.
pub trait ConnectLoop<G: ScopeParent, T: Timestamp, D: Data> {
    /// Connect a `Stream` to be the input of a loop variable.
    ///
    /// #Examples
    /// ```
    /// use timely::dataflow::operators::{LoopVariable, ConnectLoop, ToStream, Concat, Inspect};
    ///
    /// timely::example(|scope| {
    ///     // circulate 0..10 for 100 iterations.
    ///     let (handle, cycle) = scope.loop_variable(100, 1);
    ///     (0..10).to_stream(scope)
    ///            .concat(&cycle)
    ///            .inspect(|x| println!("seen: {:?}", x))
    ///            .connect_loop(handle);
    /// });
    /// ```
    fn connect_loop(&self, Handle<G::Timestamp, T, D>);
}

impl<'a, G: ScopeParent, T: Timestamp, D: Data> ConnectLoop<G, T, D> for Stream<Child<'a, G, T>, D> {
    fn connect_loop(&self, helper: Handle<G::Timestamp, T, D>) {
        let channel_id = self.scope().new_identifier();
        self.connect_to(Target { index: helper.index, port: 0 }, helper.target, channel_id);
    }
}

/// A handle used to bind the source of a loop variable.
pub struct Handle<TOuter: Timestamp, TInner: Timestamp, D: Data> {
    index:  usize,
    target: Counter<Product<TOuter, TInner>, D, Observer<TOuter, TInner, D>>
}

// the scope that the progress tracker interacts with
struct Operator<T:Timestamp> {
    consumed_messages:  Rc<RefCell<ChangeBatch<T>>>,
    produced_messages:  Rc<RefCell<ChangeBatch<T>>>,
    summary:            T::Summary,
}

impl<T:Timestamp> Operate<T> for Operator<T> {
    fn name(&self) -> String { "Feedback".to_owned() }
    fn inputs(&self) -> usize { 1 }
    fn outputs(&self) -> usize { 1 }

    fn get_internal_summary(&mut self) -> (Vec<Vec<Antichain<T::Summary>>>, Vec<ChangeBatch<T>>) {
        (vec![vec![Antichain::from_elem(self.summary.clone())]], vec![ChangeBatch::new()])
    }

    fn pull_internal_progress(&mut self, messages_consumed: &mut [ChangeBatch<T>],
                                        _frontier_progress: &mut [ChangeBatch<T>],
                                         messages_produced: &mut [ChangeBatch<T>]) -> bool {

        self.consumed_messages.borrow_mut().drain_into(&mut messages_consumed[0]);
        self.produced_messages.borrow_mut().drain_into(&mut messages_produced[0]);
        false
    }

    fn notify_me(&self) -> bool { false }
}
<|endoftext|>


<commit_before>//=========================================================================
// public imports
//=========================================================================
pub use secret_collection::*;
pub use secret_item::*;
pub use secret_value::*;

//=========================================================================
// private imports
//=========================================================================
use std::ptr;
use std::collections::HashMap;
use glib::Error;
use glib::glib_container::GlibContainer;
use glib::object::{Ref, Wrapper, Object, Upcast};
use glib::types::{StaticType, Type};
use glib::translate::*;
use SecretResult;
use ffi;

/// A SecretService object represents the Secret Service implementation which runs as a D-Bus service.
/// In order to securely transfer secrets to the Sercret Service, a session is established. This will automatically be done when calling `SecretService::get()`
/// To search for items, use the `search()` method.
/// Multiple collections can exist in the Secret Service, each of which contains secret items. To access the list of Collections, use `get_collections()`.
/// Certain actions on the Secret Service require user prompting to complete, such as creating a collection, or unlocking a collection. When such a prompt is necessary, then a SecretPrompt object is created by libsecret, and passed to the secret_service_prompt() method. In this way it is handled automatically.
pub struct SecretService(Ref);

impl SecretService {

    /// Constructs a new SecretService which has established a session and whose collections are loaded.
    /// The underlying FFI object might be identical for multiple instances of this struct.
    pub fn get() -> SecretResult<Self>{
        SecretService::with_flags(SECRET_SERVICE_OPEN_SESSION | SECRET_SERVICE_LOAD_COLLECTIONS)
    }

    fn with_flags(flags: i32) -> SecretResult<Self> {
        let mut err = ptr::null_mut();
        let ptr = unsafe{ffi::secret_service_get_sync(flags, ptr::null_mut(), &mut err)};
        if err.is_null() {
            Ok(unsafe{from_glib_full(ptr)})
        } else {
            Err(Error::wrap(err))
        }
    }

    /// Returns if a session to the SecretService is currently established.
    pub fn is_session_established(&self) -> bool {
        let flags = unsafe {ffi::secret_service_get_flags(self.to_glib_none().0)};
        flags & SECRET_SERVICE_OPEN_SESSION != 0
    }

    /// Returns if the Service's collections are loaded.
    pub fn are_collections_loaded(&self) -> bool {
        let flags = unsafe {ffi::secret_service_get_flags(self.to_glib_none().0)};
        flags & SECRET_SERVICE_LOAD_COLLECTIONS != 0
    }

    /// Get the set of algorithms being used to transfer secrets between this secret service proxy and the Secret Service itself.
    /// The contained String has the format "algorithm-algorithm-algorithm-..."
    pub fn get_session_algorithms(&self) -> String {
        unsafe{
            let ptr = ffi::secret_service_get_session_algorithms(self.to_glib_none().0);
            from_glib_none(ptr)
        }
    }

    /// Get the collections of the Service.
    /// A collection contains multiple SecretItems.
    pub fn get_collections(&self) -> Vec<SecretCollection> {
        unsafe {
            let glist = ffi::secret_service_get_collections(self.to_glib_none().0);
            Vec::from_glib_full(glist)
        }
    }

    /// Search for items matching the attributes. All collections are searched. The attributes should be a table of string keys and string values.
    pub fn search(&self, attributes: &HashMap<String, String>) -> SecretResult<Vec<SecretItem>> {
        let mut err = ptr::null_mut();
        unsafe {
            let glist = ffi::secret_service_search_sync(self.to_glib_none().0, ptr::null(), attributes.to_glib_none().0, SECRET_SEARCH_ALL, ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(Vec::from_glib_full(glist))
            } else {
                Err(Error::wrap(err))
            }
        }
    }

    /// Store a secret value in the secret service.
    /// The `attributes` should be a set of key and value string pairs.
    /// If the attributes match a secret item already stored in the collection, then the item will be updated with these new values.
    /// `collection` is a collection alias, or `None` to store the value in the default collection (TODO: What about session storage?)
    /// `label` specifies a label for the secret.
    /// `value` is the actual secret to store. This can be created with `SecretValue::new()`.
    pub fn store(&self, attributes: &HashMap<String, String>, collection: Option<&str>, label: &str, value: &SecretValue) -> SecretResult<()> {
        let mut err = ptr::null_mut();
        unsafe {
            ffi::secret_service_store_sync(self.to_glib_none().0, ptr::null(), attributes.to_glib_none().0, collection.to_glib_none().0, label.to_glib_none().0, value.to_glib_none(), ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(())
            } else {
                Err(Error::wrap(err))
            }
        }
    }

    /*
    pub fn lookup () -> SecretValue {

    }
    */

    /// Remove unlocked items which match the attributes from the secret service.
    /// The attributes should be a set of key and value string pairs.
    pub fn clear(&self, attributes: &HashMap<String, String>) -> SecretResult<()> {
        let mut err = ptr::null_mut();
        unsafe {
            ffi::secret_service_clear_sync(self.to_glib_none().0, ptr::null(), attributes.to_glib_none().0, ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(())
            } else {
                Err(Error::wrap(err))
            }
        }
    }

    /// Ensures that a session is established.
    pub fn ensure_session(&self) -> SecretResult<()> {
        unsafe {
            let mut err = ptr::null_mut();
            ffi::secret_service_ensure_session_sync(self.to_glib_none().0, ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(())
            } else {
                Err(Error::wrap(err))
            }
        }
    }

    /// Ensures that the collections are loaded.
    pub fn load_collections(&self) -> SecretResult<()> {
        unsafe {
            let mut err = ptr::null_mut();
            ffi::secret_service_load_collections_sync(self.to_glib_none().0, ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(())
            } else {
                Err(Error::wrap(err))
            }
        }
    }
}

unsafe impl Upcast<Object> for SecretService { }

impl StaticType for SecretService {
    fn static_type() -> Type{
        unsafe { from_glib(ffi::secret_service_get_type()) }
    }
}

impl Wrapper for SecretService {
    type GlibType = ffi::SecretService;

    unsafe fn wrap(r: Ref) -> Self{
        SecretService(r)
    }

    fn as_ref(&self) -> &Ref{
        &self.0
    }

    fn unwrap(self) -> Ref{
        self.0
    }
}

#[allow(dead_code)]
const SECRET_SERVICE_NONE: i32              = 0;
const SECRET_SERVICE_OPEN_SESSION: i32      = 1 << 1;
const SECRET_SERVICE_LOAD_COLLECTIONS: i32  = 1 << 2;

#[allow(dead_code)]
const SECRET_SEARCH_NONE: i32               = 0;
#[allow(dead_code)]
const SECRET_SEARCH_ALL: i32                = 1 << 1;
#[allow(dead_code)]
const SECRET_SEARCH_UNLOCK: i32             = 1 << 2;
#[allow(dead_code)]
const SECRET_SEARCH_LOAD_SECRETS: i32       = 1 << 3;

#[cfg(test)]
mod test {
    use glib::types::{StaticType, Type};
    use super::SecretService;

    #[test]
    pub fn test_ss_get() {
        let res = SecretService::get();
        assert!(res.is_ok());
        let ss = res.ok().unwrap();
        assert!(ss.is_session_established());
        assert!(ss.are_collections_loaded());
    }

    #[test]
    pub fn test_ss_session_algorithms() {
        let ss = SecretService::get().ok().unwrap();
        assert!(ss.get_session_algorithms() == "plain" || ss.get_session_algorithms() == "dh-ietf1024-sha256-aes128-cbc-pkcs7");
    }

    #[test]
    pub fn test_ss_static_type() {
        match SecretService::static_type() {
            Type::Other(_) => {},
            _ => panic!("Expected Type::Other")
        }
    }

    /*
    #[test]
    pub fn test_ss_store_search_clear() {
        let res = SecretService::get().ok().unwrap();

    }
    */
}
<commit_msg>SecretService new testcase<commit_after>//=========================================================================
// public imports
//=========================================================================
pub use secret_collection::*;
pub use secret_item::*;
pub use secret_value::*;

//=========================================================================
// private imports
//=========================================================================
use std::ptr;
use std::collections::HashMap;
use glib::Error;
use glib::glib_container::GlibContainer;
use glib::object::{Ref, Wrapper, Object, Upcast};
use glib::types::{StaticType, Type};
use glib::translate::*;
use SecretResult;
use ffi;

/// A SecretService object represents the Secret Service implementation which runs as a D-Bus service.
/// In order to securely transfer secrets to the Sercret Service, a session is established. This will automatically be done when calling `SecretService::get()`
/// To search for items, use the `search()` method.
/// Multiple collections can exist in the Secret Service, each of which contains secret items. To access the list of Collections, use `get_collections()`.
/// Certain actions on the Secret Service require user prompting to complete, such as creating a collection, or unlocking a collection. When such a prompt is necessary, then a SecretPrompt object is created by libsecret, and passed to the secret_service_prompt() method. In this way it is handled automatically.
pub struct SecretService(Ref);

impl SecretService {

    /// Constructs a new SecretService which has established a session and whose collections are loaded.
    /// The underlying FFI object might be identical for multiple instances of this struct.
    pub fn get() -> SecretResult<Self>{
        SecretService::with_flags(SECRET_SERVICE_OPEN_SESSION | SECRET_SERVICE_LOAD_COLLECTIONS)
    }

    fn with_flags(flags: i32) -> SecretResult<Self> {
        let mut err = ptr::null_mut();
        let ptr = unsafe{ffi::secret_service_get_sync(flags, ptr::null_mut(), &mut err)};
        if err.is_null() {
            Ok(unsafe{from_glib_full(ptr)})
        } else {
            Err(Error::wrap(err))
        }
    }

    /// Returns if a session to the SecretService is currently established.
    pub fn is_session_established(&self) -> bool {
        let flags = unsafe {ffi::secret_service_get_flags(self.to_glib_none().0)};
        flags & SECRET_SERVICE_OPEN_SESSION != 0
    }

    /// Returns if the Service's collections are loaded.
    pub fn are_collections_loaded(&self) -> bool {
        let flags = unsafe {ffi::secret_service_get_flags(self.to_glib_none().0)};
        flags & SECRET_SERVICE_LOAD_COLLECTIONS != 0
    }

    /// Get the set of algorithms being used to transfer secrets between this secret service proxy and the Secret Service itself.
    /// The contained String has the format "algorithm-algorithm-algorithm-..."
    pub fn get_session_algorithms(&self) -> String {
        unsafe{
            let ptr = ffi::secret_service_get_session_algorithms(self.to_glib_none().0);
            from_glib_none(ptr)
        }
    }

    /// Get the collections of the Service.
    /// A collection contains multiple SecretItems.
    pub fn get_collections(&self) -> Vec<SecretCollection> {
        unsafe {
            let glist = ffi::secret_service_get_collections(self.to_glib_none().0);
            Vec::from_glib_full(glist)
        }
    }

    /// Search for items matching the attributes. All collections are searched. The attributes should be a table of string keys and string values.
    pub fn search(&self, attributes: &HashMap<String, String>) -> SecretResult<Vec<SecretItem>> {
        let mut err = ptr::null_mut();
        unsafe {
            let glist = ffi::secret_service_search_sync(self.to_glib_none().0, ptr::null(), attributes.to_glib_none().0, SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS, ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(Vec::from_glib_full(glist))
            } else {
                Err(Error::wrap(err))
            }
        }
    }

    /// Store a secret value in the secret service.
    /// The `attributes` should be a set of key and value string pairs.
    /// If the attributes match a secret item already stored in the collection, then the item will be updated with these new values.
    /// `collection` is a collection alias, or `None` to store the value in the default collection (TODO: What about session storage?)
    /// `label` specifies a label for the secret.
    /// `value` is the actual secret to store. This can be created with `SecretValue::new()`.
    pub fn store(&self, attributes: &HashMap<String, String>, collection: Option<&str>, label: &str, value: &SecretValue) -> SecretResult<()> {
        let mut err = ptr::null_mut();
        unsafe {
            ffi::secret_service_store_sync(self.to_glib_none().0, ptr::null(), attributes.to_glib_none().0, collection.to_glib_none().0, label.to_glib_none().0, value.to_glib_none(), ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(())
            } else {
                Err(Error::wrap(err))
            }
        }
    }

    /*
    pub fn lookup () -> SecretValue {

    }
    */

    /// Remove unlocked items which match the attributes from the secret service.
    /// The attributes should be a set of key and value string pairs.
    pub fn clear(&self, attributes: &HashMap<String, String>) -> SecretResult<()> {
        let mut err = ptr::null_mut();
        unsafe {
            ffi::secret_service_clear_sync(self.to_glib_none().0, ptr::null(), attributes.to_glib_none().0, ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(())
            } else {
                Err(Error::wrap(err))
            }
        }
    }

    /// Ensures that a session is established.
    pub fn ensure_session(&self) -> SecretResult<()> {
        unsafe {
            let mut err = ptr::null_mut();
            ffi::secret_service_ensure_session_sync(self.to_glib_none().0, ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(())
            } else {
                Err(Error::wrap(err))
            }
        }
    }

    /// Ensures that the collections are loaded.
    pub fn load_collections(&self) -> SecretResult<()> {
        unsafe {
            let mut err = ptr::null_mut();
            ffi::secret_service_load_collections_sync(self.to_glib_none().0, ptr::null_mut(), &mut err);
            if err.is_null() {
                Ok(())
            } else {
                Err(Error::wrap(err))
            }
        }
    }
}

unsafe impl Upcast<Object> for SecretService { }

impl StaticType for SecretService {
    fn static_type() -> Type{
        unsafe { from_glib(ffi::secret_service_get_type()) }
    }
}

impl Wrapper for SecretService {
    type GlibType = ffi::SecretService;

    unsafe fn wrap(r: Ref) -> Self{
        SecretService(r)
    }

    fn as_ref(&self) -> &Ref{
        &self.0
    }

    fn unwrap(self) -> Ref{
        self.0
    }
}

#[allow(dead_code)]
const SECRET_SERVICE_NONE: i32              = 0;
const SECRET_SERVICE_OPEN_SESSION: i32      = 1 << 1;
const SECRET_SERVICE_LOAD_COLLECTIONS: i32  = 1 << 2;

#[allow(dead_code)]
const SECRET_SEARCH_NONE: i32               = 0;
const SECRET_SEARCH_ALL: i32                = 1 << 1;
const SECRET_SEARCH_UNLOCK: i32             = 1 << 2;
const SECRET_SEARCH_LOAD_SECRETS: i32       = 1 << 3;

#[cfg(test)]
mod test {
    use std::collections::HashMap;
    use glib::types::{StaticType, Type};
    use secret_value::SecretValue;
    use super::SecretService;

    #[test]
    pub fn test_ss_get() {
        let res = SecretService::get();
        assert!(res.is_ok());
        let ss = res.ok().unwrap();
        assert!(ss.is_session_established());
        assert!(ss.are_collections_loaded());
    }

    #[test]
    pub fn test_ss_session_algorithms() {
        let ss = SecretService::get().ok().unwrap();
        assert!(ss.get_session_algorithms() == "plain" || ss.get_session_algorithms() == "dh-ietf1024-sha256-aes128-cbc-pkcs7");
    }

    #[test]
    pub fn test_ss_static_type() {
        match SecretService::static_type() {
            Type::Other(_) => {},
            _ => panic!("Expected Type::Other")
        }
    }

    #[test]
    pub fn test_ss_store_search_clear() {
        let ss = SecretService::get().ok().unwrap();
        let mut attrs: HashMap<String, String> = HashMap::new();
        attrs.insert("application".into(), "secret-rs-unit-test".into());

        let sv = SecretValue::new("password123");

        ss.store(&attrs, None, "mylabel", &sv).ok().unwrap();
        let search_results = ss.search(&attrs).ok().unwrap();
        assert!(search_results.len() == 1);
        let item = search_results.get(0).unwrap();
        assert_eq!(item.get_label(), "mylabel");
        assert_eq!(item.get_attributes().get("application").unwrap(), "secret-rs-unit-test");
        assert_eq!(item.get_secret().unwrap().get().unwrap(), "password123");
        ss.clear(&attrs).ok().unwrap();
        let search_results = ss.search(&attrs).ok().unwrap();
        assert!(search_results.len() == 0);
    }
}
<|endoftext|>


<commit_before>
extern crate regex;
use regex::Regex;

#[allow(dead_code)]
static DAY_1_EXAMPLE: &'static str =
"Disc #1 has 5 positions; at time=0, it is at position 4.
Disc #2 has 2 positions; at time=0, it is at position 1.";

#[allow(dead_code)]
static DAY_1_INPUT: &'static str =
"Disc #1 has 17 positions; at time=0, it is at position 15.
Disc #2 has 3 positions; at time=0, it is at position 2.
Disc #3 has 19 positions; at time=0, it is at position 4.
Disc #4 has 13 positions; at time=0, it is at position 2.
Disc #5 has 7 positions; at time=0, it is at position 2.
Disc #6 has 5 positions; at time=0, it is at position 0.";

#[derive(Debug)]
struct Disk {
    id: u32,
    num_positions: u32,
    initial_pos: u32
}

fn parse_input(input: &str) -> Vec<Disk> {
    //let re = Regex::new(r"([RL])([:digit:]+)").unwrap();

    let re = Regex::new(r"Disc #(\d+) has (\d+) positions; at time=0, it is at position (\d+).").unwrap();;

    let mut results = Vec::new();
    for cap in re.captures_iter(&input) {
        results.push(Disk{
            id: cap[1].parse().unwrap(),
            num_positions: cap[2].parse().unwrap(),
            initial_pos: cap[3].parse().unwrap()
        });
    }
    results
}

// Solve the system of equations for positive T:
// (initial_pos + id + T) % num_positions = 0
fn solve(disks: Vec<Disk>) -> u32 {

    for time in 0..500000 {
        let mut total_residual = 0;
        for disk in &disks {
            let residual = (disk.initial_pos + disk.id + time) % disk.num_positions;
//            println!("  Disk {}: ({} + {} + {}) % {} = {}",
//                     disk.id, disk.initial_pos, disk.id, time, disk.num_positions, residual);
            total_residual += residual;
        }
        if total_residual == 0 {
            return time;
        }
    }
    0
}

fn main() {

    let example_input = parse_input(DAY_1_EXAMPLE);

    println!("{:?}", example_input);

    let day1_sample_time = solve(example_input);
    println!("Part 1 example time: {}", day1_sample_time);

    let day1_time = solve(parse_input(DAY_1_INPUT));
    println!("Part 1: time: {}", day1_time);
    assert_eq!(day1_time, 400589)
}
<commit_msg>Day 15, part 2<commit_after>
extern crate regex;
use regex::Regex;

#[allow(dead_code)]
static EXAMPLE_INPUT: &'static str =
"Disc #1 has 5 positions; at time=0, it is at position 4.
Disc #2 has 2 positions; at time=0, it is at position 1.";

#[allow(dead_code)]
static PART_1_INPUT: &'static str =
"Disc #1 has 17 positions; at time=0, it is at position 15.
Disc #2 has 3 positions; at time=0, it is at position 2.
Disc #3 has 19 positions; at time=0, it is at position 4.
Disc #4 has 13 positions; at time=0, it is at position 2.
Disc #5 has 7 positions; at time=0, it is at position 2.
Disc #6 has 5 positions; at time=0, it is at position 0.";

#[derive(Debug,Copy,Clone)]
struct Disk {
    id: u32,
    num_positions: u32,
    initial_pos: u32
}

fn parse_input(input: &str) -> Vec<Disk> {
    //let re = Regex::new(r"([RL])([:digit:]+)").unwrap();

    let re = Regex::new(r"Disc #(\d+) has (\d+) positions; at time=0, it is at position (\d+).").unwrap();;

    let mut results = Vec::new();
    for cap in re.captures_iter(&input) {
        results.push(Disk{
            id: cap[1].parse().unwrap(),
            num_positions: cap[2].parse().unwrap(),
            initial_pos: cap[3].parse().unwrap()
        });
    }
    results
}

// Solve the system of equations for positive T:
// (initial_pos + id + T) % num_positions = 0
fn solve(disks: &Vec<Disk>) -> u32 {

    for time in 0..u32::max_value() {
        let mut total_residual = 0;
        for disk in disks {
            let residual = (disk.initial_pos + disk.id + time) % disk.num_positions;
//            println!("  Disk {}: ({} + {} + {}) % {} = {}",
//                     disk.id, disk.initial_pos, disk.id, time, disk.num_positions, residual);
            total_residual += residual;
        }
        if total_residual == 0 {
            return time;
        }
    }
    0
}

fn main() {

    let example_input = parse_input(EXAMPLE_INPUT);

    println!("{:?}", example_input);

    let day1_sample_time = solve(&example_input);
    println!("Part 1 example time: {}", day1_sample_time);

    let part1_input = parse_input(PART_1_INPUT);
    let part1_time = solve(&part1_input);
    println!("Part 1: time: {}", part1_time);
    assert_eq!(part1_time, 400589);

    // Part 2
    let mut part2_input = part1_input.clone();
    part2_input.push(Disk{id: 7, num_positions: 11, initial_pos: 0});
    let part2_time = solve(&part2_input);
    println!("Part 2: time: {}", part2_time);
    assert_eq!(part2_time, 3045959);
}
<|endoftext|>


<commit_before>#![allow(unsafe_code)]
//! This module implements the standard attribute dictionary.
//!
//! This dictionary is a singleton containing all information about the
//! DICOM attributes specified in the standard according to DICOM PS3.6 2016c,
//! and it will be used by default
//!
//! When not using private tags, this dictionary should suffice.

extern crate lazy_static;

mod entries;

use std::collections::HashMap;
use std::fmt;
use std::fmt::{Display, Formatter};
use attribute::dictionary::{AttributeDictionary, DictionaryEntry};
use attribute::ValueRepresentation;
use self::entries::ENTRIES;

lazy_static! {
    static ref DICT: StandardAttributeDictionary = {
        init_dictionary()
    };
}

/// Retrieve a singleton instance of the standard dictionary.
pub fn get_instance() -> &'static StandardAttributeDictionary {
    &DICT
}

/// The data struct for the standard dictionary.
#[derive(Debug)]
pub struct StandardAttributeDictionary {
    name_to_pair: HashMap<&'static str, &'static DictionaryEntry<'static>>,
    pair_to_name: HashMap<(u16, u16), &'static DictionaryEntry<'static>>
}

impl StandardAttributeDictionary {
    fn new() -> StandardAttributeDictionary {
        StandardAttributeDictionary {
            name_to_pair: HashMap::new(),
            pair_to_name: HashMap::new()
        }
    }

    fn index(&mut self, entry: &'static DictionaryEntry<'static>) -> &mut Self {
        self.name_to_pair.insert(entry.alias, entry);
        self.pair_to_name.insert(entry.tag, entry);
        self
    }
}

impl AttributeDictionary<'static> for StandardAttributeDictionary {
    fn get_by_name(&self, name: &str) -> Option<&'static DictionaryEntry<'static>> {
        self.name_to_pair.get(name).map(|r| { *r })
    }

    fn get_by_tag(&self, tag: (u16, u16)) -> Option<&'static DictionaryEntry<'static>> {
        self.pair_to_name.get(&tag).map(|r| { *r })
    }
}

impl Display for StandardAttributeDictionary {
    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        f.write_str("Standard Attribute Dictionary")
    }
}

fn init_dictionary() -> StandardAttributeDictionary {
    let mut d = StandardAttributeDictionary::new();
    for entry in ENTRIES {
        d.index(&entry);
    }
    for entry in META_ENTRIES {
        d.index(&entry);
    }
    d
}

// meta information entries
type E<'a> = DictionaryEntry<'a>;
const META_ENTRIES : &'static [E<'static>] = &[
    E {tag: (0x0002,0x0000), alias: "FileMetaInformationGroupLength", vr: ValueRepresentation::UL},
    E {tag: (0x0002,0x0001), alias: "FileMetaInformationVersion", vr: ValueRepresentation::OB},
    E {tag: (0x0002,0x0002), alias: "MediaStorageSOPClassUID", vr: ValueRepresentation::UI},
    E {tag: (0x0002,0x0003), alias: "MediaStorageSOPInstanceUID", vr: ValueRepresentation::UI},
    E {tag: (0x0002,0x0010), alias: "TransferSyntaxUID", vr: ValueRepresentation::UI},
    E {tag: (0x0002,0x0012), alias: "ImplementationClassUID", vr: ValueRepresentation::UI},
    E {tag: (0x0002,0x0013), alias: "ImplentationVersionName", vr: ValueRepresentation::SH},
    E {tag: (0x0002,0x0016), alias: "SourceApplicationEntityTitle", vr: ValueRepresentation::AE},
    E {tag: (0x0002,0x0017), alias: "SendingApplicationEntityTitle", vr: ValueRepresentation::AE},
    E {tag: (0x0002,0x0018), alias: "ReceivingApplicationEntityTitle", vr: ValueRepresentation::AE},
    E {tag: (0x0002,0x0100), alias: "PrivateInformationCreatorUID", vr: ValueRepresentation::UI},
    E {tag: (0x0002,0x0102), alias: "PrivateInformation", vr: ValueRepresentation::OB},
];
<commit_msg>Remove hidden chars in file meta dict entries<commit_after>#![allow(unsafe_code)]
//! This module implements the standard attribute dictionary.
//!
//! This dictionary is a singleton containing all information about the
//! DICOM attributes specified in the standard according to DICOM PS3.6 2016c,
//! and it will be used by default
//!
//! When not using private tags, this dictionary should suffice.

extern crate lazy_static;

mod entries;

use std::collections::HashMap;
use std::fmt;
use std::fmt::{Display, Formatter};
use attribute::dictionary::{AttributeDictionary, DictionaryEntry};
use attribute::ValueRepresentation;
use self::entries::ENTRIES;

lazy_static! {
    static ref DICT: StandardAttributeDictionary = {
        init_dictionary()
    };
}

/// Retrieve a singleton instance of the standard dictionary.
pub fn get_instance() -> &'static StandardAttributeDictionary {
    &DICT
}

/// The data struct for the standard dictionary.
#[derive(Debug)]
pub struct StandardAttributeDictionary {
    name_to_pair: HashMap<&'static str, &'static DictionaryEntry<'static>>,
    pair_to_name: HashMap<(u16, u16), &'static DictionaryEntry<'static>>
}

impl StandardAttributeDictionary {
    fn new() -> StandardAttributeDictionary {
        StandardAttributeDictionary {
            name_to_pair: HashMap::new(),
            pair_to_name: HashMap::new()
        }
    }

    fn index(&mut self, entry: &'static DictionaryEntry<'static>) -> &mut Self {
        self.name_to_pair.insert(entry.alias, entry);
        self.pair_to_name.insert(entry.tag, entry);
        self
    }
}

impl AttributeDictionary<'static> for StandardAttributeDictionary {
    fn get_by_name(&self, name: &str) -> Option<&'static DictionaryEntry<'static>> {
        self.name_to_pair.get(name).map(|r| { *r })
    }

    fn get_by_tag(&self, tag: (u16, u16)) -> Option<&'static DictionaryEntry<'static>> {
        self.pair_to_name.get(&tag).map(|r| { *r })
    }
}

impl Display for StandardAttributeDictionary {
    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
        f.write_str("Standard Attribute Dictionary")
    }
}

fn init_dictionary() -> StandardAttributeDictionary {
    let mut d = StandardAttributeDictionary::new();
    for entry in ENTRIES {
        d.index(&entry);
    }
    for entry in META_ENTRIES {
        d.index(&entry);
    }
    d
}

// meta information entries
type E<'a> = DictionaryEntry<'a>;
const META_ENTRIES : &'static [E<'static>] = &[
    E {tag: (0x0002,0x0000), alias: "FileMetaInformationGroupLength", vr: ValueRepresentation::UL},
    E {tag: (0x0002,0x0001), alias: "FileMetaInformationVersion", vr: ValueRepresentation::OB},
    E {tag: (0x0002,0x0002), alias: "MediaStorageSOPClassUID", vr: ValueRepresentation::UI},
    E {tag: (0x0002,0x0003), alias: "MediaStorageSOPInstanceUID", vr: ValueRepresentation::UI},
    E {tag: (0x0002,0x0010), alias: "TransferSyntaxUID", vr: ValueRepresentation::UI},
    E {tag: (0x0002,0x0012), alias: "ImplementationClassUID", vr: ValueRepresentation::UI},
    E {tag: (0x0002,0x0013), alias: "ImplentationVersionName", vr: ValueRepresentation::SH},
    E {tag: (0x0002,0x0016), alias: "SourceApplicationEntityTitle", vr: ValueRepresentation::AE},
    E {tag: (0x0002,0x0017), alias: "SendingApplicationEntityTitle", vr: ValueRepresentation::AE},
    E {tag: (0x0002,0x0018), alias: "ReceivingApplicationEntityTitle", vr: ValueRepresentation::AE},
    E {tag: (0x0002,0x0100), alias: "PrivateInformationCreatorUID", vr: ValueRepresentation::UI},
    E {tag: (0x0002,0x0102), alias: "PrivateInformation", vr: ValueRepresentation::OB},
];
<|endoftext|>