<commit_before>use std::convert::From;
use std::io;
use byteorder;
use url;

#[cfg(feature = "tls")]
use openssl;
#[cfg(feature = "tls")]
use std::error::Error;

#[derive(Debug, Clone)]
pub enum AMQPError {
    AMQPIoError(io::ErrorKind),
    ByteOrderError,
    DecodeError(&'static str),
    QueueEmpty,
    Protocol(String),
    SyncError,
    UrlParseError(url::ParseError),
    SchemeError(String)
}

pub type AMQPResult<T> = Result<T, AMQPError>;

impl From<io::Error> for AMQPError {
    fn from(err: io::Error) -> AMQPError {
        AMQPError::AMQPIoError(err.kind())
    }
}

impl From<byteorder::Error> for AMQPError {
    fn from(_err: byteorder::Error) -> AMQPError {
        AMQPError::ByteOrderError
    }
}

impl From<url::ParseError> for AMQPError {
    fn from(err: url::ParseError) -> AMQPError {
        AMQPError::UrlParseError(err)
    }
}

#[cfg(feature = "tls")]
impl From<openssl::ssl::error::SslError> for AMQPError {
    fn from(err: openssl::ssl::error::SslError) -> AMQPError {
        AMQPError::Protocol(format!("{}", err))
    }
}
<commit_msg>Impl std::err::Error for AMQPError.<commit_after>use std::convert::From;
use std::{io, error, fmt};
use byteorder;
use url;

#[cfg(feature = "tls")]
use openssl;
#[cfg(feature = "tls")]
use std::error::Error;

#[derive(Debug, Clone)]
pub enum AMQPError {
    IoError(io::ErrorKind),
    DecodeError(&'static str),
    Protocol(String),
    SchemeError(String),
    UrlParseError(url::ParseError),
    ByteOrderError,
    QueueEmpty,
    SyncError,
}

impl fmt::Display for AMQPError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "AMQP Error: {}", error::Error::description(self))
    }
}

impl error::Error for AMQPError {
    fn description(&self) -> &str {
        match self {
            &AMQPError::IoError(_) => "IoError",
            &AMQPError::DecodeError(_) => "Protocol decoding error",
            &AMQPError::Protocol(_) => "Protocol level error",
            &AMQPError::SchemeError(_) => "Invalid scheme",
            &AMQPError::UrlParseError(_) => "URL parsing error",
            &AMQPError::ByteOrderError => "ByteOrderError",
            &AMQPError::QueueEmpty => "Queue is empty",
            &AMQPError::SyncError => "Synchronisation error"
        }
    }
}

pub type AMQPResult<T> = Result<T, AMQPError>;

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

impl From<byteorder::Error> for AMQPError {
    fn from(_err: byteorder::Error) -> AMQPError {
        AMQPError::ByteOrderError
    }
}

impl From<url::ParseError> for AMQPError {
    fn from(err: url::ParseError) -> AMQPError {
        AMQPError::UrlParseError(err)
    }
}

#[cfg(feature = "tls")]
impl From<openssl::ssl::error::SslError> for AMQPError {
    fn from(err: openssl::ssl::error::SslError) -> AMQPError {
        AMQPError::Protocol(format!("{}", err))
    }
}
<|endoftext|>


<commit_before>extern crate rand;

use std::{env, fs, io, mem, slice};
use std::io::{ErrorKind, Read, Write};
use rand::Rng;

#[derive(Copy, Clone, PartialEq)]
struct CooccurRec {
    word1: u32,
    word2: u32,
    val: f64,
}

static mut VERBOSE: i32 = 2;
macro_rules! progress {
    ($v:expr, $fmt:expr) => (
        if unsafe { VERBOSE > $v } { write!(io::stderr(), $fmt).unwrap(); }
    );
    ($v:expr, $fmt:expr, $($arg:tt)*) => (
        if unsafe { VERBOSE > $v } { write!(io::stderr(), $fmt, $($arg)*).unwrap(); }
    );
}
macro_rules! log {
    ($v:expr, $fmt:expr) => (
        if unsafe { VERBOSE > $v } { writeln!(io::stderr(), $fmt).unwrap(); }
    );
    ($v:expr, $fmt:expr, $($arg:tt)*) => (
        if unsafe { VERBOSE > $v } { writeln!(io::stderr(), $fmt, $($arg)*).unwrap(); }
    );
}

fn write_chunk<T>(cr: &Vec<CooccurRec>, fout: &mut T) where T: Write + Sized {
    if cr.len() == 0 { return; }
    let mut old = cr[0];

    for x in &cr[1..] {
        if x.word1 == old.word1 && x.word2 == old.word2 {
            old.val += x.val;
            continue;
        }
        fout.write(unsafe {
            slice::from_raw_parts((&old as *const CooccurRec) as *const u8,
            mem::size_of::<CooccurRec>())}).unwrap();
        old = *x;
    }
    fout.write(unsafe {
        slice::from_raw_parts((&old as *const CooccurRec) as *const u8,
        mem::size_of::<CooccurRec>())}).unwrap();
}

fn shuffle_merge(array_size: usize, file_head: &str, n_temp_file: usize) -> i32 {
    let mut array: Vec<CooccurRec> = Vec::with_capacity(array_size);
    let mut fid:  Vec<Option<fs::File>> = Vec::with_capacity(n_temp_file);
    for fidcounter in 0 .. n_temp_file {
        fid.push(Some(fs::File::open(
                    format!("{}_{:>04}.bin", file_head, fidcounter)).unwrap()));
    }
    progress!(0, "Merging temp files: processed 0 lines.");
    let mut rng = rand::thread_rng();
    let mut total = 0usize;
    loop {
        for f in fid.iter_mut() {
            if f.is_none() { continue; }
            for _ in 0 .. array_size / n_temp_file {
                array.push(unsafe { mem::uninitialized::<CooccurRec>() });
                if let Err(e) = f.as_mut().unwrap().read_exact(unsafe {
                    slice::from_raw_parts_mut((array.last_mut().unwrap() as *mut CooccurRec) as *mut u8,
                    mem::size_of::<CooccurRec>())}) {
                    if e.kind() == ErrorKind::UnexpectedEof {
                        array.pop();
                        *f = None;
                        break;
                    }
                    else {
                        panic!(e);
                    }
                }
            }
        }
        if array.len() == 0 { break; }
        total += array.len();
        rng.shuffle(&mut array);
        write_chunk(&array, &mut io::stdout());
        array.clear();
        progress!(0, "\x1b[31G{} lines.", total);
    }
    log!(-1, "\x1b[0GMerging temp files: processed {} lines.", total);
    fid.clear();
    for i in 0 .. n_temp_file {
        if let Err(e) = fs::remove_file(format!("{}_{:>04}.bin", file_head, i)) {
            log!(-1, "Error on fs::remove_file. Error is {:?}", e);
        }
    }
    0
}

// Shuffle large input stream by splitting into chunks
fn shuffle_by_chunks(array_size: usize, file_head: &str) -> i32 {
    log!(-1, "SHUFFLING COOCCURRENCES");
    log!(0, "array size: {}", array_size);

    let mut rng = rand::thread_rng();

    let mut array: Vec<CooccurRec> = Vec::with_capacity(array_size);
    let mut total = 0usize;
    let mut fin = io::stdin();

    let mut fidcounter = 0;
    progress!(1, "Shuffling by chunks: processed 0 lines.");
    loop {
        array.push(unsafe { mem::uninitialized() });
        if let Err(e) = fin.read_exact(unsafe {
            slice::from_raw_parts_mut((array.last_mut().unwrap() as *mut CooccurRec) as *mut u8,
            mem::size_of::<CooccurRec>()) }) {
            if e.kind() == ErrorKind::UnexpectedEof {
                array.pop();
                break;
            }
            else {
                panic!(e);
            }
        }
        if array.len() >= array_size {
            rng.shuffle(&mut array);
            total += array.len();
            progress!(1, "\x1b[22Gprocessed {} lines.", total);
            write_chunk(&array, &mut fs::File::create(
                    format!("{}_{:>04}.bin", file_head, fidcounter)).unwrap());
            fidcounter += 1;
            array.clear();
        }
    }
    rng.shuffle(&mut array);
    total += array.len();
    write_chunk(&array, &mut fs::File::create(
            format!("{}_{:>04}.bin", file_head, fidcounter)).unwrap());
    fidcounter += 1;
    log!(1, "\x1b[22Gprocessed {} lines.", total);
    log!(1, "Wrote {} temporary file(s).", fidcounter);
    shuffle_merge(array_size, file_head, fidcounter)
}

fn main() {
    let array_size: usize;
    let mut temp_file = "temp_shuffle".to_string();
    {
        let args: Vec<String> = env::args().collect();

        if args.len() == 1 {
            println!("Tools to shuffle entries of word-word cooccurrence files
This program is transportation of GloVe shuffle.
Original program (written in C) author is Jeffrey Pennington (jpennin@stanford.edu)
Transpoted to rust by Takumi Kato (takumi.kt@gmail.com)

Usage options:
\t-verbose <int>
\t\tSet verbosity: 0, 1, or 2 (default)
\t-memory <float>
\t\tSoft limit for memory consumption, in GB -- based on simple heuristic, so not extremely accurate; default 4.0
\t-array-size <int>
\t\tLimit to length <int> the buffer  which stores chunks of data to shuffle before writing to disk.
This value overrides that which is automatically produced by '-memory'.
\t-temp-file <file>
\t\tFilename, excluding extension, for temporary file; default temp_shuffle

Example usage:
./shuffle -verbose 2 -memory 8.0 < cooccurrence.bin > cooccurrence.shuf.bin");
            std::process::exit(0);
        }
        let mut is_skip = true;
        let mut it = args.into_iter().peekable();

        let mut array_size_opt: Option<usize> = None;
        let mut memory_limit = 2.0f64;
        loop {
            let arg = match it.next() {
                Some(s) => s,
                None => break,
            };
            if is_skip {
                is_skip = false;
                continue
            }
            match arg.as_ref() {
                "-verbose" => {
                    unsafe { VERBOSE = it.peek().and_then(|v| v.parse().ok())
                        .and_then(|x| if 0 <= x && x <= 2 { Some(x) } else { None })
                        .expect("-verbose <int>, verbosity: 0, 1, or 2 (default)") };
                    is_skip = true;
                },
                "-memory" => {
                    memory_limit = it.peek().and_then(|v| v.parse().ok())
                        .expect("-memory <float>");
                    is_skip = true;
                },
                "-array-size" => {
                    array_size_opt = Some(it.peek().and_then(|v| v.parse().ok())
                        .expect("-array-size <int>"));
                    is_skip = true;
                },
                "-temp-file" => {
                    temp_file = it.peek().expect("-temp-file <file>").to_string();
                    is_skip = true;
                },
                &_ => {},
            }
        }
        array_size = array_size_opt.unwrap_or((0.95 * memory_limit as f64 * 1073741724f64 / mem::size_of::<CooccurRec>() as f64) as usize);
    }
    std::process::exit(shuffle_by_chunks(array_size, &temp_file));
}
<commit_msg>Array size was wrong.<commit_after>extern crate rand;

use std::{env, fs, io, mem, slice};
use std::io::{ErrorKind, Read, Write};
use rand::Rng;

#[derive(Copy, Clone, PartialEq)]
struct CooccurRec {
    word1: u32,
    word2: u32,
    val: f64,
}

static mut VERBOSE: i32 = 2;
macro_rules! progress {
    ($v:expr, $fmt:expr) => (
        if unsafe { VERBOSE > $v } { write!(io::stderr(), $fmt).unwrap(); }
    );
    ($v:expr, $fmt:expr, $($arg:tt)*) => (
        if unsafe { VERBOSE > $v } { write!(io::stderr(), $fmt, $($arg)*).unwrap(); }
    );
}
macro_rules! log {
    ($v:expr, $fmt:expr) => (
        if unsafe { VERBOSE > $v } { writeln!(io::stderr(), $fmt).unwrap(); }
    );
    ($v:expr, $fmt:expr, $($arg:tt)*) => (
        if unsafe { VERBOSE > $v } { writeln!(io::stderr(), $fmt, $($arg)*).unwrap(); }
    );
}

fn write_chunk<T>(cr: &Vec<CooccurRec>, fout: &mut T) where T: Write + Sized {
    if cr.len() == 0 { return; }
    let mut old = cr[0];

    for x in &cr[1..] {
        if x.word1 == old.word1 && x.word2 == old.word2 {
            old.val += x.val;
            continue;
        }
        fout.write(unsafe {
            slice::from_raw_parts((&old as *const CooccurRec) as *const u8,
            mem::size_of::<CooccurRec>())}).unwrap();
        old = *x;
    }
    fout.write(unsafe {
        slice::from_raw_parts((&old as *const CooccurRec) as *const u8,
        mem::size_of::<CooccurRec>())}).unwrap();
}

fn shuffle_merge(array_size: usize, file_head: &str, n_temp_file: usize) -> i32 {
    let mut array: Vec<CooccurRec> = Vec::with_capacity(array_size);
    let mut fid:  Vec<Option<fs::File>> = Vec::with_capacity(n_temp_file);
    for fidcounter in 0 .. n_temp_file {
        fid.push(Some(fs::File::open(
                    format!("{}_{:>04}.bin", file_head, fidcounter)).unwrap()));
    }
    progress!(0, "Merging temp files: processed 0 lines.");
    let mut rng = rand::thread_rng();
    let mut total = 0usize;
    loop {
        for f in fid.iter_mut() {
            if f.is_none() { continue; }
            for _ in 0 .. array_size / n_temp_file {
                array.push(unsafe { mem::uninitialized::<CooccurRec>() });
                if let Err(e) = f.as_mut().unwrap().read_exact(unsafe {
                    slice::from_raw_parts_mut((array.last_mut().unwrap() as *mut CooccurRec) as *mut u8,
                    mem::size_of::<CooccurRec>())}) {
                    if e.kind() == ErrorKind::UnexpectedEof {
                        array.pop();
                        *f = None;
                        break;
                    }
                    else {
                        panic!(e);
                    }
                }
            }
        }
        if array.len() == 0 { break; }
        total += array.len();
        rng.shuffle(&mut array);
        write_chunk(&array, &mut io::stdout());
        array.clear();
        progress!(0, "\x1b[31G{} lines.", total);
    }
    log!(-1, "\x1b[0GMerging temp files: processed {} lines.", total);
    fid.clear();
    for i in 0 .. n_temp_file {
        if let Err(e) = fs::remove_file(format!("{}_{:>04}.bin", file_head, i)) {
            log!(-1, "Error on fs::remove_file. Error is {:?}", e);
        }
    }
    0
}

// Shuffle large input stream by splitting into chunks
fn shuffle_by_chunks(array_size: usize, file_head: &str) -> i32 {
    log!(-1, "SHUFFLING COOCCURRENCES");
    log!(0, "array size: {}", array_size);

    let mut rng = rand::thread_rng();

    let mut array: Vec<CooccurRec> = Vec::with_capacity(array_size);
    let mut total = 0usize;
    let mut fin = io::stdin();

    let mut fidcounter = 0;
    progress!(1, "Shuffling by chunks: processed 0 lines.");
    loop {
        array.push(unsafe { mem::uninitialized() });
        if let Err(e) = fin.read_exact(unsafe {
            slice::from_raw_parts_mut((array.last_mut().unwrap() as *mut CooccurRec) as *mut u8,
            mem::size_of::<CooccurRec>()) }) {
            if e.kind() == ErrorKind::UnexpectedEof {
                array.pop();
                break;
            }
            else {
                panic!(e);
            }
        }
        if array.len() >= array_size {
            rng.shuffle(&mut array);
            total += array.len();
            progress!(1, "\x1b[22Gprocessed {} lines.", total);
            write_chunk(&array, &mut fs::File::create(
                    format!("{}_{:>04}.bin", file_head, fidcounter)).unwrap());
            fidcounter += 1;
            array.clear();
        }
    }
    rng.shuffle(&mut array);
    total += array.len();
    write_chunk(&array, &mut fs::File::create(
            format!("{}_{:>04}.bin", file_head, fidcounter)).unwrap());
    fidcounter += 1;
    log!(1, "\x1b[22Gprocessed {} lines.", total);
    log!(1, "Wrote {} temporary file(s).", fidcounter);
    shuffle_merge(array_size, file_head, fidcounter)
}

fn main() {
    let array_size: usize;
    let mut temp_file = "temp_shuffle".to_string();
    {
        let args: Vec<String> = env::args().collect();

        if args.len() == 1 {
            println!("Tools to shuffle entries of word-word cooccurrence files
This program is transportation of GloVe shuffle.
Original program (written in C) author is Jeffrey Pennington (jpennin@stanford.edu)
Transpoted to rust by Takumi Kato (takumi.kt@gmail.com)

Usage options:
\t-verbose <int>
\t\tSet verbosity: 0, 1, or 2 (default)
\t-memory <float>
\t\tSoft limit for memory consumption, in GB -- based on simple heuristic, so not extremely accurate; default 4.0
\t-array-size <int>
\t\tLimit to length <int> the buffer  which stores chunks of data to shuffle before writing to disk.
This value overrides that which is automatically produced by '-memory'.
\t-temp-file <file>
\t\tFilename, excluding extension, for temporary file; default temp_shuffle

Example usage:
./shuffle -verbose 2 -memory 8.0 < cooccurrence.bin > cooccurrence.shuf.bin");
            std::process::exit(0);
        }
        let mut is_skip = true;
        let mut it = args.into_iter().peekable();

        let mut array_size_opt: Option<usize> = None;
        let mut memory_limit = 2.0f64;
        loop {
            let arg = match it.next() {
                Some(s) => s,
                None => break,
            };
            if is_skip {
                is_skip = false;
                continue
            }
            match arg.as_ref() {
                "-verbose" => {
                    unsafe { VERBOSE = it.peek().and_then(|v| v.parse().ok())
                        .and_then(|x| if 0 <= x && x <= 2 { Some(x) } else { None })
                        .expect("-verbose <int>, verbosity: 0, 1, or 2 (default)") };
                    is_skip = true;
                },
                "-memory" => {
                    memory_limit = it.peek().and_then(|v| v.parse().ok())
                        .expect("-memory <float>");
                    is_skip = true;
                },
                "-array-size" => {
                    array_size_opt = Some(it.peek().and_then(|v| v.parse().ok())
                        .expect("-array-size <int>"));
                    is_skip = true;
                },
                "-temp-file" => {
                    temp_file = it.peek().expect("-temp-file <file>").to_string();
                    is_skip = true;
                },
                &_ => {},
            }
        }
        array_size = array_size_opt.unwrap_or((0.95 * memory_limit as f64 * 1073741824f64 / mem::size_of::<CooccurRec>() as f64) as usize);
    }
    std::process::exit(shuffle_by_chunks(array_size, &temp_file));
}
<|endoftext|>


<commit_before>//! General-use structs and methods.

use std::ascii::AsciiExt;
use std::f64;
use std::str;

use {Error, Result};

/// x, y, and z values in one struct.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Triple<T: Copy> {
    /// The x value of the triple.
    pub x: T,
    /// The y value of the triple.
    pub y: T,
    /// The z value of the triple.
    pub z: T,
}

impl<T: Copy> Triple<T> {
    /// Creates a new triple.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::utils::Triple;
    /// let triple = Triple::new(1., 2., 3.);
    /// ```
    pub fn new(x: T, y: T, z: T) -> Triple<T> {
        Triple { x: x, y: y, z: z }
    }
}

/// Three-dimensional bounds.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Bounds<T: Copy> {
    /// The smallest corner of the bounds.
    pub min: Triple<T>,
    /// The largest corner of the bounds.
    pub max: Triple<T>,
}

impl<T: Copy> Bounds<T> {
    /// Creates a new bounds from min and max values.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::utils::Bounds;
    /// let bounds = Bounds::new(1., 2., 3., 4., 5., 6.);
    /// ```
    pub fn new(minx: T, miny: T, minz: T, maxx: T, maxy: T, maxz: T) -> Bounds<T> {
        Bounds {
            min: Triple {
                x: minx,
                y: miny,
                z: minz,
            },
            max: Triple {
                x: maxx,
                y: maxy,
                z: maxz,
            },
        }
    }
}

impl Bounds<f64> {
    /// Grows the bounds to include the point.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::utils::{Triple, Bounds};
    /// let mut bounds = Bounds::new(1., 2., 3., 4., 5., 6.);
    /// let point = Triple { x: 10., y: 11., z: 12. };
    /// bounds.grow(point);
    /// assert_eq!(point, bounds.max);
    /// ```
    pub fn grow(&mut self, triple: Triple<f64>) {
        self.min.x = self.min.x.min(triple.x);
        self.min.y = self.min.y.min(triple.y);
        self.min.z = self.min.z.min(triple.z);
        self.max.x = self.max.x.max(triple.x);
        self.max.y = self.max.y.max(triple.y);
        self.max.z = self.max.z.max(triple.z);
    }
}

impl Default for Bounds<f64> {
    fn default() -> Bounds<f64> {
        Bounds {
            min: Triple {
                x: f64::INFINITY,
                y: f64::INFINITY,
                z: f64::INFINITY,
            },
            max: Triple {
                x: f64::NEG_INFINITY,
                y: f64::NEG_INFINITY,
                z: f64::NEG_INFINITY,
            },
        }
    }
}

/// Converts bytes into a string, following LAS rules.
///
/// LAS specifies that all string fields should be ASCII and nul filled, but not all LAS data in
/// the wild follows these rules (here's looking at you, Riegl). This trait has two methods, one
/// permissive (`to_las_str`) and one strict (`to_las_str_strict`). The first just does it's best
/// to produce some sort of `&str`, while the second checks the rules.
pub trait ToLasStr {
    /// Interprets the bytes as a `&str`, permissively.
    ///
    /// # Examples
    ///
    /// `[u8]` implements `ToLasStr`.
    ///
    /// ```
    /// use las::utils::ToLasStr;
    /// assert_eq!("LiDAR", [76, 105, 68, 65, 82, 0, 33].to_las_str().unwrap());
    /// ```
    fn to_las_str(&self) -> Result<&str>;

    /// Interprets the bytes as a `&str`, enforcing the LAS rules.
    ///
    /// # Examples
    ///
    /// `[u8]` implements `ToLasStr`.
    ///
    /// ```
    /// use las::utils::ToLasStr;
    /// assert!([76, 105, 68, 65, 82, 0, 33].to_las_str_strict().is_err());
    /// ```
    fn to_las_str_strict(&self) -> Result<&str>;
}

impl ToLasStr for [u8] {
    fn to_las_str(&self) -> Result<&str> {
        if let Some(idx) = self.iter().position(|&n| n == 0) {
                str::from_utf8(&self[0..idx])
            } else {
                str::from_utf8(&self)
            }
            .map_err(Error::from)
    }

    fn to_las_str_strict(&self) -> Result<&str> {
        let s = try!(if let Some(idx) = self.iter().position(|&n| n == 0) {
            if self[idx..].iter().all(|&n| n == 0) {
                str::from_utf8(&self[0..idx]).map_err(Error::from)
            } else {
                Err(Error::NotNulFilled(self.to_vec()))
            }
        } else {
            str::from_utf8(&self).map_err(Error::from)
        });
        if s.is_ascii() {
            Ok(s)
        } else {
            Err(Error::NotAscii(s.to_string()))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn bounds_new() {
        let bounds = Bounds::new(1., 2., 3., 4., 5., 6.);
        assert_eq!(1., bounds.min.x);
        assert_eq!(2., bounds.min.y);
        assert_eq!(3., bounds.min.z);
        assert_eq!(4., bounds.max.x);
        assert_eq!(5., bounds.max.y);
        assert_eq!(6., bounds.max.z);
    }

    #[test]
    fn bounds_grow() {
        let mut bounds: Bounds<f64> = Default::default();
        bounds.grow(Triple::new(1., 2., 3.));
        bounds.grow(Triple::new(-1., -2., -3.));
        assert_eq!(Bounds::new(-1., -2., -3., 1., 2., 3.), bounds);
    }

    #[test]
    fn to_las_str_empty() {
        let buf = [0; 0];
        assert_eq!("", buf.to_las_str().unwrap());
        assert_eq!("", buf.to_las_str_strict().unwrap());
    }

    #[test]
    fn to_las_str_one() {
        let buf = [76];
        assert_eq!("L", buf.to_las_str().unwrap());
        assert_eq!("L", buf.to_las_str_strict().unwrap());
    }

    #[test]
    fn to_las_str_not_filled() {
        let buf = [76, 0, 33];
        assert_eq!("L", buf.to_las_str().unwrap());
        assert!(buf.to_las_str_strict().is_err());
    }

    #[test]
    fn to_las_str_unicode() {
        let buf = [240, 159, 146, 150];
        assert_eq!("\u{1f496}", buf.to_las_str().unwrap());
        assert!(buf.to_las_str_strict().is_err());
    }
}
<commit_msg>Add linear transform<commit_after>//! General-use structs and methods.

use std::ascii::AsciiExt;
use std::f64;
use std::str;

use {Error, Result};

/// x, y, and z values in one struct.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Triple<T: Copy> {
    /// The x value of the triple.
    pub x: T,
    /// The y value of the triple.
    pub y: T,
    /// The z value of the triple.
    pub z: T,
}

impl<T: Copy> Triple<T> {
    /// Creates a new triple.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::utils::Triple;
    /// let triple = Triple::new(1., 2., 3.);
    /// ```
    pub fn new(x: T, y: T, z: T) -> Triple<T> {
        Triple { x: x, y: y, z: z }
    }
}

/// Three-dimensional bounds.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Bounds<T: Copy> {
    /// The smallest corner of the bounds.
    pub min: Triple<T>,
    /// The largest corner of the bounds.
    pub max: Triple<T>,
}

impl<T: Copy> Bounds<T> {
    /// Creates a new bounds from min and max values.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::utils::Bounds;
    /// let bounds = Bounds::new(1., 2., 3., 4., 5., 6.);
    /// ```
    pub fn new(minx: T, miny: T, minz: T, maxx: T, maxy: T, maxz: T) -> Bounds<T> {
        Bounds {
            min: Triple {
                x: minx,
                y: miny,
                z: minz,
            },
            max: Triple {
                x: maxx,
                y: maxy,
                z: maxz,
            },
        }
    }
}

impl Bounds<f64> {
    /// Grows the bounds to include the point.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::utils::{Triple, Bounds};
    /// let mut bounds = Bounds::new(1., 2., 3., 4., 5., 6.);
    /// let point = Triple { x: 10., y: 11., z: 12. };
    /// bounds.grow(point);
    /// assert_eq!(point, bounds.max);
    /// ```
    pub fn grow(&mut self, triple: Triple<f64>) {
        self.min.x = self.min.x.min(triple.x);
        self.min.y = self.min.y.min(triple.y);
        self.min.z = self.min.z.min(triple.z);
        self.max.x = self.max.x.max(triple.x);
        self.max.y = self.max.y.max(triple.y);
        self.max.z = self.max.z.max(triple.z);
    }
}

impl Default for Bounds<f64> {
    fn default() -> Bounds<f64> {
        Bounds {
            min: Triple {
                x: f64::INFINITY,
                y: f64::INFINITY,
                z: f64::INFINITY,
            },
            max: Triple {
                x: f64::NEG_INFINITY,
                y: f64::NEG_INFINITY,
                z: f64::NEG_INFINITY,
            },
        }
    }
}

/// Converts bytes into a string, following LAS rules.
///
/// LAS specifies that all string fields should be ASCII and nul filled, but not all LAS data in
/// the wild follows these rules (here's looking at you, Riegl). This trait has two methods, one
/// permissive (`to_las_str`) and one strict (`to_las_str_strict`). The first just does it's best
/// to produce some sort of `&str`, while the second checks the rules.
pub trait ToLasStr {
    /// Interprets the bytes as a `&str`, permissively.
    ///
    /// # Examples
    ///
    /// `[u8]` implements `ToLasStr`.
    ///
    /// ```
    /// use las::utils::ToLasStr;
    /// assert_eq!("LiDAR", [76, 105, 68, 65, 82, 0, 33].to_las_str().unwrap());
    /// ```
    fn to_las_str(&self) -> Result<&str>;

    /// Interprets the bytes as a `&str`, enforcing the LAS rules.
    ///
    /// # Examples
    ///
    /// `[u8]` implements `ToLasStr`.
    ///
    /// ```
    /// use las::utils::ToLasStr;
    /// assert!([76, 105, 68, 65, 82, 0, 33].to_las_str_strict().is_err());
    /// ```
    fn to_las_str_strict(&self) -> Result<&str>;
}

impl ToLasStr for [u8] {
    fn to_las_str(&self) -> Result<&str> {
        if let Some(idx) = self.iter().position(|&n| n == 0) {
                str::from_utf8(&self[0..idx])
            } else {
                str::from_utf8(&self)
            }
            .map_err(Error::from)
    }

    fn to_las_str_strict(&self) -> Result<&str> {
        let s = try!(if let Some(idx) = self.iter().position(|&n| n == 0) {
            if self[idx..].iter().all(|&n| n == 0) {
                str::from_utf8(&self[0..idx]).map_err(Error::from)
            } else {
                Err(Error::NotNulFilled(self.to_vec()))
            }
        } else {
            str::from_utf8(&self).map_err(Error::from)
        });
        if s.is_ascii() {
            Ok(s)
        } else {
            Err(Error::NotAscii(s.to_string()))
        }
    }
}

/// A linear transformation.
///
/// If `y = ax + b`, `a` is the scale and `b` is the offset.
///
/// # Examples
///
/// Linear transforms can be created from `(f64, f64)`:
///
/// ```
/// # use las::utils::LinearTransform;
/// let transform = LinearTransform::from((1., 0.));
/// ```
///
/// The `scale * x + offset` version of the transformation can be computed with `direct`:
///
/// ```
/// # use las::utils::LinearTransform;
/// let transform = LinearTransform::from((2., 1.));
/// assert_eq!(7., transform.direct(3));
/// ```
///
/// The `inverse` computes `(x - offset) / scale`:
///
/// ```
/// # use las::utils::LinearTransform;
/// # let transform = LinearTransform::from((2., 1.));
/// assert_eq!(3, transform.inverse(7.));
/// ```
#[derive(Clone, Copy, Debug)]
pub struct LinearTransform {
    scale: f64,
    offset: f64,
}

impl LinearTransform {
    /// Computes the forward (direct) transformation, `scale * x + offset`.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::utils::LinearTransform;
    /// let transform = LinearTransform::from((2., 1.));
    /// assert_eq!(7., transform.direct(3));
    /// ```
    pub fn direct(&self, n: u32) -> f64 {
        self.scale * n as f64 + self.offset
    }

    /// Computes the backwards (inverse) transformation, `(x - offset) / scale`.
    ///
    /// TODO formalize truncating/rounding.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::utils::LinearTransform;
    /// let transform = LinearTransform::from((2., 1.));
    /// assert_eq!(3, transform.inverse(7.));
    /// ```
    pub fn inverse(&self, n: f64) -> u32 {
        ((n - self.offset) / self.scale) as u32
    }
}

impl From<(f64, f64)> for LinearTransform {
    fn from((scale, offset): (f64, f64)) -> LinearTransform {
        LinearTransform {
            scale: scale,
            offset: offset,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn bounds_new() {
        let bounds = Bounds::new(1., 2., 3., 4., 5., 6.);
        assert_eq!(1., bounds.min.x);
        assert_eq!(2., bounds.min.y);
        assert_eq!(3., bounds.min.z);
        assert_eq!(4., bounds.max.x);
        assert_eq!(5., bounds.max.y);
        assert_eq!(6., bounds.max.z);
    }

    #[test]
    fn bounds_grow() {
        let mut bounds: Bounds<f64> = Default::default();
        bounds.grow(Triple::new(1., 2., 3.));
        bounds.grow(Triple::new(-1., -2., -3.));
        assert_eq!(Bounds::new(-1., -2., -3., 1., 2., 3.), bounds);
    }

    #[test]
    fn to_las_str_empty() {
        let buf = [0; 0];
        assert_eq!("", buf.to_las_str().unwrap());
        assert_eq!("", buf.to_las_str_strict().unwrap());
    }

    #[test]
    fn to_las_str_one() {
        let buf = [76];
        assert_eq!("L", buf.to_las_str().unwrap());
        assert_eq!("L", buf.to_las_str_strict().unwrap());
    }

    #[test]
    fn to_las_str_not_filled() {
        let buf = [76, 0, 33];
        assert_eq!("L", buf.to_las_str().unwrap());
        assert!(buf.to_las_str_strict().is_err());
    }

    #[test]
    fn to_las_str_unicode() {
        let buf = [240, 159, 146, 150];
        assert_eq!("\u{1f496}", buf.to_las_str().unwrap());
        assert!(buf.to_las_str_strict().is_err());
    }

    #[test]
    fn linear_transformation_identity() {
        let transform = LinearTransform::from((1.0, 0.));
        assert_eq!(2., transform.direct(2));
        assert_eq!(2, transform.inverse(2.));
    }

    #[test]
    fn linear_transformation_changes() {
        let transform = LinearTransform::from((2.0, 1.));
        assert_eq!(7., transform.direct(3));
        assert_eq!(3, transform.inverse(7.));
    }
}
<|endoftext|>


<commit_before>use std::ptr;
use std::fs::{create_dir, remove_dir_all, read_dir, remove_file, remove_dir};
use std::fs::{metadata};
use std::path::{Path, PathBuf};
use std::path::Component::Normal;
use std::io::Error as IoError;
use std::io::ErrorKind::AlreadyExists;
use std::ffi::CString;
use std::env::current_dir;
use libc::{c_int, c_char, timeval, c_void, mode_t, uid_t, gid_t};
use libc::{chmod, chdir, chown};

use super::tree_config::Range;
use super::container_config::IdMap;

pub type Time = f64;

extern {
    fn chroot(dir: *const c_char) -> c_int;
    fn pivot_root(new_root: *const c_char, put_old: *const c_char) -> c_int;
    fn gettimeofday(tp: *mut timeval, tzp: *mut c_void) -> c_int;
}

pub fn temporary_change_root<T, F>(path: &Path, fun: F)
    -> Result<T, String>
    where F: Fn() -> Result<T, String>
{
    let cwd = current_dir().unwrap();
    if unsafe { chdir(CString::new("/").unwrap().as_ptr()) } != 0 {
        return Err(format!("Error chdir to root: {}",
                           IoError::last_os_error()));
    }
    if unsafe { chroot(cpath(&path).as_ptr()) } != 0 {
        return Err(format!("Error chroot to {:?}: {}",
                           path, IoError::last_os_error()));
    }
    let res = fun();
    if unsafe { chroot(CString::new(".").unwrap().as_ptr()) } != 0 {
        return Err(format!("Error chroot back: {}",
                           IoError::last_os_error()));
    }
    if unsafe { chdir(cpath(&cwd).as_ptr()) } != 0 {
        return Err(format!("Error chdir to workdir back: {}",
                           IoError::last_os_error()));
    }
    return res;
}

pub fn in_range(ranges: &Vec<Range>, value: u32) -> bool {
    for rng in ranges.iter() {
        if rng.start <= value && rng.end >= value {
            return true;
        }
    }
    return false;
}

pub fn in_mapping(mapping: &Vec<IdMap>, value: u32) -> bool {
    for mp in mapping.iter() {
        if value >= mp.inside && value < mp.inside + mp.count {
            return true;
        }
    }
    return false;
}

pub fn check_mapping(ranges: &Vec<Range>, map: &Vec<IdMap>) -> bool {
    // TODO(tailhook) do more comprehensive algo
    'map: for item in map.iter() {
        for rng in ranges.iter() {
            if rng.start <= item.outside &&
                rng.end >= item.outside + item.count - 1
            {
                continue 'map;
            }
        }
        return false;
    }
    return true;
}

pub fn change_root(new_root: &Path, put_old: &Path) -> Result<(), String>
{
    if unsafe { pivot_root(
            cpath(new_root).as_ptr(),
            cpath(put_old).as_ptr()) } != 0
    {
        return Err(format!("Error pivot_root to {}: {}", new_root.display(),
                           IoError::last_os_error()));
    }
    if unsafe { chdir(CString::new("/").unwrap().as_ptr()) } != 0
    {
        return Err(format!("Error chdir to root: {}",
                           IoError::last_os_error()));
    }
    return Ok(());
}

pub fn ensure_dir(dir: &Path) -> Result<(), String> {
    if let Ok(dmeta) = metadata(dir) {
        if !dmeta.is_dir() {
            return Err(format!(concat!("Can't create dir {:?}, ",
                "path already exists but not a directory"), dir));
        }
        return Ok(());
    }

    match create_dir(dir) {
        Ok(()) => return Ok(()),
        Err(ref e) if e.kind() == AlreadyExists => {
            let dmeta = metadata(dir);
            if dmeta.is_ok() && dmeta.unwrap().is_dir() {
                return Ok(());
            } else {
                return Err(format!(concat!("Can't create dir {:?}, ",
                    "path already exists but not a directory"),
                    dir));
            }
        }
        Err(ref e) => {
            return Err(format!(concat!("Can't create dir {:?}: {} ",
                "path already exists but not a directory"), dir, e));
        }
    }
}

pub fn clean_dir(dir: &Path, remove_dir_itself: bool) -> Result<(), String> {
    if metadata(dir).is_ok() {
        return Ok(());
    }
    // We temporarily change root, so that symlinks inside the dir
    // would do no harm. But note that dir itself can be a symlink
    try!(temporary_change_root(dir, || {
        let dirlist = try!(read_dir("/")
             .map_err(|e| format!("Can't read directory {:?}: {}", dir, e)))
             .filter_map(|x| x.ok())
             .collect::<Vec<_>>();
        for entry in dirlist.into_iter() {
            match metadata(entry.path()) {
                Ok(ref meta) if meta.is_dir() => {
                    try!(remove_dir_all(entry.path())
                        .map_err(|e| format!("Can't remove directory {:?}{:?}: {}",
                            dir, entry.path(), e)));
                }
                Ok(_) => {
                    try!(remove_file(entry.path())
                        .map_err(|e| format!("Can't remove file {:?}{:?}: {}",
                            dir, entry.path(), e)));
                }
                Err(_) => {
                    return Err(format!("Can't stat file {:?}", entry.path()));
                }
            }
        }
        Ok(())
    }));
    if remove_dir_itself {
        try!(remove_dir(dir)
            .map_err(|e| format!("Can't remove dir {:?}: {}", dir, e)));
    }
    return Ok(());
}

pub fn join<S1, S2, I>(mut iter: I, sep: S2) -> String
    where S1:AsRef<str>, S2:AsRef<str>, I:Iterator<Item=S1>
{
    let mut buf = String::new();
    match iter.next() {
        Some(x) => buf.push_str(x.as_ref()),
        None => {}
    }
    for i in iter {
        buf.push_str(sep.as_ref());
        buf.push_str(i.as_ref());
    }
    return buf;
}

pub fn get_time() -> Time {
    let mut tv = timeval { tv_sec: 0, tv_usec: 0 };
    unsafe { gettimeofday(&mut tv, ptr::null_mut()) };
    return tv.tv_sec as f64 +  tv.tv_usec as f64 * 0.000001;
}

pub fn set_file_owner(path: &Path, owner: uid_t, group: gid_t)
    -> Result<(), IoError>
{
    let cpath = cpath(path);
    let rc = unsafe { chown(cpath.as_ptr(), owner, group) };
    if rc < 0 {
        return Err(IoError::last_os_error());
    }
    return Ok(());
}

pub fn set_file_mode(path: &Path, mode: mode_t) -> Result<(), IoError> {
    let cpath = cpath(path);
    let rc = unsafe { chmod(cpath.as_ptr(), mode) };
    if rc < 0 {
        return Err(IoError::last_os_error());
    }
    return Ok(());
}

pub fn cpath<P:AsRef<Path>>(path: P) -> CString {
    CString::new(path.as_ref().to_str().unwrap()).unwrap()
}

pub fn relative(child: &Path, base: &Path) -> PathBuf {
    assert!(child.starts_with(base));
    let mut res = PathBuf::new();
    for cmp in child.components().skip(base.components().count()) {
        if let Normal(ref chunk) = cmp {
            res.push(chunk);
        } else {
            panic!("Bad path for relative ({:?} from {:?} against {:?})",
                cmp, child, base);
        }
    }
    return res
}

pub fn read_yaml_dir(dir: &Path) -> Result<Vec<(String, PathBuf)>, IoError> {
    let mut res = vec!();
    for entry in try!(read_dir(dir)) {
        let entry = try!(entry);
        if let Some(fname) = entry.file_name().as_os_str().to_str() {
            if fname.ends_with(".yaml") && !fname.starts_with(".") {
                res.push((
                    fname[..fname.len()-5].to_string(),
                    entry.path().to_path_buf(),
                    ));
            }
        }
    }
    Ok(res)
}

<commit_msg>Fix `clean_dir` function<commit_after>use std::ptr;
use std::fs::{create_dir, remove_dir_all, read_dir, remove_file, remove_dir};
use std::fs::{metadata};
use std::path::{Path, PathBuf};
use std::path::Component::Normal;
use std::io::Error as IoError;
use std::io::ErrorKind::{AlreadyExists, NotFound};
use std::ffi::CString;
use std::env::current_dir;
use libc::{c_int, c_char, timeval, c_void, mode_t, uid_t, gid_t};
use libc::{chmod, chdir, chown};

use super::tree_config::Range;
use super::container_config::IdMap;

pub type Time = f64;

extern {
    fn chroot(dir: *const c_char) -> c_int;
    fn pivot_root(new_root: *const c_char, put_old: *const c_char) -> c_int;
    fn gettimeofday(tp: *mut timeval, tzp: *mut c_void) -> c_int;
}

pub fn temporary_change_root<T, F>(path: &Path, fun: F)
    -> Result<T, String>
    where F: Fn() -> Result<T, String>
{
    let cwd = current_dir().unwrap();
    if unsafe { chdir(CString::new("/").unwrap().as_ptr()) } != 0 {
        return Err(format!("Error chdir to root: {}",
                           IoError::last_os_error()));
    }
    if unsafe { chroot(cpath(&path).as_ptr()) } != 0 {
        return Err(format!("Error chroot to {:?}: {}",
                           path, IoError::last_os_error()));
    }
    let res = fun();
    if unsafe { chroot(CString::new(".").unwrap().as_ptr()) } != 0 {
        return Err(format!("Error chroot back: {}",
                           IoError::last_os_error()));
    }
    if unsafe { chdir(cpath(&cwd).as_ptr()) } != 0 {
        return Err(format!("Error chdir to workdir back: {}",
                           IoError::last_os_error()));
    }
    return res;
}

pub fn in_range(ranges: &Vec<Range>, value: u32) -> bool {
    for rng in ranges.iter() {
        if rng.start <= value && rng.end >= value {
            return true;
        }
    }
    return false;
}

pub fn in_mapping(mapping: &Vec<IdMap>, value: u32) -> bool {
    for mp in mapping.iter() {
        if value >= mp.inside && value < mp.inside + mp.count {
            return true;
        }
    }
    return false;
}

pub fn check_mapping(ranges: &Vec<Range>, map: &Vec<IdMap>) -> bool {
    // TODO(tailhook) do more comprehensive algo
    'map: for item in map.iter() {
        for rng in ranges.iter() {
            if rng.start <= item.outside &&
                rng.end >= item.outside + item.count - 1
            {
                continue 'map;
            }
        }
        return false;
    }
    return true;
}

pub fn change_root(new_root: &Path, put_old: &Path) -> Result<(), String>
{
    if unsafe { pivot_root(
            cpath(new_root).as_ptr(),
            cpath(put_old).as_ptr()) } != 0
    {
        return Err(format!("Error pivot_root to {}: {}", new_root.display(),
                           IoError::last_os_error()));
    }
    if unsafe { chdir(CString::new("/").unwrap().as_ptr()) } != 0
    {
        return Err(format!("Error chdir to root: {}",
                           IoError::last_os_error()));
    }
    return Ok(());
}

pub fn ensure_dir(dir: &Path) -> Result<(), String> {
    if let Ok(dmeta) = metadata(dir) {
        if !dmeta.is_dir() {
            return Err(format!(concat!("Can't create dir {:?}, ",
                "path already exists but not a directory"), dir));
        }
        return Ok(());
    }

    match create_dir(dir) {
        Ok(()) => return Ok(()),
        Err(ref e) if e.kind() == AlreadyExists => {
            let dmeta = metadata(dir);
            if dmeta.is_ok() && dmeta.unwrap().is_dir() {
                return Ok(());
            } else {
                return Err(format!(concat!("Can't create dir {:?}, ",
                    "path already exists but not a directory"),
                    dir));
            }
        }
        Err(ref e) => {
            return Err(format!(concat!("Can't create dir {:?}: {} ",
                "path already exists but not a directory"), dir, e));
        }
    }
}

pub fn clean_dir(dir: &Path, remove_dir_itself: bool) -> Result<(), String> {
    if let Err(e) = metadata(dir) {
        if e.kind() == NotFound {
            return Ok(());
        } else {
            return Err(format!("Can't stat dir {:?}: {}", dir, e));
        }
    }
    // We temporarily change root, so that symlinks inside the dir
    // would do no harm. But note that dir itself can be a symlink
    try!(temporary_change_root(dir, || {
        let dirlist = try!(read_dir("/")
             .map_err(|e| format!("Can't read directory {:?}: {}", dir, e)))
             .filter_map(|x| x.ok())
             .collect::<Vec<_>>();
        for entry in dirlist.into_iter() {
            match metadata(entry.path()) {
                Ok(ref meta) if meta.is_dir() => {
                    try!(remove_dir_all(entry.path())
                        .map_err(|e| format!("Can't remove directory {:?}{:?}: {}",
                            dir, entry.path(), e)));
                }
                Ok(_) => {
                    try!(remove_file(entry.path())
                        .map_err(|e| format!("Can't remove file {:?}{:?}: {}",
                            dir, entry.path(), e)));
                }
                Err(_) => {
                    return Err(format!("Can't stat file {:?}", entry.path()));
                }
            }
        }
        Ok(())
    }));
    if remove_dir_itself {
        try!(remove_dir(dir)
            .map_err(|e| format!("Can't remove dir {:?}: {}", dir, e)));
    }
    return Ok(());
}

pub fn join<S1, S2, I>(mut iter: I, sep: S2) -> String
    where S1:AsRef<str>, S2:AsRef<str>, I:Iterator<Item=S1>
{
    let mut buf = String::new();
    match iter.next() {
        Some(x) => buf.push_str(x.as_ref()),
        None => {}
    }
    for i in iter {
        buf.push_str(sep.as_ref());
        buf.push_str(i.as_ref());
    }
    return buf;
}

pub fn get_time() -> Time {
    let mut tv = timeval { tv_sec: 0, tv_usec: 0 };
    unsafe { gettimeofday(&mut tv, ptr::null_mut()) };
    return tv.tv_sec as f64 +  tv.tv_usec as f64 * 0.000001;
}

pub fn set_file_owner(path: &Path, owner: uid_t, group: gid_t)
    -> Result<(), IoError>
{
    let cpath = cpath(path);
    let rc = unsafe { chown(cpath.as_ptr(), owner, group) };
    if rc < 0 {
        return Err(IoError::last_os_error());
    }
    return Ok(());
}

pub fn set_file_mode(path: &Path, mode: mode_t) -> Result<(), IoError> {
    let cpath = cpath(path);
    let rc = unsafe { chmod(cpath.as_ptr(), mode) };
    if rc < 0 {
        return Err(IoError::last_os_error());
    }
    return Ok(());
}

pub fn cpath<P:AsRef<Path>>(path: P) -> CString {
    CString::new(path.as_ref().to_str().unwrap()).unwrap()
}

pub fn relative(child: &Path, base: &Path) -> PathBuf {
    assert!(child.starts_with(base));
    let mut res = PathBuf::new();
    for cmp in child.components().skip(base.components().count()) {
        if let Normal(ref chunk) = cmp {
            res.push(chunk);
        } else {
            panic!("Bad path for relative ({:?} from {:?} against {:?})",
                cmp, child, base);
        }
    }
    return res
}

pub fn read_yaml_dir(dir: &Path) -> Result<Vec<(String, PathBuf)>, IoError> {
    let mut res = vec!();
    for entry in try!(read_dir(dir)) {
        let entry = try!(entry);
        if let Some(fname) = entry.file_name().as_os_str().to_str() {
            if fname.ends_with(".yaml") && !fname.starts_with(".") {
                res.push((
                    fname[..fname.len()-5].to_string(),
                    entry.path().to_path_buf(),
                    ));
            }
        }
    }
    Ok(res)
}

<|endoftext|>


<commit_before>use std::{mem};

use num::{BigInt, One, Signed};
use num::bigint::{Sign};

use deserializer::{DeserializationError, DeserializationResult};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ObjectIdentifier {
    pub parts: Vec<u32>
}

impl ObjectIdentifier {
    pub fn new(oid: Vec<u32>) -> Option<ObjectIdentifier> {
        if oid.len() < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
            return None;
        }

        return Some(ObjectIdentifier{
            parts: oid,
        });
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BitString {
    pub data: Vec<u8>,
    pub bit_length: usize,
}

impl BitString {
    pub fn new(data: Vec<u8>, bit_length: usize) -> Option<BitString> {
        match (data.len(), bit_length) {
            (0, 0) => (),
            (_, 0) | (0, _) => return None,
            (i, j) if (i * 8 < j) || (i - 1) * 8 > j => return None,
            _ => (),
        }

        return Some(BitString {
            data: data,
            bit_length: bit_length,
        });
    }
}

fn _int_length(v: i64) -> usize {
    let mut num_bytes = 1;
    let mut i = v;

    while i > 127 || i < -128 {
        num_bytes += 1;
        i >>= 8;
    }
    return num_bytes;
}

pub trait Integer: Sized {
    fn encode(&self) -> Vec<u8>;
    fn decode(&[u8]) -> DeserializationResult<Self>;
}

macro_rules! primitive_integer {
    ($Int:ident) => {
        impl Integer for $Int {
            fn encode(&self) -> Vec<u8> {
                let n = _int_length(*self as i64);
                let mut result = Vec::with_capacity(n);
                for i in (1..n+1).rev() {
                    result.push((self >> ((i - 1) * 8)) as u8);
                }
                return result;
            }

            fn decode(data: &[u8]) -> DeserializationResult<$Int> {
                if data.len() > mem::size_of::<$Int>() {
                    return Err(DeserializationError::IntegerOverflow);
                } else if data.is_empty() {
                    return Err(DeserializationError::InvalidValue);
                }

                let mut ret = 0;
                for b in data.iter() {
                    ret <<= 8;
                    ret |= *b as i64;
                }
                // Shift up and down in order to sign extend the result.
                ret <<= 64 - data.len() * 8;
                ret >>= 64 - data.len() * 8;
                return Ok(ret as $Int);
            }
        }
    }
}

primitive_integer!(i8);
primitive_integer!(i32);
primitive_integer!(i64);

impl Integer for BigInt {
    fn encode(&self) -> Vec<u8> {
        if self.is_positive() {
            let (_, mut bytes) = self.to_bytes_be();
            if bytes[0] & 0x80 == 0x80 {
                // If the data has a leading 0x80, pad with a zero-byte.
                bytes.insert(0, 0);
            }
            return bytes;
        } else if self.is_negative() {
            // Convert negative numbers to two's-complement by subtracting one and inverting.
            let n_minus_1 = -self - BigInt::one();
            let (_, mut bytes) = n_minus_1.to_bytes_be();

            for i in 0..bytes.len() {
                bytes[i] ^= 0xff;
            }

            if bytes[0] & 0x80 == 0 {
                bytes.insert(0, 0xff);
            }
            return bytes;
        } else {
            return b"\x00".to_vec();
        }
    }

    fn decode(data: &[u8]) -> DeserializationResult<BigInt> {
        if data.is_empty() {
            return Err(DeserializationError::InvalidValue);
        }

        if data[0] & 0x80 == 0x80 {
            let inverse_bytes = data.iter().map(|b| !b).collect::<Vec<u8>>();
            let n_minus_1 = BigInt::from_bytes_be(Sign::Plus, &inverse_bytes[..]);
            return Ok(-(n_minus_1 + BigInt::one()));
        } else {
            return Ok(BigInt::from_bytes_be(Sign::Plus, &data[..]));
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{BitString, ObjectIdentifier};

    #[test]
    fn test_object_identifier_new() {
        assert!(ObjectIdentifier::new(vec![]).is_none());
        assert!(ObjectIdentifier::new(vec![3, 10]).is_none());
        assert!(ObjectIdentifier::new(vec![1, 50]).is_none());
    }

    #[test]
    fn test_bit_string_new() {
        assert!(BitString::new(b"".to_vec(), 1).is_none());
        assert!(BitString::new(b"\x00".to_vec(), 0).is_none());
        assert!(BitString::new(b"\x00".to_vec(), 9).is_none());
    }
}
<commit_msg>added the from_bytes alternate constructor<commit_after>use std::{mem};

use num::{BigInt, One, Signed};
use num::bigint::{Sign};

use deserializer::{DeserializationError, DeserializationResult};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ObjectIdentifier {
    pub parts: Vec<u32>
}

impl ObjectIdentifier {
    pub fn new(oid: Vec<u32>) -> Option<ObjectIdentifier> {
        if oid.len() < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
            return None;
        }

        return Some(ObjectIdentifier{
            parts: oid,
        });
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BitString {
    pub data: Vec<u8>,
    pub bit_length: usize,
}

impl BitString {
    pub fn from_bytes(data: Vec<u8>) -> Option<BitString> {
        let length = data.len() * 8;
        return BitString::new(data, length);
    }

    pub fn new(data: Vec<u8>, bit_length: usize) -> Option<BitString> {
        match (data.len(), bit_length) {
            (0, 0) => (),
            (_, 0) | (0, _) => return None,
            (i, j) if (i * 8 < j) || (i - 1) * 8 > j => return None,
            _ => (),
        }

        return Some(BitString {
            data: data,
            bit_length: bit_length,
        });
    }
}

fn _int_length(v: i64) -> usize {
    let mut num_bytes = 1;
    let mut i = v;

    while i > 127 || i < -128 {
        num_bytes += 1;
        i >>= 8;
    }
    return num_bytes;
}

pub trait Integer: Sized {
    fn encode(&self) -> Vec<u8>;
    fn decode(&[u8]) -> DeserializationResult<Self>;
}

macro_rules! primitive_integer {
    ($Int:ident) => {
        impl Integer for $Int {
            fn encode(&self) -> Vec<u8> {
                let n = _int_length(*self as i64);
                let mut result = Vec::with_capacity(n);
                for i in (1..n+1).rev() {
                    result.push((self >> ((i - 1) * 8)) as u8);
                }
                return result;
            }

            fn decode(data: &[u8]) -> DeserializationResult<$Int> {
                if data.len() > mem::size_of::<$Int>() {
                    return Err(DeserializationError::IntegerOverflow);
                } else if data.is_empty() {
                    return Err(DeserializationError::InvalidValue);
                }

                let mut ret = 0;
                for b in data.iter() {
                    ret <<= 8;
                    ret |= *b as i64;
                }
                // Shift up and down in order to sign extend the result.
                ret <<= 64 - data.len() * 8;
                ret >>= 64 - data.len() * 8;
                return Ok(ret as $Int);
            }
        }
    }
}

primitive_integer!(i8);
primitive_integer!(i32);
primitive_integer!(i64);

impl Integer for BigInt {
    fn encode(&self) -> Vec<u8> {
        if self.is_positive() {
            let (_, mut bytes) = self.to_bytes_be();
            if bytes[0] & 0x80 == 0x80 {
                // If the data has a leading 0x80, pad with a zero-byte.
                bytes.insert(0, 0);
            }
            return bytes;
        } else if self.is_negative() {
            // Convert negative numbers to two's-complement by subtracting one and inverting.
            let n_minus_1 = -self - BigInt::one();
            let (_, mut bytes) = n_minus_1.to_bytes_be();

            for i in 0..bytes.len() {
                bytes[i] ^= 0xff;
            }

            if bytes[0] & 0x80 == 0 {
                bytes.insert(0, 0xff);
            }
            return bytes;
        } else {
            return b"\x00".to_vec();
        }
    }

    fn decode(data: &[u8]) -> DeserializationResult<BigInt> {
        if data.is_empty() {
            return Err(DeserializationError::InvalidValue);
        }

        if data[0] & 0x80 == 0x80 {
            let inverse_bytes = data.iter().map(|b| !b).collect::<Vec<u8>>();
            let n_minus_1 = BigInt::from_bytes_be(Sign::Plus, &inverse_bytes[..]);
            return Ok(-(n_minus_1 + BigInt::one()));
        } else {
            return Ok(BigInt::from_bytes_be(Sign::Plus, &data[..]));
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{BitString, ObjectIdentifier};

    #[test]
    fn test_object_identifier_new() {
        assert!(ObjectIdentifier::new(vec![]).is_none());
        assert!(ObjectIdentifier::new(vec![3, 10]).is_none());
        assert!(ObjectIdentifier::new(vec![1, 50]).is_none());
    }

    #[test]
    fn test_bit_string_new() {
        assert!(BitString::new(b"".to_vec(), 1).is_none());
        assert!(BitString::new(b"\x00".to_vec(), 0).is_none());
        assert!(BitString::new(b"\x00".to_vec(), 9).is_none());
    }

    #[test]
    fn test_bit_string_from_bytes() {
        assert_eq!(BitString::from_bytes(b"".to_vec()), BitString::new(b"".to_vec(), 0));
        assert_eq!(BitString::from_bytes(b"abc".to_vec()), BitString::new(b"abc".to_vec(), 24));
    }
}
<|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/. */

<%! from data import to_rust_ident %>
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_SIZES, PHYSICAL_SIDES, LOGICAL_SIDES %>

<% data.new_style_struct("Position", inherited=False) %>

// "top" / "left" / "bottom" / "right"
% for side in PHYSICAL_SIDES:
    ${helpers.predefined_type(side, "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
                              animatable=True)}
% endfor
// offset-* logical properties, map to "top" / "left" / "bottom" / "right"
% for side in LOGICAL_SIDES:
    ${helpers.predefined_type("offset-%s" % side, "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              spec="https://drafts.csswg.org/css-logical-props/#propdef-offset-%s" % side,
                              animatable=True, logical=True)}
% endfor

<%helpers:longhand name="z-index" spec="https://www.w3.org/TR/CSS2/visuren.html#z-index" animatable="True">
    use values::HasViewportPercentage;
    use values::computed::ComputedValueAsSpecified;

    impl ComputedValueAsSpecified for SpecifiedValue {}
    no_viewport_percentage!(SpecifiedValue);
    pub type SpecifiedValue = computed_value::T;
    pub mod computed_value {
        use std::fmt;
        use style_traits::ToCss;

        #[derive(PartialEq, Clone, Eq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub enum T {
            Auto,
            Number(i32),
        }

        impl ToCss for T {
            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                match *self {
                    T::Auto => dest.write_str("auto"),
                    T::Number(number) => write!(dest, "{}", number),
                }
            }
        }

        impl T {
            pub fn number_or_zero(self) -> i32 {
                match self {
                    T::Auto => 0,
                    T::Number(value) => value,
                }
            }
        }
    }
    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T::Auto
    }
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
            Ok(computed_value::T::Auto)
        } else {
            specified::parse_integer(input).map(computed_value::T::Number)
        }
    }
</%helpers:longhand>

// CSS Flexible Box Layout Module Level 1
// http://www.w3.org/TR/css3-flexbox/

// Flex container properties
${helpers.single_keyword("flex-direction", "row row-reverse column column-reverse",
                         spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property",
                         extra_prefixes="webkit", animatable=False)}

${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse",
                         spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property",
                         extra_prefixes="webkit", animatable=False)}

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword("justify-content", "stretch flex-start flex-end center space-between space-around",
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="justify-content",
                              type="AlignJustifyContent",
                              initial_value="specified::AlignJustifyContent::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
                              extra_prefixes="webkit",
                              animatable=False)}
% endif

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword("align-content", "stretch flex-start flex-end center space-between space-around",
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-align/#propdef-align-content",
                             animatable=False)}

    ${helpers.single_keyword("align-items",
                             "stretch flex-start flex-end center baseline",
                             need_clone=True,
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-flexbox/#align-items-property",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="align-content",
                              type="AlignJustifyContent",
                              initial_value="specified::AlignJustifyContent::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-align-content",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="align-items",
                              type="AlignItems",
                              initial_value="specified::AlignItems::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-align-items",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="justify-items",
                              type="JustifyItems",
                              initial_value="specified::JustifyItems::auto()",
                              spec="https://drafts.csswg.org/css-align/#propdef-justify-items",
                              animatable=False)}
% endif

// Flex item properties
${helpers.predefined_type("flex-grow", "Number",
                          "0.0", "parse_non_negative",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property",
                          extra_prefixes="webkit",
                          needs_context=False,
                          animatable=True)}

${helpers.predefined_type("flex-shrink", "Number",
                          "1.0", "parse_non_negative",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property",
                          extra_prefixes="webkit",
                          needs_context=False,
                          animatable=True)}

// https://drafts.csswg.org/css-align/#align-self-property
% if product == "servo":
    // FIXME: Update Servo to support the same syntax as Gecko.
    ${helpers.single_keyword("align-self", "auto stretch flex-start flex-end center baseline",
                             need_clone=True,
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="align-self",
                              type="AlignJustifySelf",
                              initial_value="specified::AlignJustifySelf::auto()",
                              spec="https://drafts.csswg.org/css-align/#align-self-property",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="justify-self",
                              type="AlignJustifySelf",
                              initial_value="specified::AlignJustifySelf::auto()",
                              spec="https://drafts.csswg.org/css-align/#justify-self-property",
                              animatable=False)}
% endif

// https://drafts.csswg.org/css-flexbox/#propdef-order
<%helpers:longhand name="order" animatable="True" extra_prefixes="webkit"
                   spec="https://drafts.csswg.org/css-flexbox/#order-property">
    use values::computed::ComputedValueAsSpecified;

    impl ComputedValueAsSpecified for SpecifiedValue {}

    pub type SpecifiedValue = computed_value::T;

    pub mod computed_value {
        pub type T = i32;
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        0
    }

    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        specified::parse_integer(input)
    }
</%helpers:longhand>

// FIXME: This property should be animatable.
${helpers.predefined_type("flex-basis",
                          "LengthOrPercentageOrAutoOrContent",
                          "computed::LengthOrPercentageOrAutoOrContent::Auto",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
                          extra_prefixes="webkit",
                          animatable=False)}

% for (size, logical) in ALL_SIZES:
    <%
      spec = "https://drafts.csswg.org/css-box/#propdef-%s"
      if logical:
        spec = "https://drafts.csswg.org/css-logical-props/#propdef-%s"
    %>
    // width, height, block-size, inline-size
    ${helpers.predefined_type("%s" % size,
                              "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              "parse_non_negative",
                              needs_context=False,
                              spec=spec % size,
                              animatable=True, logical = logical)}

    % if product == "gecko":
        // min-width, min-height, min-block-size, min-inline-size
        ${helpers.predefined_type("min-%s" % size,
                                  "MinLength",
                                  "computed::MinLength::LengthOrPercentage(" +
                                  "computed::LengthOrPercentage::Length(Au(0)))",
                                  spec=spec % ("min-%s" % size),
                                  animatable=True, logical = logical)}
    % else:
        ${helpers.predefined_type("min-%s" % size,
                                  "LengthOrPercentage",
                                  "computed::LengthOrPercentage::Length(Au(0))",
                                  "parse_non_negative",
                                  needs_context=False,
                                  spec=spec % ("min-%s" % size),
                                  animatable=True, logical = logical)}
    % endif

    // max-width, max-height, max-block-size, max-inline-size
    % if product == "gecko":
        ${helpers.predefined_type("max-%s" % size,
                                  "MaxLength",
                                  "computed::MaxLength::None",
                                  spec=spec % ("max-%s" % size),
                                  animatable=True, logical = logical)}
    % else:
        ${helpers.predefined_type("max-%s" % size,
                                  "LengthOrPercentageOrNone",
                                  "computed::LengthOrPercentageOrNone::None",
                                  "parse_non_negative",
                                  needs_context=False,
                                  spec=spec % ("max-%s" % size),
                                  animatable=True, logical = logical)}
    % endif
% endfor

${helpers.single_keyword("box-sizing",
                         "content-box border-box",
                         extra_prefixes="moz webkit",
                         spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing",
                         animatable=False)}

${helpers.single_keyword("object-fit", "fill contain cover none scale-down",
                         products="gecko", animatable=False,
                         spec="https://drafts.csswg.org/css-images/#propdef-object-fit")}

${helpers.predefined_type("object-position",
                          "Position",
                          "computed::Position::zero()",
                          products="gecko",
                          boxed="True",
                          spec="https://drafts.csswg.org/css-images-3/#the-object-position",
                          animatable=True)}

% for kind in ["row", "column"]:
    ${helpers.predefined_type("grid-%s-gap" % kind,
                              "LengthOrPercentage",
                              "computed::LengthOrPercentage::Length(Au(0))",
                              spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-gap" % kind,
                              animatable=True,
                              products="gecko")}

    % for range in ["start", "end"]:
        ${helpers.predefined_type("grid-%s-%s" % (kind, range),
                                  "GridLine",
                                  "Default::default()",
                                  animatable=False,
                                  spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range),
                                  products="gecko",
                                  boxed=True)}
    % endfor

    // NOTE: According to the spec, this should handle multiple values of `<track-size>`,
    // but gecko supports only a single value
    ${helpers.predefined_type("grid-auto-%ss" % kind,
                              "TrackSize",
                              "Default::default()",
                              animatable=False,
                              spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
                              products="gecko",
                              boxed=True)}
% endfor
<commit_msg>Disallow keyword values in min/max-size properties in the block direction<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/. */

<%! from data import to_rust_ident %>
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_SIZES, PHYSICAL_SIDES, LOGICAL_SIDES %>

<% data.new_style_struct("Position", inherited=False) %>

// "top" / "left" / "bottom" / "right"
% for side in PHYSICAL_SIDES:
    ${helpers.predefined_type(side, "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
                              animatable=True)}
% endfor
// offset-* logical properties, map to "top" / "left" / "bottom" / "right"
% for side in LOGICAL_SIDES:
    ${helpers.predefined_type("offset-%s" % side, "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              spec="https://drafts.csswg.org/css-logical-props/#propdef-offset-%s" % side,
                              animatable=True, logical=True)}
% endfor

<%helpers:longhand name="z-index" spec="https://www.w3.org/TR/CSS2/visuren.html#z-index" animatable="True">
    use values::HasViewportPercentage;
    use values::computed::ComputedValueAsSpecified;

    impl ComputedValueAsSpecified for SpecifiedValue {}
    no_viewport_percentage!(SpecifiedValue);
    pub type SpecifiedValue = computed_value::T;
    pub mod computed_value {
        use std::fmt;
        use style_traits::ToCss;

        #[derive(PartialEq, Clone, Eq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub enum T {
            Auto,
            Number(i32),
        }

        impl ToCss for T {
            fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                match *self {
                    T::Auto => dest.write_str("auto"),
                    T::Number(number) => write!(dest, "{}", number),
                }
            }
        }

        impl T {
            pub fn number_or_zero(self) -> i32 {
                match self {
                    T::Auto => 0,
                    T::Number(value) => value,
                }
            }
        }
    }
    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T::Auto
    }
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
            Ok(computed_value::T::Auto)
        } else {
            specified::parse_integer(input).map(computed_value::T::Number)
        }
    }
</%helpers:longhand>

// CSS Flexible Box Layout Module Level 1
// http://www.w3.org/TR/css3-flexbox/

// Flex container properties
${helpers.single_keyword("flex-direction", "row row-reverse column column-reverse",
                         spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property",
                         extra_prefixes="webkit", animatable=False)}

${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse",
                         spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property",
                         extra_prefixes="webkit", animatable=False)}

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword("justify-content", "stretch flex-start flex-end center space-between space-around",
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="justify-content",
                              type="AlignJustifyContent",
                              initial_value="specified::AlignJustifyContent::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
                              extra_prefixes="webkit",
                              animatable=False)}
% endif

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword("align-content", "stretch flex-start flex-end center space-between space-around",
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-align/#propdef-align-content",
                             animatable=False)}

    ${helpers.single_keyword("align-items",
                             "stretch flex-start flex-end center baseline",
                             need_clone=True,
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-flexbox/#align-items-property",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="align-content",
                              type="AlignJustifyContent",
                              initial_value="specified::AlignJustifyContent::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-align-content",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="align-items",
                              type="AlignItems",
                              initial_value="specified::AlignItems::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-align-items",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="justify-items",
                              type="JustifyItems",
                              initial_value="specified::JustifyItems::auto()",
                              spec="https://drafts.csswg.org/css-align/#propdef-justify-items",
                              animatable=False)}
% endif

// Flex item properties
${helpers.predefined_type("flex-grow", "Number",
                          "0.0", "parse_non_negative",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property",
                          extra_prefixes="webkit",
                          needs_context=False,
                          animatable=True)}

${helpers.predefined_type("flex-shrink", "Number",
                          "1.0", "parse_non_negative",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property",
                          extra_prefixes="webkit",
                          needs_context=False,
                          animatable=True)}

// https://drafts.csswg.org/css-align/#align-self-property
% if product == "servo":
    // FIXME: Update Servo to support the same syntax as Gecko.
    ${helpers.single_keyword("align-self", "auto stretch flex-start flex-end center baseline",
                             need_clone=True,
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="align-self",
                              type="AlignJustifySelf",
                              initial_value="specified::AlignJustifySelf::auto()",
                              spec="https://drafts.csswg.org/css-align/#align-self-property",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="justify-self",
                              type="AlignJustifySelf",
                              initial_value="specified::AlignJustifySelf::auto()",
                              spec="https://drafts.csswg.org/css-align/#justify-self-property",
                              animatable=False)}
% endif

// https://drafts.csswg.org/css-flexbox/#propdef-order
<%helpers:longhand name="order" animatable="True" extra_prefixes="webkit"
                   spec="https://drafts.csswg.org/css-flexbox/#order-property">
    use values::computed::ComputedValueAsSpecified;

    impl ComputedValueAsSpecified for SpecifiedValue {}

    pub type SpecifiedValue = computed_value::T;

    pub mod computed_value {
        pub type T = i32;
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        0
    }

    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        specified::parse_integer(input)
    }
</%helpers:longhand>

// FIXME: This property should be animatable.
${helpers.predefined_type("flex-basis",
                          "LengthOrPercentageOrAutoOrContent",
                          "computed::LengthOrPercentageOrAutoOrContent::Auto",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
                          extra_prefixes="webkit",
                          animatable=False)}

% for (size, logical) in ALL_SIZES:
    <%
      spec = "https://drafts.csswg.org/css-box/#propdef-%s"
      if logical:
        spec = "https://drafts.csswg.org/css-logical-props/#propdef-%s"
    %>
    // width, height, block-size, inline-size
    ${helpers.predefined_type("%s" % size,
                              "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              "parse_non_negative",
                              needs_context=False,
                              spec=spec % size,
                              animatable=True, logical = logical)}
    % if product == "gecko":
        % for min_max in ["min", "max"]:
            <%
                MinMax = min_max.title()
                initial = "None" if "max" == min_max else "Auto"
            %>

            // min-width, min-height, min-block-size, min-inline-size,
            // max-width, max-height, max-block-size, max-inline-size
            //
            // Keyword values are only valid in the inline direction; they must
            // be replaced with auto/none in block.
            <%helpers:longhand name="${min_max}-${size}" spec="${spec % ('%s-%s' % (min_max, size))}"
                               animatable="True" logical="${logical}" predefined_type="${MinMax}Length">

                use std::fmt;
                use style_traits::ToCss;
                use values::HasViewportPercentage;
                use values::specified::${MinMax}Length;

                impl HasViewportPercentage for SpecifiedValue {
                    fn has_viewport_percentage(&self) -> bool {
                        self.0.has_viewport_percentage()
                    }
                }

                pub mod computed_value {
                    pub type T = ::values::computed::${MinMax}Length;
                }

                #[derive(PartialEq, Clone, Debug)]
                #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
                pub struct SpecifiedValue(${MinMax}Length);

                #[inline]
                pub fn get_initial_value() -> computed_value::T {
                    use values::computed::${MinMax}Length;
                    ${MinMax}Length::${initial}
                }
                fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
                    ${MinMax}Length::parse(context, input).map(SpecifiedValue)
                }

                impl ToCss for SpecifiedValue {
                    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                        self.0.to_css(dest)
                    }
                }

                impl ToComputedValue for SpecifiedValue {
                    type ComputedValue = computed_value::T;
                    #[inline]
                    fn to_computed_value(&self, context: &Context) -> computed_value::T {
                        use values::computed::${MinMax}Length;
                        let computed = self.0.to_computed_value(context);

                        // filter out keyword values in the block direction
                        % if logical:
                            % if "block" in size:
                                if let ${MinMax}Length::ExtremumLength(..) = computed {
                                    return get_initial_value()
                                }
                            % endif
                        % else:
                            if let ${MinMax}Length::ExtremumLength(..) = computed {
                                <% is_height = "true" if "height" in size else "false" %>
                                if ${is_height} != context.style().writing_mode.is_vertical() {
                                    return get_initial_value()
                                }
                            }
                        % endif
                        computed
                    }

                    #[inline]
                    fn from_computed_value(computed: &computed_value::T) -> Self {
                        SpecifiedValue(ToComputedValue::from_computed_value(computed))
                    }
                }
            </%helpers:longhand>
        % endfor
    % else:
        // servo versions (no keyword support)
        ${helpers.predefined_type("min-%s" % size,
                                  "LengthOrPercentage",
                                  "computed::LengthOrPercentage::Length(Au(0))",
                                  "parse_non_negative",
                                  needs_context=False,
                                  spec=spec % ("min-%s" % size),
                                  animatable=True, logical = logical)}
        ${helpers.predefined_type("max-%s" % size,
                                  "LengthOrPercentageOrNone",
                                  "computed::LengthOrPercentageOrNone::None",
                                  "parse_non_negative",
                                  needs_context=False,
                                  spec=spec % ("min-%s" % size),
                                  animatable=True, logical = logical)}
    % endif
% endfor

${helpers.single_keyword("box-sizing",
                         "content-box border-box",
                         extra_prefixes="moz webkit",
                         spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing",
                         animatable=False)}

${helpers.single_keyword("object-fit", "fill contain cover none scale-down",
                         products="gecko", animatable=False,
                         spec="https://drafts.csswg.org/css-images/#propdef-object-fit")}

${helpers.predefined_type("object-position",
                          "Position",
                          "computed::Position::zero()",
                          products="gecko",
                          boxed="True",
                          spec="https://drafts.csswg.org/css-images-3/#the-object-position",
                          animatable=True)}

% for kind in ["row", "column"]:
    ${helpers.predefined_type("grid-%s-gap" % kind,
                              "LengthOrPercentage",
                              "computed::LengthOrPercentage::Length(Au(0))",
                              spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-gap" % kind,
                              animatable=True,
                              products="gecko")}

    % for range in ["start", "end"]:
        ${helpers.predefined_type("grid-%s-%s" % (kind, range),
                                  "GridLine",
                                  "Default::default()",
                                  animatable=False,
                                  spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range),
                                  products="gecko",
                                  boxed=True)}
    % endfor

    // NOTE: According to the spec, this should handle multiple values of `<track-size>`,
    // but gecko supports only a single value
    ${helpers.predefined_type("grid-auto-%ss" % kind,
                              "TrackSize",
                              "Default::default()",
                              animatable=False,
                              spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
                              products="gecko",
                              boxed=True)}
% endfor
<|endoftext|>