<commit_before>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//!
//! glTF JSON, buffers, and images may come from a range of external sources, so
//! customization is an important design goal of the import module. The `Source`
//! trait is provided to facilitate customization of the data loading process.
//!
//! For convenience, the library contains one implementation of the `Source` trait,
//! namely `FromPath`, which allows loading from file system and also from embedded
//! base64 encoded data. This implementation may be used as reference for other
//! schemes such as `http`.

extern crate gltf;

use gltf::json::{self, validation};
use std::{fmt, fs, io};

use gltf::{Gltf, Loaded};
use std::error::Error as StdError;
use std::path::{Path, PathBuf};

/// Contains data structures for import configuration.
pub mod config;

pub use self::config::Config;

/// Error encountered when importing a glTF 2.0 asset.
#[derive(Debug)]
pub enum Error {
    /// A glTF extension required by the asset has not been enabled by the user.
    ExtensionDisabled(String),

    /// A glTF extension required by the asset is not supported by the library.
    ExtensionUnsupported(String),

    /// The glTF version of the asset is incompatible with the importer.
    IncompatibleVersion(String),

    /// Standard I/O error.
    Io(std::io::Error),

    /// Failure when parsing a .glb file.
    MalformedGlb(String),

    /// Failure when deserializing .gltf or .glb JSON.
    MalformedJson(json::Error),

    /// The .gltf data is invalid.
    Validation(Vec<(json::Path, validation::Error)>),
}

/// A `Future` that drives the importation of glTF.
#[derive(Debug)]
pub struct Importer {
    path: PathBuf,
    data: Vec<u8>,
    gltf: Option<Gltf>,
    buffers: Vec<Vec<u8>>,
    config: Config,
}

impl gltf::Source for Importer {
    fn source_buffer(&self, buffer: &gltf::Buffer) -> &[u8] {
        &self.buffers[buffer.index()]
    }
}

/// The contents of a .glb file.
#[derive(Clone, Debug)]
struct Glb<'a>(&'a [u8]);

/// The header section of a .glb file.
#[derive(Copy, Clone, Debug)]
#[repr(C)]
struct Header {
    /// Must be `b"glTF"`.
    magic: [u8; 4],

    /// Must be `2`.
    version: u32,

    /// Must match the length of the parent .glb file.
    length: u32,
}

/// The header of the JSON or BIN section of a .glb file.
#[derive(Copy, Clone, Debug)]
#[repr(C)]
struct ChunkInfo {
    /// The length of the chunk data in byte excluding the header.
    length: u32,

    /// Either `b"JSON"` or `b"BIN\0"`.
    kind: [u8; 4],

    // Data follows...
}

/// Represents a subset of `Importer::glb`.
#[derive(Copy, Clone, Debug, Default)]
struct Slice {
    /// Offset into `glb` in bytes.
    offset: usize,

    /// Length of the slice in bytes.
    length: usize,
}

/// The header, JSON, and BIN sections of a .glb file, respectively.
type Split = (Header, Slice, Option<Slice>);

impl<'a> Glb<'a> {
    /// Obtains a slice of the GLB data.
    fn slice(&self, slice: Slice) -> &[u8] {
        let begin = slice.offset;
        let end = begin + slice.length;
        &self.0[begin..end]
    }

    /// Splits loaded GLB into its three chunks.
    ///
    /// * Mandatory GLB header.
    /// * Mandatory JSON chunk.
    /// * Optional BIN chunk.
    fn split(&self) -> Result<Split, Error> {
        use std::mem::{size_of, transmute};
        let err = |reason: &str| Err(Error::MalformedGlb(reason.to_string()));
        let glb = &self.0;
        let glb_ptr = glb.as_ptr();
        if glb.len() < size_of::<Header>() + size_of::<ChunkInfo>() {
            return err("GLB missing header");
        }

        // Offset in bytes into `glb`.
        let mut offset = 0isize;

        let header = unsafe {
            let x: *const Header = transmute(glb_ptr.offset(offset));
            if &(*x).magic != b"glTF" {
                return err("GLB does not start with `glTF`");
            }
            if (*x).length as usize != glb.len() {
                return err("length does not match length of data");
            }
            if (*x).version != 2 {
                return err("Not GLB version 2.0");
            }
            *x
        };

        offset += size_of::<Header>() as isize;
        let json_chunk = unsafe {
            let x: *const ChunkInfo = transmute(glb_ptr.offset(offset));
            offset += size_of::<ChunkInfo>() as isize;
            if (*x).length as usize > (glb.len() as isize - offset) as usize {
                return err("JSON chunkLength exceeds length of data");
            }
            if &(*x).kind != b"JSON" {
                return err("JSON chunkType is not `JSON`");
            }
            Slice {
                offset: offset as usize,
                length: (*x).length as usize,
            }
        };

        offset += json_chunk.length as isize;
        let blob_chunk = if glb.len() as isize - offset == 0 {
            None
        } else {
            unsafe {
                let x: *const ChunkInfo = transmute(glb_ptr.offset(offset));
                offset += size_of::<ChunkInfo>() as isize;
                if (*x).length as usize > (glb.len() as isize - offset) as usize {
                    return err("BIN chunkLength exceeds length of data");
                }
                if &(*x).kind != b"BIN\0" {
                    return err("BIN chunkType is not `BIN\0`");
                }
                Some(Slice {
                    offset: offset as usize,
                    length: (*x).length as usize,
                })
            }
        };

        Ok((header, json_chunk, blob_chunk))
    }
}

fn read_to_end_impl(path: &Path) -> Result<Vec<u8>, Error> {
    use io::Read;
    let file = fs::File::open(path)?;
    let mut reader = io::BufReader::new(file);
    let mut buffer = vec![];
    let _ = reader.read_to_end(&mut buffer)?;
    Ok(buffer)
}

fn read_to_end<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
    read_to_end_impl(path.as_ref())
}

fn load_external_buffers(
    base_path: &Path,
    root: &gltf::root::Root,
    is_glb: bool,
) -> Result<Vec<Vec<u8>>, Error> {
    let mut iter = root.as_json().buffers.iter();
    if is_glb {
        let _ = iter.next();
    }
    iter
        .map(|buffer| {
            let uri = buffer.uri.as_ref().unwrap();
            let path = base_path.parent().unwrap_or(Path::new("./")).join(uri);
            let data = read_to_end(&path)?;
            Ok(data)
        })
        .collect()
}

fn validate_standard(json: &gltf::json::Root, config: &Config) -> Result<(), Error> {
    use config::ValidationStrategy;
    use gltf::json::validation::Validate;
    match config.validation_strategy {
        ValidationStrategy::Skip => Ok(()),
        ValidationStrategy::Minimal => {
            let mut errs = vec![];
            json.validate_minimally(
                &json,
                || json::Path::new(),
                &mut |path, err| errs.push((path(), err)),
            );
            if errs.is_empty() {
                Ok(())
            } else {
                Err(Error::Validation(errs))
            }
        },
        ValidationStrategy::Complete => {
            let mut errs = vec![];
            json.validate_completely(
                &json,
                || json::Path::new(),
                &mut |path, err| errs.push((path(), err)),
            );
            if errs.is_empty() {
                Ok(())
            } else {
                Err(Error::Validation(errs))
            }
        },
    }
}

fn validate_binary(
    json: &gltf::json::Root,
    config: &Config,
    has_blob: bool,
) -> Result<(), Error> {
    use self::json::validation::Error as Reason;
    let mut errs = vec![];
    let _ = validate_standard(json, config)?;
    if config.validation_strategy == config::ValidationStrategy::Skip {
        return Ok(());
    }

    // Required for the `Minimal` and `Complete` validation strategies.
    for (index, buffer) in json.buffers.iter().enumerate() {
        let path = || {
            json::Path::new()
                .field("buffers")
                .index(index)
                .field("uri")
        };
        match index {
            0 if has_blob => if buffer.uri.is_some() {
                errs.push((path(), Reason::Missing));
            },
            _ if buffer.uri.is_none() => {
                errs.push((path(), Reason::Missing));
            },
            _ => {},
        }
    }

    if errs.is_empty() {
        Ok(())
    } else {
        Err(Error::Validation(errs))
    }
}

fn import_standard<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Vec<Vec<u8>>), Error> {
    let json: gltf::json::Root = gltf::json::from_slice(data)?;
    let _ = validate_standard(&json, &config);
    let root = gltf::root::Root::new(json);
    let is_glb = false;
    let buffers = load_external_buffers(base_path, &root, is_glb)?;
    Ok((Gltf::new(root), buffers))
}

fn import_binary<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Vec<Vec<u8>>), Error> {
    let glb = Glb(data);
    let (_, json_chunk, blob_chunk) = glb.split()?;
    let begin = json_chunk.offset;
    let end = begin + json_chunk.length;
    let json: gltf::json::Root = gltf::json::from_slice(&glb.0[begin..end])?;
    let blob = blob_chunk.map(|chunk| glb.slice(chunk).to_vec());
    let has_blob = blob.is_some();
    let _ = validate_binary(&json, &config, has_blob)?;
    let root = gltf::root::Root::new(json);
    let mut buffers = vec![];
    if let Some(buffer) = blob {
        buffers.push(buffer);
    }
    let is_glb = true;
    for buffer in load_external_buffers(base_path, &root, is_glb)? {
        buffers.push(buffer);
    }
    Ok((Gltf::new(root), buffers))
}

impl Importer {
    /// Constructs an `Importer`.
    pub fn new<P: Into<PathBuf>>(path: P) -> Self {
        Self {
            path: path.into(),
            data: vec![],
            gltf: None,
            buffers: vec![],
            config: Default::default(),
        }
    }

    /// Imports the `glTF`.
    pub fn import(&mut self) -> Result<Loaded<&Gltf>, Error> {
        self.data = read_to_end(&self.path)?;
        let (gltf, buffers) = if self.data.starts_with(b"glTF") {
            import_binary(&self.data, &self.config, &self.path)
        } else {
            import_standard(&self.data, &self.config, &self.path)
        }?;
        self.buffers = buffers;
        self.gltf = Some(gltf);
        Ok(self.gltf.as_ref().unwrap().loaded(self))
    }

    /// Returns the path to the glTF.
    pub fn path(&self) -> &Path {
        &self.path
    }
}

impl From<json::Error> for Error {
    fn from(err: json::Error) -> Error {
        Error::MalformedJson(err)
    }
}

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

impl From<Vec<(json::Path, validation::Error)>> for Error {
    fn from(errs: Vec<(json::Path, validation::Error)>) -> Error {
        Error::Validation(errs)
    }
}

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

impl StdError for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match self {
            &ExtensionDisabled(_) => "Asset requires a disabled extension",
            &ExtensionUnsupported(_) => "Assets requires an unsupported extension",
            &IncompatibleVersion(_) => "Asset is not glTF version 2.0",
            &Io(_) => "I/O error",
            &MalformedGlb(_) => "Malformed .glb file",
            &MalformedJson(_) => "Malformed .gltf / .glb JSON",
            &Validation(_) => "Asset failed validation tests",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        use self::Error::*;
        match self {
            &MalformedJson(ref err) => Some(err),
            &Io(ref err) => Some(err),
            _ => None,
        }
    }
}
<commit_msg>Fix load_external_buffers for binary glTF<commit_after>
// Copyright 2017 The gltf Library Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//!
//! glTF JSON, buffers, and images may come from a range of external sources, so
//! customization is an important design goal of the import module. The `Source`
//! trait is provided to facilitate customization of the data loading process.
//!
//! For convenience, the library contains one implementation of the `Source` trait,
//! namely `FromPath`, which allows loading from file system and also from embedded
//! base64 encoded data. This implementation may be used as reference for other
//! schemes such as `http`.

extern crate gltf;

use gltf::json::{self, validation};
use std::{fmt, fs, io};

use gltf::{Gltf, Loaded};
use std::error::Error as StdError;
use std::path::{Path, PathBuf};

/// Contains data structures for import configuration.
pub mod config;

pub use self::config::Config;

/// Error encountered when importing a glTF 2.0 asset.
#[derive(Debug)]
pub enum Error {
    /// A glTF extension required by the asset has not been enabled by the user.
    ExtensionDisabled(String),

    /// A glTF extension required by the asset is not supported by the library.
    ExtensionUnsupported(String),

    /// The glTF version of the asset is incompatible with the importer.
    IncompatibleVersion(String),

    /// Standard I/O error.
    Io(std::io::Error),

    /// Failure when parsing a .glb file.
    MalformedGlb(String),

    /// Failure when deserializing .gltf or .glb JSON.
    MalformedJson(json::Error),

    /// The .gltf data is invalid.
    Validation(Vec<(json::Path, validation::Error)>),
}

/// A `Future` that drives the importation of glTF.
#[derive(Debug)]
pub struct Importer {
    path: PathBuf,
    data: Vec<u8>,
    gltf: Option<Gltf>,
    buffers: Vec<Vec<u8>>,
    config: Config,
}

impl gltf::Source for Importer {
    fn source_buffer(&self, buffer: &gltf::Buffer) -> &[u8] {
        &self.buffers[buffer.index()]
    }
}

/// The contents of a .glb file.
#[derive(Clone, Debug)]
struct Glb<'a>(&'a [u8]);

/// The header section of a .glb file.
#[derive(Copy, Clone, Debug)]
#[repr(C)]
struct Header {
    /// Must be `b"glTF"`.
    magic: [u8; 4],

    /// Must be `2`.
    version: u32,

    /// Must match the length of the parent .glb file.
    length: u32,
}

/// The header of the JSON or BIN section of a .glb file.
#[derive(Copy, Clone, Debug)]
#[repr(C)]
struct ChunkInfo {
    /// The length of the chunk data in byte excluding the header.
    length: u32,

    /// Either `b"JSON"` or `b"BIN\0"`.
    kind: [u8; 4],

    // Data follows...
}

/// Represents a subset of `Importer::glb`.
#[derive(Copy, Clone, Debug, Default)]
struct Slice {
    /// Offset into `glb` in bytes.
    offset: usize,

    /// Length of the slice in bytes.
    length: usize,
}

/// The header, JSON, and BIN sections of a .glb file, respectively.
type Split = (Header, Slice, Option<Slice>);

impl<'a> Glb<'a> {
    /// Obtains a slice of the GLB data.
    fn slice(&self, slice: Slice) -> &[u8] {
        let begin = slice.offset;
        let end = begin + slice.length;
        &self.0[begin..end]
    }

    /// Splits loaded GLB into its three chunks.
    ///
    /// * Mandatory GLB header.
    /// * Mandatory JSON chunk.
    /// * Optional BIN chunk.
    fn split(&self) -> Result<Split, Error> {
        use std::mem::{size_of, transmute};
        let err = |reason: &str| Err(Error::MalformedGlb(reason.to_string()));
        let glb = &self.0;
        let glb_ptr = glb.as_ptr();
        if glb.len() < size_of::<Header>() + size_of::<ChunkInfo>() {
            return err("GLB missing header");
        }

        // Offset in bytes into `glb`.
        let mut offset = 0isize;

        let header = unsafe {
            let x: *const Header = transmute(glb_ptr.offset(offset));
            if &(*x).magic != b"glTF" {
                return err("GLB does not start with `glTF`");
            }
            if (*x).length as usize != glb.len() {
                return err("length does not match length of data");
            }
            if (*x).version != 2 {
                return err("Not GLB version 2.0");
            }
            *x
        };

        offset += size_of::<Header>() as isize;
        let json_chunk = unsafe {
            let x: *const ChunkInfo = transmute(glb_ptr.offset(offset));
            offset += size_of::<ChunkInfo>() as isize;
            if (*x).length as usize > (glb.len() as isize - offset) as usize {
                return err("JSON chunkLength exceeds length of data");
            }
            if &(*x).kind != b"JSON" {
                return err("JSON chunkType is not `JSON`");
            }
            Slice {
                offset: offset as usize,
                length: (*x).length as usize,
            }
        };

        offset += json_chunk.length as isize;
        let blob_chunk = if glb.len() as isize - offset == 0 {
            None
        } else {
            unsafe {
                let x: *const ChunkInfo = transmute(glb_ptr.offset(offset));
                offset += size_of::<ChunkInfo>() as isize;
                if (*x).length as usize > (glb.len() as isize - offset) as usize {
                    return err("BIN chunkLength exceeds length of data");
                }
                if &(*x).kind != b"BIN\0" {
                    return err("BIN chunkType is not `BIN\0`");
                }
                Some(Slice {
                    offset: offset as usize,
                    length: (*x).length as usize,
                })
            }
        };

        Ok((header, json_chunk, blob_chunk))
    }
}

fn read_to_end_impl(path: &Path) -> Result<Vec<u8>, Error> {
    use io::Read;
    let file = fs::File::open(path)?;
    let mut reader = io::BufReader::new(file);
    let mut buffer = vec![];
    let _ = reader.read_to_end(&mut buffer)?;
    Ok(buffer)
}

fn read_to_end<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
    read_to_end_impl(path.as_ref())
}

fn load_external_buffers(
    base_path: &Path,
    root: &gltf::root::Root,
    has_blob: bool,
) -> Result<Vec<Vec<u8>>, Error> {
    let mut iter = root.as_json().buffers.iter();
    if has_blob {
        let _ = iter.next();
    }
    iter
        .map(|buffer| {
            let uri = buffer.uri.as_ref().unwrap();
            let path = base_path.parent().unwrap_or(Path::new("./")).join(uri);
            let data = read_to_end(&path)?;
            Ok(data)
        })
        .collect()
}

fn validate_standard(json: &gltf::json::Root, config: &Config) -> Result<(), Error> {
    use config::ValidationStrategy;
    use gltf::json::validation::Validate;
    match config.validation_strategy {
        ValidationStrategy::Skip => Ok(()),
        ValidationStrategy::Minimal => {
            let mut errs = vec![];
            json.validate_minimally(
                &json,
                || json::Path::new(),
                &mut |path, err| errs.push((path(), err)),
            );
            if errs.is_empty() {
                Ok(())
            } else {
                Err(Error::Validation(errs))
            }
        },
        ValidationStrategy::Complete => {
            let mut errs = vec![];
            json.validate_completely(
                &json,
                || json::Path::new(),
                &mut |path, err| errs.push((path(), err)),
            );
            if errs.is_empty() {
                Ok(())
            } else {
                Err(Error::Validation(errs))
            }
        },
    }
}

fn validate_binary(
    json: &gltf::json::Root,
    config: &Config,
    has_blob: bool,
) -> Result<(), Error> {
    use self::json::validation::Error as Reason;
    let mut errs = vec![];
    let _ = validate_standard(json, config)?;
    if config.validation_strategy == config::ValidationStrategy::Skip {
        return Ok(());
    }

    // Required for the `Minimal` and `Complete` validation strategies.
    for (index, buffer) in json.buffers.iter().enumerate() {
        let path = || {
            json::Path::new()
                .field("buffers")
                .index(index)
                .field("uri")
        };
        match index {
            0 if has_blob => if buffer.uri.is_some() {
                errs.push((path(), Reason::Missing));
            },
            _ if buffer.uri.is_none() => {
                errs.push((path(), Reason::Missing));
            },
            _ => {},
        }
    }

    if errs.is_empty() {
        Ok(())
    } else {
        Err(Error::Validation(errs))
    }
}

fn import_standard<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Vec<Vec<u8>>), Error> {
    let json: gltf::json::Root = gltf::json::from_slice(data)?;
    let _ = validate_standard(&json, &config);
    let root = gltf::root::Root::new(json);
    let has_blob = false;
    let buffers = load_external_buffers(base_path, &root, has_blob)?;
    Ok((Gltf::new(root), buffers))
}

fn import_binary<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Vec<Vec<u8>>), Error> {
    let glb = Glb(data);
    let (_, json_chunk, blob_chunk) = glb.split()?;
    let begin = json_chunk.offset;
    let end = begin + json_chunk.length;
    let json: gltf::json::Root = gltf::json::from_slice(&glb.0[begin..end])?;
    let blob = blob_chunk.map(|chunk| glb.slice(chunk).to_vec());
    let has_blob = blob.is_some();
    let _ = validate_binary(&json, &config, has_blob)?;
    let root = gltf::root::Root::new(json);
    let mut buffers = vec![];
    if let Some(buffer) = blob {
        buffers.push(buffer);
    }
    for buffer in load_external_buffers(base_path, &root, has_blob)? {
        buffers.push(buffer);
    }
    Ok((Gltf::new(root), buffers))
}

impl Importer {
    /// Constructs an `Importer`.
    pub fn new<P: Into<PathBuf>>(path: P) -> Self {
        Self {
            path: path.into(),
            data: vec![],
            gltf: None,
            buffers: vec![],
            config: Default::default(),
        }
    }

    /// Imports the `glTF`.
    pub fn import(&mut self) -> Result<Loaded<&Gltf>, Error> {
        self.data = read_to_end(&self.path)?;
        let (gltf, buffers) = if self.data.starts_with(b"glTF") {
            import_binary(&self.data, &self.config, &self.path)
        } else {
            import_standard(&self.data, &self.config, &self.path)
        }?;
        self.buffers = buffers;
        self.gltf = Some(gltf);
        Ok(self.gltf.as_ref().unwrap().loaded(self))
    }

    /// Returns the path to the glTF.
    pub fn path(&self) -> &Path {
        &self.path
    }
}

impl From<json::Error> for Error {
    fn from(err: json::Error) -> Error {
        Error::MalformedJson(err)
    }
}

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

impl From<Vec<(json::Path, validation::Error)>> for Error {
    fn from(errs: Vec<(json::Path, validation::Error)>) -> Error {
        Error::Validation(errs)
    }
}

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

impl StdError for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match self {
            &ExtensionDisabled(_) => "Asset requires a disabled extension",
            &ExtensionUnsupported(_) => "Assets requires an unsupported extension",
            &IncompatibleVersion(_) => "Asset is not glTF version 2.0",
            &Io(_) => "I/O error",
            &MalformedGlb(_) => "Malformed .glb file",
            &MalformedJson(_) => "Malformed .gltf / .glb JSON",
            &Validation(_) => "Asset failed validation tests",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        use self::Error::*;
        match self {
            &MalformedJson(ref err) => Some(err),
            &Io(ref err) => Some(err),
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>//! The reference loader implementation for the `gltf` crate.
//!
//! # Examples
//!
//! ### Importing some `glTF` 2.0
//!
//! ```rust
//! use gltf_importer::import;
//! # #[allow(unused_variables)]
//! let path = "path/to/asset.gltf";
//! # let path = "../examples/Box.gltf";
//! match import(path) {
//!     Ok((gltf, _)) => println!("{:#?}", gltf.as_json()),
//!     Err(err) => println!("error: {:?}", err),
//! }
//! ```

extern crate base64;
extern crate gltf;
extern crate gltf_utils;

use gltf::json::{self, validation};
use std::{fmt, fs, io};

use gltf::Gltf;
use gltf_utils::Source;
use std::error::Error as StdError;
use std::path::Path;

/// Contains parameters for import configuration.
pub mod config;

pub use self::config::Config;
pub use self::config::ValidationStrategy;

/// Error encountered when importing a glTF 2.0 asset.
#[derive(Debug)]
pub enum Error {
    /// A loaded glTF buffer is not of the required length.
    BufferLength(json::Path),

    /// Base 64 decoding error.
    Base64Decoding(base64::DecodeError),

    /// A glTF extension required by the asset has not been enabled by the user.
    ExtensionDisabled(String),

    /// A glTF extension required by the asset is not supported by the library.
    ExtensionUnsupported(String),

    /// The glTF version of the asset is incompatible with the importer.
    IncompatibleVersion(String),

    /// Standard I/O error.
    Io(std::io::Error),

    /// `gltf` crate error.
    Gltf(gltf::Error),

    /// Failure when deserializing .gltf or .glb JSON.
    MalformedJson(json::Error),

    /// The .gltf data is invalid.
    Validation(Vec<(json::Path, validation::Error)>),
}

/// Buffer data returned from `import`.
#[derive(Clone, Debug)]
pub struct Buffers(Vec<Vec<u8>>);

impl Source for Buffers {
    fn source_buffer(&self, buffer: &gltf::Buffer) -> &[u8] {
        &self.0[buffer.index()]
    }
}

impl Buffers {
    /// Obtain the contents of a loaded buffer.
    pub fn buffer(&self, buffer: &gltf::Buffer) -> Option<&[u8]> {
        self.0.get(buffer.index()).map(Vec::as_slice)
    }

    /// Obtain the contents of a loaded buffer view.
    pub fn view(&self, view: &gltf::buffer::View) -> Option<&[u8]> {
        self.buffer(&view.buffer())
            .map(|data| {
                let begin = view.offset();
                let end = begin + view.length();
                &data[begin..end]
            })
    }

    /// Take the loaded buffer data.
    pub fn take(self) -> Vec<Vec<u8>> {
        self.0
    }
}

fn import_impl(path: &Path, config: &Config) -> Result<(Gltf, Buffers), Error> {
    let data = read_to_end(path)?;
    import_data_slice(&data, path, config)
}

pub fn import_data_slice<'a>(data: &'a [u8], path: &Path, config: Config) -> Result<(Gltf, Buffers), Error> {
    if data.starts_with(b"glTF") {
        import_binary(&data, config, path)
    } else {
        import_standard(&data, config, path)
    }
}

/// Imports glTF 2.0 with custom configuration.
pub fn import_with_config<P>(path: P, config: &Config) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), config)
}

/// Imports glTF 2.0 with default configuration.
pub fn import<P>(path: P) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), &Default::default())
}

fn read_to_end_impl(path: &Path) -> Result<Vec<u8>, Error> {
    use io::Read;
    let file = fs::File::open(path)?;
    let mut reader = io::BufReader::new(file);
    let mut buffer = vec![];
    let _ = reader.read_to_end(&mut buffer)?;
    Ok(buffer)
}

fn read_to_end<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
    read_to_end_impl(path.as_ref())
}

fn parse_data_uri(uri: &str) -> Result<Vec<u8>, Error> {
    let encoded = uri.split(',').nth(1).unwrap();
    let decoded = base64::decode(&encoded)?;
    Ok(decoded)
}

fn load_external_buffers(
    base_path: &Path,
    gltf: &Gltf,
    mut bin: Option<Vec<u8>>,
) -> Result<Vec<Vec<u8>>, Error> {
    let mut buffers = vec![];
    for (index, buffer) in gltf.buffers().enumerate() {
        let uri = buffer.uri();
        let data = if uri == "#bin" {
            Ok(bin.take().unwrap())
        } else if uri.starts_with("data:") {
            Ok(parse_data_uri(uri)?)
        } else {
            let path = base_path.parent().unwrap_or_else(|| Path::new("./")).join(uri);
            read_to_end(&path)
        }?;
        if data.len() < buffer.length() {
            let path = json::Path::new().field("buffers").index(index);
            return Err(Error::BufferLength(path));
        }
        buffers.push(data);
    }
    Ok(buffers)
}

fn validate_standard(
    unvalidated: gltf::Unvalidated,
    config: &Config,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    Ok(match config.validation_strategy {
        ValidationStrategy::Skip => unvalidated.skip_validation(),
        ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
        ValidationStrategy::Complete => unvalidated.validate_completely()?,
    })
}

fn validate_binary(
    unvalidated: gltf::Unvalidated,
    config: &Config,
    has_bin: bool,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    use json::validation::Error as Reason;

    if config.validation_strategy == ValidationStrategy::Skip {
        return Ok(unvalidated.skip_validation());
    }

    let mut errs = vec![];
    {
        let json = unvalidated.as_json();
        for (index, buffer) in json.buffers.iter().enumerate() {
            let path = || {
                json::Path::new()
                    .field("buffers")
                    .index(index)
                    .field("uri")
            };
            match index {
                0 if has_bin => if buffer.uri.is_some() {
                    errs.push((path(), Reason::Missing));
                },
                _ if buffer.uri.is_none() => {
                    errs.push((path(), Reason::Missing));
                },
                _ => {},
            }
        }
    }

    if errs.is_empty() {
        Ok(match config.validation_strategy {
            ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
            ValidationStrategy::Complete => unvalidated.validate_completely()?,
            ValidationStrategy::Skip => unreachable!(),
        })
    } else {
        Err(Error::Validation(errs))
    }
}

fn import_standard<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let unvalidated = Gltf::from_slice(data)?;
    let gltf = validate_standard(unvalidated, config)?;
    let bin = None;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

fn import_binary<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let gltf::Glb { json, bin, .. } = gltf::Glb::from_slice(data)?;
    let unvalidated = Gltf::from_slice(json)?;
    let bin = bin.map(|x| x.to_vec());
    let gltf = validate_binary(unvalidated, config, bin.is_some())?;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

impl From<json::Error> for Error {
    fn from(err: json::Error) -> Error {
        Error::MalformedJson(err)
    }
}

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

impl From<Vec<(json::Path, validation::Error)>> for Error {
    fn from(errs: Vec<(json::Path, validation::Error)>) -> Error {
        Error::Validation(errs)
    }
}

impl From<gltf::Error> for Error {
    fn from(err: gltf::Error) -> Error {
        match err {
            gltf::Error::Validation(errs) => Error::Validation(errs),
            _ => Error::Gltf(err),
        }
    }
}

impl From<base64::DecodeError> for Error {
    fn from(err: base64::DecodeError) -> Error {
        Error::Base64Decoding(err)
    }
}

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

impl StdError for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            Base64Decoding(_) => "Base 64 decoding failed",
            BufferLength(_) => "Loaded buffer does not match required length",
            ExtensionDisabled(_) => "Asset requires a disabled extension",
            ExtensionUnsupported(_) => "Assets requires an unsupported extension",
            IncompatibleVersion(_) => "Asset is not glTF version 2.0",
            Io(_) => "I/O error",
            Gltf(_) => "Error from gltf crate",
            MalformedJson(_) => "Malformed .gltf / .glb JSON",
            Validation(_) => "Asset failed validation tests",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        use self::Error::*;
        match *self {
            MalformedJson(ref err) => Some(err),
            Io(ref err) => Some(err),
            _ => None,
        }
    }
}
<commit_msg>Add FileNotFound error kind<commit_after>//! The reference loader implementation for the `gltf` crate.
//!
//! # Examples
//!
//! ### Importing some `glTF` 2.0
//!
//! ```rust
//! use gltf_importer::import;
//! # #[allow(unused_variables)]
//! let path = "path/to/asset.gltf";
//! # let path = "../examples/Box.gltf";
//! match import(path) {
//!     Ok((gltf, _)) => println!("{:#?}", gltf.as_json()),
//!     Err(err) => println!("error: {:?}", err),
//! }
//! ```

extern crate base64;
extern crate gltf;
extern crate gltf_utils;

use gltf::json::{self, validation};
use std::{fmt, fs, io, path};

use gltf::Gltf;
use gltf_utils::Source;
use std::error::Error as StdError;
use std::path::Path;

/// Contains parameters for import configuration.
pub mod config;

pub use self::config::Config;
pub use self::config::ValidationStrategy;

/// Error encountered when importing a glTF 2.0 asset.
#[derive(Debug)]
pub enum Error {
    /// A loaded glTF buffer is not of the required length.
    BufferLength(json::Path),

    /// Base 64 decoding error.
    Base64Decoding(base64::DecodeError),

    /// A glTF extension required by the asset has not been enabled by the user.
    ExtensionDisabled(String),

    /// A glTF extension required by the asset is not supported by the library.
    ExtensionUnsupported(String),

    /// File not found.
    FileNotFound(path::PathBuf),

    /// The glTF version of the asset is incompatible with the importer.
    IncompatibleVersion(String),

    /// Standard I/O error.
    Io(io::Error),

    /// `gltf` crate error.
    Gltf(gltf::Error),

    /// Failure when deserializing .gltf or .glb JSON.
    MalformedJson(json::Error),

    /// The .gltf data is invalid.
    Validation(Vec<(json::Path, validation::Error)>),
}

/// Buffer data returned from `import`.
#[derive(Clone, Debug)]
pub struct Buffers(Vec<Vec<u8>>);

impl Source for Buffers {
    fn source_buffer(&self, buffer: &gltf::Buffer) -> &[u8] {
        &self.0[buffer.index()]
    }
}

impl Buffers {
    /// Obtain the contents of a loaded buffer.
    pub fn buffer(&self, buffer: &gltf::Buffer) -> Option<&[u8]> {
        self.0.get(buffer.index()).map(Vec::as_slice)
    }

    /// Obtain the contents of a loaded buffer view.
    pub fn view(&self, view: &gltf::buffer::View) -> Option<&[u8]> {
        self.buffer(&view.buffer())
            .map(|data| {
                let begin = view.offset();
                let end = begin + view.length();
                &data[begin..end]
            })
    }

    /// Take the loaded buffer data.
    pub fn take(self) -> Vec<Vec<u8>> {
        self.0
    }
}

fn import_impl(path: &Path, config: &Config) -> Result<(Gltf, Buffers), Error> {
    let data = read_to_end(path)?;
    import_data_slice(&data, path, config)
}

pub fn import_data_slice<'a>(data: &'a [u8], path: &Path, config: Config) -> Result<(Gltf, Buffers), Error> {
    if data.starts_with(b"glTF") {
        import_binary(&data, config, path)
    } else {
        import_standard(&data, config, path)
    }
}

/// Imports glTF 2.0 with custom configuration.
pub fn import_with_config<P>(path: P, config: &Config) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), config)
}

/// Imports glTF 2.0 with default configuration.
pub fn import<P>(path: P) -> Result<(Gltf, Buffers), Error>
    where P: AsRef<Path>
{
    import_impl(path.as_ref(), &Default::default())
}

fn read_to_end_impl(path: &Path) -> Result<Vec<u8>, Error> {
    use io::Read;
    if path.exists() {
        let file = fs::File::open(path)?;
        let mut reader = io::BufReader::new(file);
        let mut buffer = vec![];
        let _ = reader.read_to_end(&mut buffer)?;
        Ok(buffer)
    } else {
        Err(Error::FileNotFound(path.to_path_buf()))
    }
}

fn read_to_end<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
    read_to_end_impl(path.as_ref())
}

fn parse_data_uri(uri: &str) -> Result<Vec<u8>, Error> {
    let encoded = uri.split(',').nth(1).unwrap();
    let decoded = base64::decode(&encoded)?;
    Ok(decoded)
}

fn load_external_buffers(
    base_path: &Path,
    gltf: &Gltf,
    mut bin: Option<Vec<u8>>,
) -> Result<Vec<Vec<u8>>, Error> {
    let mut buffers = vec![];
    for (index, buffer) in gltf.buffers().enumerate() {
        let uri = buffer.uri();
        let data = if uri == "#bin" {
            Ok(bin.take().unwrap())
        } else if uri.starts_with("data:") {
            Ok(parse_data_uri(uri)?)
        } else {
            let path = base_path.parent().unwrap_or_else(|| Path::new("./")).join(uri);
            read_to_end(&path)
        }?;
        if data.len() < buffer.length() {
            let path = json::Path::new().field("buffers").index(index);
            return Err(Error::BufferLength(path));
        }
        buffers.push(data);
    }
    Ok(buffers)
}

fn validate_standard(
    unvalidated: gltf::Unvalidated,
    config: &Config,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    Ok(match config.validation_strategy {
        ValidationStrategy::Skip => unvalidated.skip_validation(),
        ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
        ValidationStrategy::Complete => unvalidated.validate_completely()?,
    })
}

fn validate_binary(
    unvalidated: gltf::Unvalidated,
    config: &Config,
    has_bin: bool,
) -> Result<Gltf, Error> {
    use config::ValidationStrategy;
    use json::validation::Error as Reason;

    if config.validation_strategy == ValidationStrategy::Skip {
        return Ok(unvalidated.skip_validation());
    }

    let mut errs = vec![];
    {
        let json = unvalidated.as_json();
        for (index, buffer) in json.buffers.iter().enumerate() {
            let path = || {
                json::Path::new()
                    .field("buffers")
                    .index(index)
                    .field("uri")
            };
            match index {
                0 if has_bin => if buffer.uri.is_some() {
                    errs.push((path(), Reason::Missing));
                },
                _ if buffer.uri.is_none() => {
                    errs.push((path(), Reason::Missing));
                },
                _ => {},
            }
        }
    }

    if errs.is_empty() {
        Ok(match config.validation_strategy {
            ValidationStrategy::Minimal => unvalidated.validate_minimally()?,
            ValidationStrategy::Complete => unvalidated.validate_completely()?,
            ValidationStrategy::Skip => unreachable!(),
        })
    } else {
        Err(Error::Validation(errs))
    }
}

fn import_standard<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let unvalidated = Gltf::from_slice(data)?;
    let gltf = validate_standard(unvalidated, config)?;
    let bin = None;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

fn import_binary<'a>(
    data: &'a [u8],
    config: &Config,
    base_path: &Path,
) -> Result<(Gltf, Buffers), Error> {
    let gltf::Glb { json, bin, .. } = gltf::Glb::from_slice(data)?;
    let unvalidated = Gltf::from_slice(json)?;
    let bin = bin.map(|x| x.to_vec());
    let gltf = validate_binary(unvalidated, config, bin.is_some())?;
    let mut buffers = Buffers(vec![]);
    for buffer in load_external_buffers(base_path, &gltf, bin)? {
        buffers.0.push(buffer);
    }
    Ok((gltf, buffers))
}

impl From<json::Error> for Error {
    fn from(err: json::Error) -> Error {
        Error::MalformedJson(err)
    }
}

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

impl From<Vec<(json::Path, validation::Error)>> for Error {
    fn from(errs: Vec<(json::Path, validation::Error)>) -> Error {
        Error::Validation(errs)
    }
}

impl From<gltf::Error> for Error {
    fn from(err: gltf::Error) -> Error {
        match err {
            gltf::Error::Validation(errs) => Error::Validation(errs),
            _ => Error::Gltf(err),
        }
    }
}

impl From<base64::DecodeError> for Error {
    fn from(err: base64::DecodeError) -> Error {
        Error::Base64Decoding(err)
    }
}

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

impl StdError for Error {
    fn description(&self) -> &str {
        use self::Error::*;
        match *self {
            Base64Decoding(_) => "base 64 decoding failed",
            BufferLength(_) => "buffer does not match required length",
            ExtensionDisabled(_) => "asset requires a disabled extension",
            ExtensionUnsupported(_) => "asset requires an unsupported extension",
            FileNotFound(_) => "file not found",
            IncompatibleVersion(_) => "asset is not glTF version 2.0",
            Io(_) => "io error",
            Gltf(_) => "error from gltf crate",
            MalformedJson(_) => "malformed .gltf / .glb JSON",
            Validation(_) => "asset failed validation tests",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        use self::Error::*;
        match *self {
            MalformedJson(ref err) => Some(err),
            Io(ref err) => Some(err),
            _ => None,
        }
    }
}
<|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/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    build.no_default_flags(true);
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP").flag("/clang:-undef");
    } else {
        build.flag("-xc").flag("-P").flag("-undef");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    // SWGL relies heavily on inlining for performance so override -Oz with -O2
    if build.get_compiler().args().contains(&std::ffi::OsString::from("-Oz")) {
        build.flag("-O2");
    }

    build.file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .flag("-ffast-math") // most GLSL compilers assume something similar
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<commit_msg>Backed out changeset b4c24acb1025 (bug 1700717) for causing wrench bustages.<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/. */

extern crate cc;
extern crate glsl_to_cxx;
extern crate webrender_build;

use std::collections::HashSet;
use std::fmt::Write;
use webrender_build::shader::{ShaderFeatureFlags, get_shader_features};

// Shader key is in "name feature,feature" format.
// File name needs to be formatted as "name_feature_feature".
fn shader_file(shader_key: &str) -> String {
    shader_key.replace(' ', "_").replace(',', "_")
}

fn write_load_shader(shader_keys: &[String]) {
    let mut load_shader = String::new();
    for s in shader_keys {
        let _ = write!(load_shader, "#include \"{}.h\"\n", shader_file(s));
    }
    load_shader.push_str("ProgramLoader load_shader(const char* name) {\n");
    for s in shader_keys {
        let _ = write!(load_shader, "  if (!strcmp(name, \"{}\")) {{ return {}_program::loader; }}\n",
                       s, shader_file(s));
    }
    load_shader.push_str("  return nullptr;\n}\n");
    std::fs::write(std::env::var("OUT_DIR").unwrap() + "/load_shader.h", load_shader).unwrap();
}

fn process_imports(shader_dir: &str, shader: &str, included: &mut HashSet<String>, output: &mut String) {
    if !included.insert(shader.into()) {
        return;
    }
    println!("cargo:rerun-if-changed={}/{}.glsl", shader_dir, shader);
    let source = std::fs::read_to_string(format!("{}/{}.glsl", shader_dir, shader)).unwrap();
    for line in source.lines() {
        if line.starts_with("#include ") {
            let imports = line["#include ".len() ..].split(',');
            for import in imports {
                process_imports(shader_dir, import, included, output);
            }
        } else if line.starts_with("#version ") || line.starts_with("#extension ") {
            // ignore
        } else {
            output.push_str(line);
            output.push('\n');
        }
    }
}

fn translate_shader(shader_key: &str, shader_dir: &str) {
    let mut imported = String::from("#define SWGL 1\n#define __VERSION__ 150\n");
    let _ = write!(imported, "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}U\n",
                   webrender_build::MAX_VERTEX_TEXTURE_WIDTH);

    let (basename, features) =
        shader_key.split_at(shader_key.find(' ').unwrap_or(shader_key.len()));
    if !features.is_empty() {
        for feature in features.trim().split(',') {
            let _ = write!(imported, "#define WR_FEATURE_{}\n", feature);
        }
    }

    process_imports(shader_dir, basename, &mut HashSet::new(), &mut imported);

    let shader = shader_file(shader_key);

    let out_dir = std::env::var("OUT_DIR").unwrap();
    let imp_name = format!("{}/{}.c", out_dir, shader);
    std::fs::write(&imp_name, imported).unwrap();

    let mut build = cc::Build::new();
    build.no_default_flags(true);
    if build.get_compiler().is_like_msvc() {
        build.flag("/EP").flag("/clang:-undef");
    } else {
        build.flag("-xc").flag("-P").flag("-undef");
    }
    build.file(&imp_name);
    let vs = build.clone()
        .define("WR_VERTEX_SHADER", Some("1"))
        .expand();
    let fs = build.clone()
        .define("WR_FRAGMENT_SHADER", Some("1"))
        .expand();
    let vs_name = format!("{}/{}.vert", out_dir, shader);
    let fs_name = format!("{}/{}.frag", out_dir, shader);
    std::fs::write(&vs_name, vs).unwrap();
    std::fs::write(&fs_name, fs).unwrap();

    let args = vec![
        "glsl_to_cxx".to_string(),
        vs_name,
        fs_name,
    ];
    let result = glsl_to_cxx::translate(&mut args.into_iter());
    std::fs::write(format!("{}/{}.h", out_dir, shader), result).unwrap();
}

fn main() {
    let shader_dir = match std::env::var("MOZ_SRC") {
        Ok(dir) => dir + "/gfx/wr/webrender/res",
        Err(_) => std::env::var("CARGO_MANIFEST_DIR").unwrap() + "/../webrender/res",
    };

    let shader_flags =
        ShaderFeatureFlags::GL |
        ShaderFeatureFlags::DUAL_SOURCE_BLENDING |
        ShaderFeatureFlags::ADVANCED_BLEND_EQUATION |
        ShaderFeatureFlags::DEBUG;
    let mut shaders: Vec<String> = Vec::new();
    for (name, features) in get_shader_features(shader_flags) {
        shaders.extend(features.iter().map(|f| {
            if f.is_empty() { name.to_owned() } else { format!("{} {}", name, f) }
        }));
    }

    shaders.sort();

    for shader in &shaders {
        translate_shader(shader, &shader_dir);
    }

    write_load_shader(&shaders);

    println!("cargo:rerun-if-changed=src/blend.h");
    println!("cargo:rerun-if-changed=src/composite.h");
    println!("cargo:rerun-if-changed=src/gl_defs.h");
    println!("cargo:rerun-if-changed=src/glsl.h");
    println!("cargo:rerun-if-changed=src/program.h");
    println!("cargo:rerun-if-changed=src/rasterize.h");
    println!("cargo:rerun-if-changed=src/swgl_ext.h");
    println!("cargo:rerun-if-changed=src/texture.h");
    println!("cargo:rerun-if-changed=src/vector_type.h");
    println!("cargo:rerun-if-changed=src/gl.cc");
    let mut build = cc::Build::new();
    build.cpp(true);

    // SWGL relies heavily on inlining for performance so override -Oz with -O2
    if build.get_compiler().args().contains(&std::ffi::OsString::from("-Oz")) {
        build.flag("-O2");
    }

    build.file("src/gl.cc")
        .flag("-std=c++17")
        .flag("-UMOZILLA_CONFIG_H")
        .flag("-fno-exceptions")
        .flag("-fno-rtti")
        .flag("-fno-math-errno")
        .define("_GLIBCXX_USE_CXX11_ABI", Some("0"))
        .include(shader_dir)
        .include("src")
        .include(std::env::var("OUT_DIR").unwrap())
        .compile("gl_cc");
}
<|endoftext|>


<commit_before>use std::error;
use std::fmt;
use std::io;
use regex;
use std::path;

#[derive(Debug)]
pub enum DotcopterError {
  IO(io::Error),
  Regex(regex::Error),
  StripPrefix(path::StripPrefixError),
}

impl fmt::Display for DotcopterError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      DotcopterError::IO(ref err) => write!(f, "IO error: {}", err),
      DotcopterError::Regex(ref err) => write!(f, "Regex error: {}", err),
      DotcopterError::StripPrefix(ref err) => write!(f, "Strip prefix error: {}", err),
    }
  }
}

impl error::Error for DotcopterError {
  fn description(&self) -> &str {
    match *self {
      DotcopterError::IO(ref err) => err.description(),
      DotcopterError::Regex(ref err) => err.description(),
      DotcopterError::StripPrefix(ref err) => err.description(),
    }
  }

  fn cause(&self) -> Option<&error::Error> {
    match *self {
      DotcopterError::IO(ref err) => Some(err),
      DotcopterError::Regex(ref err) => Some(err),
      DotcopterError::StripPrefix(ref err) => Some(err),
    }
  }
}

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

impl From<regex::Error> for DotcopterError {
  fn from(err: regex::Error) -> DotcopterError {
    DotcopterError::Regex(err)
  }
}

impl From<path::StripPrefixError> for DotcopterError {
  fn from(err: path::StripPrefixError) -> DotcopterError {
    DotcopterError::StripPrefix(err)
  }
}
<commit_msg>use macro to implement dotcopter error from<commit_after>use std::error;
use std::fmt;
use std::io;
use regex;
use std::path;

#[derive(Debug)]
pub enum DotcopterError {
  IO(io::Error),
  Regex(regex::Error),
  StripPrefix(path::StripPrefixError),
}

macro_rules! dotcopter_error_from {
  ($error: ty, $dotcopter_error: ident) => {
    impl From<$error> for DotcopterError {
      fn from(err: $error) -> DotcopterError {
        DotcopterError::$dotcopter_error(err)
      }
    }
  }
}

impl fmt::Display for DotcopterError {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    match *self {
      DotcopterError::IO(ref err) => write!(f, "IO error: {}", err),
      DotcopterError::Regex(ref err) => write!(f, "Regex error: {}", err),
      DotcopterError::StripPrefix(ref err) => write!(f, "Strip prefix error: {}", err),
    }
  }
}

impl error::Error for DotcopterError {
  fn description(&self) -> &str {
    match *self {
      DotcopterError::IO(ref err) => err.description(),
      DotcopterError::Regex(ref err) => err.description(),
      DotcopterError::StripPrefix(ref err) => err.description(),
    }
  }

  fn cause(&self) -> Option<&error::Error> {
    match *self {
      DotcopterError::IO(ref err) => Some(err),
      DotcopterError::Regex(ref err) => Some(err),
      DotcopterError::StripPrefix(ref err) => Some(err),
    }
  }
}

dotcopter_error_from!(io::Error, IO);
dotcopter_error_from!(regex::Error, Regex);
dotcopter_error_from!(path::StripPrefixError, StripPrefix);
<|endoftext|>


<commit_before>//! String escaping.
//!
//! Although filenames in Linux are commonly represented as UTF-8
//! sequences, there is no system requirement that this be the case.  As a
//! consequence, this means that it is possible for filenames in Linux to
//! not be valid UTF-8, and therefore not representable as strings.
//!
//! To prevent encoding problems, as well as to allow certain characters,
//! such as space, to separate tokens in the sure file format, we escape
//! some bytes in strings by replacing them with "=xx" where "xx" is the
//! lower-cased hex version of the string.  The range of valid characters
//! is fairly straightforward, including all of the printable characters
//! from '!' to '~' except for the '=', which is always escaped.  This
//! means, for example, that a 2-byte encoded UTF-8 sequence will expand to
//! take 6 bytes.

use std::io::prelude::*;

pub trait Escape {
    fn escaped(&self) -> String;
}

pub trait Unescape {
    fn unescape(&self) -> Result<Vec<u8>, EscapeError>;
}

error_chain! {
    types {
        EscapeError, EscapeErrorKind, EscapChainError, EscapeResult;
    }

    links {
    }

    foreign_links {
    }

    errors {
        InvalidHexCharacter(ch: u8) {
            description("Invalid hex character")
            display("Invalid hex character: {:?}", ch)
        }
        InvalidHexLength {
            description("Invalid hex length")
            display("Invalid hex length")
        }
    }
}

// The basic encoding converts a sequence of bytes into a string.
impl Escape for [u8] {
    fn escaped(&self) -> String {
        let mut result = vec![];
        for &ch in self.iter() {
            // TODO: Can be made more efficient.
            if b'!' <= ch && ch <= b'~' && ch != b'=' {
                result.push(ch);
            } else {
                write!(&mut result, "={:02x}", ch).unwrap();
            }
        }

        // TODO: String::from_utf8_unchecked(result)
        String::from_utf8(result).unwrap()
    }
}

impl Unescape for str {
    fn unescape(&self) -> Result<Vec<u8>, EscapeError> {
        // Will overestimate.
        let mut buf = Vec::with_capacity(self.len() / 2);
        let mut phase = 0;
        let mut tmp = 0;

        for byte in self.bytes() {
            if phase == 0 {
                if byte == b'=' {
                    phase = 1;
                } else {
                    buf.push(byte);
                }
            } else {
                tmp <<= 4;
                match byte {
                    b'A'...b'F' => tmp |= byte - b'A' + 10,
                    b'a'...b'f' => tmp |= byte - b'a' + 10,
                    b'0'...b'f' => tmp |= byte - b'0',
                    _ => return Err(EscapeErrorKind::InvalidHexCharacter(byte).into()),
                }
                phase += 1;
                if phase == 3 {
                    buf.push(tmp);
                    phase = 0;
                    tmp = 0;
                }
            }
        }

        if phase != 0 {
            return Err(EscapeErrorKind::InvalidHexLength.into());
        }

        Ok(buf)
    }
}

#[test]
fn test_unescape() {
    macro_rules! assert_error_kind {
        ( $expr:expr, $kind:pat ) => {
            match $expr.unwrap_err().into_kind() {
                $kind => (),
                e => panic!("Unexpected error kind: {:?} (want {})", e, stringify!($kind)),
            }
        }
    }

    assert_eq!("=00".unescape().unwrap(), vec![0]);
    assert_error_kind!("=00=0".unescape(), EscapeErrorKind::InvalidHexLength);
    assert_error_kind!("=00=".unescape(), EscapeErrorKind::InvalidHexLength);
    assert_error_kind!("=4g".unescape(), EscapeErrorKind::InvalidHexCharacter(b'g'));
}

#[test]
fn test_escape() {
    let buf: Vec<u8> = (0u32..256).map(|i| i as u8).collect();
    let text = (&buf[..]).escaped();
    assert_eq!(text.unescape().unwrap(), buf);
}
<commit_msg>escape: Use `EscapeResult`<commit_after>//! String escaping.
//!
//! Although filenames in Linux are commonly represented as UTF-8
//! sequences, there is no system requirement that this be the case.  As a
//! consequence, this means that it is possible for filenames in Linux to
//! not be valid UTF-8, and therefore not representable as strings.
//!
//! To prevent encoding problems, as well as to allow certain characters,
//! such as space, to separate tokens in the sure file format, we escape
//! some bytes in strings by replacing them with "=xx" where "xx" is the
//! lower-cased hex version of the string.  The range of valid characters
//! is fairly straightforward, including all of the printable characters
//! from '!' to '~' except for the '=', which is always escaped.  This
//! means, for example, that a 2-byte encoded UTF-8 sequence will expand to
//! take 6 bytes.

use std::io::prelude::*;

pub trait Escape {
    fn escaped(&self) -> String;
}

pub trait Unescape {
    fn unescape(&self) -> EscapeResult<Vec<u8>>;
}

error_chain! {
    types {
        EscapeError, EscapeErrorKind, EscapChainError, EscapeResult;
    }

    links {
    }

    foreign_links {
    }

    errors {
        InvalidHexCharacter(ch: u8) {
            description("Invalid hex character")
            display("Invalid hex character: {:?}", ch)
        }
        InvalidHexLength {
            description("Invalid hex length")
            display("Invalid hex length")
        }
    }
}

// The basic encoding converts a sequence of bytes into a string.
impl Escape for [u8] {
    fn escaped(&self) -> String {
        let mut result = vec![];
        for &ch in self.iter() {
            // TODO: Can be made more efficient.
            if b'!' <= ch && ch <= b'~' && ch != b'=' {
                result.push(ch);
            } else {
                write!(&mut result, "={:02x}", ch).unwrap();
            }
        }

        // TODO: String::from_utf8_unchecked(result)
        String::from_utf8(result).unwrap()
    }
}

impl Unescape for str {
    fn unescape(&self) -> EscapeResult<Vec<u8>> {
        // Will overestimate.
        let mut buf = Vec::with_capacity(self.len() / 2);
        let mut phase = 0;
        let mut tmp = 0;

        for byte in self.bytes() {
            if phase == 0 {
                if byte == b'=' {
                    phase = 1;
                } else {
                    buf.push(byte);
                }
            } else {
                tmp <<= 4;
                match byte {
                    b'A'...b'F' => tmp |= byte - b'A' + 10,
                    b'a'...b'f' => tmp |= byte - b'a' + 10,
                    b'0'...b'f' => tmp |= byte - b'0',
                    _ => return Err(EscapeErrorKind::InvalidHexCharacter(byte).into()),
                }
                phase += 1;
                if phase == 3 {
                    buf.push(tmp);
                    phase = 0;
                    tmp = 0;
                }
            }
        }

        if phase != 0 {
            return Err(EscapeErrorKind::InvalidHexLength.into());
        }

        Ok(buf)
    }
}

#[test]
fn test_unescape() {
    macro_rules! assert_error_kind {
        ( $expr:expr, $kind:pat ) => {
            match $expr.unwrap_err().into_kind() {
                $kind => (),
                e => panic!("Unexpected error kind: {:?} (want {})", e, stringify!($kind)),
            }
        }
    }

    assert_eq!("=00".unescape().unwrap(), vec![0]);
    assert_error_kind!("=00=0".unescape(), EscapeErrorKind::InvalidHexLength);
    assert_error_kind!("=00=".unescape(), EscapeErrorKind::InvalidHexLength);
    assert_error_kind!("=4g".unescape(), EscapeErrorKind::InvalidHexCharacter(b'g'));
}

#[test]
fn test_escape() {
    let buf: Vec<u8> = (0u32..256).map(|i| i as u8).collect();
    let text = (&buf[..]).escaped();
    assert_eq!(text.unescape().unwrap(), buf);
}
<|endoftext|>


<commit_before>use libimagrt::runtime::Runtime;

pub fn update(rt: &Runtime) {
}

<commit_msg>Implement Store::update() interface<commit_after>use std::path::PathBuf;
use std::ops::DerefMut;

use libimagrt::runtime::Runtime;
use util::build_toml_header;
use util::build_entry_path;

pub fn update(rt: &Runtime) {
    rt.cli()
        .subcommand_matches("update")
        .map(|scmd| {
            rt.store()
                .retrieve(scmd.value_of("id").map(|id| build_entry_path(rt, id)).unwrap())
                .map(|mut locked_e| {
                    let mut e = locked_e.deref_mut();

                    scmd.value_of("content")
                        .map(|new_content| {
                            *e.get_content_mut() = String::from(new_content);
                            debug!("New content set");
                        });

                    *e.get_header_mut() = build_toml_header(scmd, e.get_header().clone());
                    debug!("New header set");
                })
        });

}

<|endoftext|>


<commit_before>use super::common::Log;

use base64;
use futures;
use futures::FutureExt;
use hyper;
use serde::{Deserialize, Serialize};
use serde_json;
use std::convert::TryFrom;
use std::io::Write;
use std::time::Duration;

#[derive(Debug, Deserialize)]
pub struct SignedCertificateTimestamp {
    sct_version: u8,
    id: String,
    timestamp: u64,
    extensions: String,
    signature: String,
}

impl SignedCertificateTimestamp {
    pub fn to_raw_bytes(&self) -> Vec<u8> {
        let mut b = Vec::new();
        b.write_all(&[self.sct_version]).unwrap();

        let log_id = base64::decode(&self.id).unwrap();
        b.write_all(&log_id).unwrap();

        b.write_all(&self.timestamp.to_be_bytes()).unwrap();

        let extensions = base64::decode(&self.extensions).unwrap();
        b.write_all(&u16::try_from(extensions.len()).unwrap().to_be_bytes())
            .unwrap();
        b.write_all(&extensions).unwrap();

        let signature = base64::decode(&self.signature).unwrap();
        b.write_all(&signature).unwrap();

        b
    }
}

async fn submit_to_log<C: hyper::client::connect::Connect + Send + Sync + Clone + 'static>(
    http_client: &hyper::Client<C>,
    log: &Log,
    payload: Vec<u8>,
) -> Result<SignedCertificateTimestamp, ()> {
    let mut url = "https://".to_string() + &log.url;
    if !url.ends_with('/') {
        url += "/";
    }
    url += "ct/v1/add-chain";

    let request = hyper::Request::builder()
        .method("POST")
        .uri(url)
        .header("Content-Type", "application/json")
        .body(payload.into())
        .unwrap();
    // TODO: maybe not all of these should be silently ignored.
    let response = http_client.request(request).await.map_err(|_| ())?;

    // 400, 403, and probably some others generally indicate a log doesn't accept certs from
    // this root, or that the log isn't accepting new submissions. Server errors mean there's
    // nothing we can do.
    if response.status().is_client_error() || response.status().is_server_error() {
        return Err(());
    }

    let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
    Ok(serde_json::from_slice(&body).unwrap())
}

#[derive(Serialize, Deserialize)]
pub struct AddChainRequest {
    pub chain: Vec<String>,
}

pub async fn submit_cert_to_logs<
    C: hyper::client::connect::Connect + Send + Sync + Clone + 'static,
>(
    http_client: &hyper::Client<C>,
    logs: &[Log],
    cert: &[Vec<u8>],
    timeout: Duration,
) -> Vec<(usize, SignedCertificateTimestamp)> {
    let payload = serde_json::to_vec(&AddChainRequest {
        chain: cert.iter().map(|r| base64::encode(r)).collect(),
    })
    .unwrap();

    let futures = logs
        .iter()
        .enumerate()
        .map(move |(idx, log)| {
            let s = tokio::time::timeout(timeout, submit_to_log(http_client, log, payload.clone()));
            async move {
                match s.await {
                    Ok(Ok(sct)) => Some((idx, sct)),
                    _ => None,
                }
            }
        })
        .collect::<Vec<_>>();

    futures::future::join_all(futures)
        .map(|scts| scts.into_iter().filter_map(|s| s).collect())
        .await
}
<commit_msg>Simplify code in ct.rs<commit_after>use super::common::Log;

use base64;
use futures;
use hyper;
use serde::{Deserialize, Serialize};
use serde_json;
use std::convert::TryFrom;
use std::io::Write;
use std::time::Duration;

#[derive(Debug, Deserialize)]
pub struct SignedCertificateTimestamp {
    sct_version: u8,
    id: String,
    timestamp: u64,
    extensions: String,
    signature: String,
}

impl SignedCertificateTimestamp {
    pub fn to_raw_bytes(&self) -> Vec<u8> {
        let mut b = Vec::new();
        b.write_all(&[self.sct_version]).unwrap();

        let log_id = base64::decode(&self.id).unwrap();
        b.write_all(&log_id).unwrap();

        b.write_all(&self.timestamp.to_be_bytes()).unwrap();

        let extensions = base64::decode(&self.extensions).unwrap();
        b.write_all(&u16::try_from(extensions.len()).unwrap().to_be_bytes())
            .unwrap();
        b.write_all(&extensions).unwrap();

        let signature = base64::decode(&self.signature).unwrap();
        b.write_all(&signature).unwrap();

        b
    }
}

async fn submit_to_log<C: hyper::client::connect::Connect + Send + Sync + Clone + 'static>(
    http_client: &hyper::Client<C>,
    log: &Log,
    payload: Vec<u8>,
) -> Result<SignedCertificateTimestamp, ()> {
    let mut url = "https://".to_string() + &log.url;
    if !url.ends_with('/') {
        url += "/";
    }
    url += "ct/v1/add-chain";

    let request = hyper::Request::builder()
        .method("POST")
        .uri(url)
        .header("Content-Type", "application/json")
        .body(payload.into())
        .unwrap();
    // TODO: maybe not all of these should be silently ignored.
    let response = http_client.request(request).await.map_err(|_| ())?;

    // 400, 403, and probably some others generally indicate a log doesn't accept certs from
    // this root, or that the log isn't accepting new submissions. Server errors mean there's
    // nothing we can do.
    if response.status().is_client_error() || response.status().is_server_error() {
        return Err(());
    }

    let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
    Ok(serde_json::from_slice(&body).unwrap())
}

#[derive(Serialize, Deserialize)]
pub struct AddChainRequest {
    pub chain: Vec<String>,
}

pub async fn submit_cert_to_logs<
    C: hyper::client::connect::Connect + Send + Sync + Clone + 'static,
>(
    http_client: &hyper::Client<C>,
    logs: &[Log],
    cert: &[Vec<u8>],
    timeout: Duration,
) -> Vec<(usize, SignedCertificateTimestamp)> {
    let payload = serde_json::to_vec(&AddChainRequest {
        chain: cert.iter().map(|r| base64::encode(r)).collect(),
    })
    .unwrap();

    let futures = logs
        .iter()
        .enumerate()
        .map(move |(idx, log)| {
            let s = tokio::time::timeout(timeout, submit_to_log(http_client, log, payload.clone()));
            async move {
                match s.await {
                    Ok(Ok(sct)) => Some((idx, sct)),
                    _ => None,
                }
            }
        })
        .collect::<Vec<_>>();

    let scts = futures::future::join_all(futures).await;
    scts.into_iter().filter_map(|s| s).collect()
}
<|endoftext|>


<commit_before>use std::fs;
use std::io;
use std::os::unix::io::AsRawFd;
use std::mem;

use byteorder::{LittleEndian, ByteOrder};

use nix::sys::ioctl;

use dm_ioctl as dmi;

const DM_IOCTL: u8 = 0xfd;
const DM_CTL_PATH: &'static str= "/dev/mapper/control";

const DM_VERSION_MAJOR: u32 = 4;
const DM_VERSION_MINOR: u32 = 30;
const DM_VERSION_PATCHLEVEL: u32 = 0;

pub fn get_version() -> io::Result<(u32, u32, u32)> {

    let f = try!(fs::File::open(DM_CTL_PATH));

    let mut hdr: dmi::Struct_dm_ioctl = Default::default();
    hdr.version[0] = DM_VERSION_MAJOR;
    hdr.version[1] = DM_VERSION_MINOR;
    hdr.version[2] = DM_VERSION_PATCHLEVEL;

    let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_VERSION_CMD as u8,
                            mem::size_of::<dmi::Struct_dm_ioctl>());

    match unsafe { ioctl::read_into(f.as_raw_fd(), op, &mut hdr) } {
        Err(_) => return Err((io::Error::last_os_error())),
        _ => {},
    };

    Ok((hdr.version[0], hdr.version[1], hdr.version[2]))
}

//
// Return up to the first \0, or None
//
fn slice_to_null(slc: &[u8]) -> Option<&[u8]> {
    for (i, c) in slc.iter().enumerate() {
        if *c == b'\0' { return Some(&slc[..i]) };
    }
    None
}

pub fn list_devices() -> io::Result<Vec<(String, u64)>> {

    let mut devs = Vec::new();

    let f = try!(fs::File::open(DM_CTL_PATH));

    let mut buf = [0u8; 10240];

    let hdr: &mut dmi::Struct_dm_ioctl = unsafe {mem::transmute(&mut buf)};

    let hdr_size = mem::size_of::<dmi::Struct_dm_ioctl>();
    hdr.version[0] = DM_VERSION_MAJOR;
    hdr.version[1] = DM_VERSION_MINOR;
    hdr.version[2] = DM_VERSION_PATCHLEVEL;
    hdr.data_size = buf.len() as u32;
    hdr.data_start = hdr_size as u32;

    let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_LIST_DEVICES_CMD as u8, buf.len());

    match unsafe { ioctl::read_into(f.as_raw_fd(), op, &mut buf) } {
        Err(_) => return Err((io::Error::last_os_error())),
        _ => {},
    };

    if (hdr.data_size - hdr_size as u32) != 0 {
        let mut result = &buf[hdr_size..];

        loop {
            let slc = slice_to_null(&result[12..]).expect("Bad data from ioctl");
            let devno = LittleEndian::read_u64(&result[..8]);
            devs.push((String::from_utf8_lossy(slc).into_owned(), devno));

            let next = LittleEndian::read_u32(&result[8..12]);
            if next == 0 { break }

            result = &result[next as usize..];
        }
    }

    Ok(devs)
}
<commit_msg>dm: Create a DM object<commit_after>use std::fs;
use std::fs::File;
use std::io;
use std::os::unix::io::AsRawFd;
use std::mem;
use std::slice::bytes::copy_memory;
use std::io::Error;
use std::io::ErrorKind::Other;

use byteorder::{LittleEndian, ByteOrder};

use nix::sys::ioctl;

use dm_ioctl as dmi;
use lv::LV;
use vg::VG;

const DM_IOCTL: u8 = 0xfd;
const DM_CTL_PATH: &'static str= "/dev/mapper/control";

const DM_VERSION_MAJOR: u32 = 4;
const DM_VERSION_MINOR: u32 = 30;
const DM_VERSION_PATCHLEVEL: u32 = 0;

pub struct DM<'a> {
    file: File,
    vg: &'a VG,
}

impl <'a> DM<'a> {
    pub fn new(vg: &'a VG) -> io::Result<Self> {
        Ok(DM {
            file: try!(File::open(DM_CTL_PATH)),
            vg: vg,
        })
    }

    fn get_version(&self) -> io::Result<(u32, u32, u32)> {

        let mut hdr: dmi::Struct_dm_ioctl = Default::default();
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_VERSION_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        Ok((hdr.version[0], hdr.version[1], hdr.version[2]))
    }

    fn list_devices(&self) -> io::Result<Vec<(String, u64)>> {

        let mut devs = Vec::new();

        let mut buf = [0u8; 10240];

        let hdr: &mut dmi::Struct_dm_ioctl = unsafe {mem::transmute(&mut buf)};

        let hdr_size = mem::size_of::<dmi::Struct_dm_ioctl>();
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;
        hdr.data_size = buf.len() as u32;
        hdr.data_start = hdr_size as u32;

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_LIST_DEVICES_CMD as u8, buf.len());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut buf) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => {},
        };

        if (hdr.data_size - hdr_size as u32) != 0 {
            let mut result = &buf[hdr_size..];

            loop {
                let slc = slice_to_null(&result[12..]).expect("Bad data from ioctl");
                let devno = LittleEndian::read_u64(&result[..8]);
                devs.push((String::from_utf8_lossy(slc).into_owned(), devno));

                let next = LittleEndian::read_u32(&result[8..12]);
                if next == 0 { break }

                result = &result[next as usize..];
            }
        }

        Ok(devs)
    }

    fn initialize_hdr(lv: &LV, hdr: &mut dmi::Struct_dm_ioctl) -> () {
        hdr.version[0] = DM_VERSION_MAJOR;
        hdr.version[1] = DM_VERSION_MINOR;
        hdr.version[2] = DM_VERSION_PATCHLEVEL;

        // Transmute [i8; 128] to [u8; 128]
        let name_dest: &mut [u8; 128] = unsafe { mem::transmute(&mut hdr.name) };
        copy_memory(&lv.name.as_bytes(), &mut name_dest[..]);

        let uuid_dest: &mut [u8; 129] = unsafe { mem::transmute(&mut hdr.uuid) };
        copy_memory(&lv.id.as_bytes(), &mut uuid_dest[..]);
    }

    fn create_device(&self, lv: &LV) -> io::Result<()> {
        let mut hdr: dmi::Struct_dm_ioctl = Default::default();

        Self::initialize_hdr(lv, &mut hdr);

        let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_DEV_CREATE_CMD as u8,
                                      mem::size_of::<dmi::Struct_dm_ioctl>());

        match unsafe { ioctl::read_into(self.file.as_raw_fd(), op, &mut hdr) } {
            Err(_) => return Err((io::Error::last_os_error())),
            _ => Ok(())
        }
    }

    fn load_device(&self, lv: &LV) -> io::Result<()> {
        let mut buf = [0u8; 10240];

        println!("hihi");

        let mut hdr: &mut dmi::Struct_dm_ioctl = unsafe {mem::transmute(&mut buf)};

        let hdr_size = mem::size_of::<dmi::Struct_dm_ioctl>();

        Self::initialize_hdr(lv, &mut hdr);

        hdr.data_size = buf.len() as u32;
        hdr.data_start = hdr_size as u32;

        println!("hihi2");

        //let op = ioctl::op_read_write(DM_IOCTL, dmi::DM_LIST_DEVICES_CMD as u8, buf.len());

        let err = || Error::new(Other, "dm load_device error");

        let sectors_per_extent = self.vg.extent_size;

        let data = &buf[hdr_size..];
        for seg in &lv.segments {
            for &(ref pvname, ref loc) in &seg.stripes {
                let pv = try!(self.vg.pvs.get(pvname).ok_or(err()));

                println!("{} {} linear {}:{} {}",
                         seg.start_extent * sectors_per_extent,
                         seg.extent_count * sectors_per_extent,
                         pv.device.major,
                         pv.device.minor,
                         (loc * sectors_per_extent) + pv.pe_start);
            }
        }

        Ok(())
    }

    fn resume_device(&self, lv: &LV) -> io::Result<()> {
        Ok(())
    }

    pub fn activate_device(&self, lv: &LV) -> io::Result<()> {

        // TODO: name/uuid mangle?

//        self.create_device(&lv);

        self.load_device(&lv);

        self.resume_device(&lv);

        Ok(())
    }
}

//
// Return up to the first \0, or None
//
fn slice_to_null(slc: &[u8]) -> Option<&[u8]> {
    for (i, c) in slc.iter().enumerate() {
        if *c == b'\0' { return Some(&slc[..i]) };
    }
    None
}
<|endoftext|>


<commit_before>#![feature(box_syntax)]
#![allow(
    clippy::borrowed_box,
    clippy::needless_pass_by_value,
    clippy::unused_unit,
    clippy::redundant_clone,
    clippy::match_single_binding
)]
#![warn(clippy::boxed_local)]

#[derive(Clone)]
struct A;

impl A {
    fn foo(&self) {}
}

trait Z {
    fn bar(&self);
}

impl Z for A {
    fn bar(&self) {
        //nothing
    }
}

fn main() {}

fn ok_box_trait(boxed_trait: &Box<dyn Z>) {
    let boxed_local = boxed_trait;
    // done
}

fn warn_call() {
    let x = box A;
    x.foo();
}

fn warn_arg(x: Box<A>) {
    x.foo();
}

fn nowarn_closure_arg() {
    let x = Some(box A);
    x.map_or((), |x| take_ref(&x));
}

fn warn_rename_call() {
    let x = box A;

    let y = x;
    y.foo(); // via autoderef
}

fn warn_notuse() {
    let bz = box A;
}

fn warn_pass() {
    let bz = box A;
    take_ref(&bz); // via deref coercion
}

fn nowarn_return() -> Box<A> {
    box A // moved out, "escapes"
}

fn nowarn_move() {
    let bx = box A;
    drop(bx) // moved in, "escapes"
}
fn nowarn_call() {
    let bx = box A;
    bx.clone(); // method only available to Box, not via autoderef
}

fn nowarn_pass() {
    let bx = box A;
    take_box(&bx); // fn needs &Box
}

fn take_box(x: &Box<A>) {}
fn take_ref(x: &A) {}

fn nowarn_ref_take() {
    // false positive, should actually warn
    let x = box A;
    let y = &x;
    take_box(y);
}

fn nowarn_match() {
    let x = box A; // moved into a match
    match x {
        y => drop(y),
    }
}

fn warn_match() {
    let x = box A;
    match &x {
        // not moved
        ref y => (),
    }
}

fn nowarn_large_array() {
    // should not warn, is large array
    // and should not be on stack
    let x = box [1; 10000];
    match &x {
        // not moved
        ref y => (),
    }
}

/// ICE regression test
pub trait Foo {
    type Item;
}

impl<'a> Foo for &'a () {
    type Item = ();
}

pub struct PeekableSeekable<I: Foo> {
    _peeked: I::Item,
}

pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}

/// Regression for #916, #1123
///
/// This shouldn't warn for `boxed_local`as the implementation of a trait
/// can't change much about the trait definition.
trait BoxedAction {
    fn do_sth(self: Box<Self>);
}

impl BoxedAction for u64 {
    fn do_sth(self: Box<Self>) {
        println!("{}", *self)
    }
}

/// Regression for #1478
///
/// This shouldn't warn for `boxed_local`as self itself is a box type.
trait MyTrait {
    fn do_sth(self);
}

impl<T> MyTrait for Box<T> {
    fn do_sth(self) {}
}

// Issue #3739 - capture in closures
mod issue_3739 {
    use super::A;

    fn consume<T>(_: T) {}
    fn borrow<T>(_: &T) {}

    fn closure_consume(x: Box<A>) {
        let _ = move || {
            consume(x);
        };
    }

    fn closure_borrow(x: Box<A>) {
        let _ = || {
            borrow(&x);
        };
    }
}

/// Issue #5542
///
/// This shouldn't warn for `boxed_local` as it is a function to be used in C.
pub extern "C" fn do_not_warn_me(_c_pointer: Box<String>) -> () {}
<commit_msg>clippy_lint: extern definition is in Rust, not C<commit_after>#![feature(box_syntax)]
#![allow(
    clippy::borrowed_box,
    clippy::needless_pass_by_value,
    clippy::unused_unit,
    clippy::redundant_clone,
    clippy::match_single_binding
)]
#![warn(clippy::boxed_local)]

#[derive(Clone)]
struct A;

impl A {
    fn foo(&self) {}
}

trait Z {
    fn bar(&self);
}

impl Z for A {
    fn bar(&self) {
        //nothing
    }
}

fn main() {}

fn ok_box_trait(boxed_trait: &Box<dyn Z>) {
    let boxed_local = boxed_trait;
    // done
}

fn warn_call() {
    let x = box A;
    x.foo();
}

fn warn_arg(x: Box<A>) {
    x.foo();
}

fn nowarn_closure_arg() {
    let x = Some(box A);
    x.map_or((), |x| take_ref(&x));
}

fn warn_rename_call() {
    let x = box A;

    let y = x;
    y.foo(); // via autoderef
}

fn warn_notuse() {
    let bz = box A;
}

fn warn_pass() {
    let bz = box A;
    take_ref(&bz); // via deref coercion
}

fn nowarn_return() -> Box<A> {
    box A // moved out, "escapes"
}

fn nowarn_move() {
    let bx = box A;
    drop(bx) // moved in, "escapes"
}
fn nowarn_call() {
    let bx = box A;
    bx.clone(); // method only available to Box, not via autoderef
}

fn nowarn_pass() {
    let bx = box A;
    take_box(&bx); // fn needs &Box
}

fn take_box(x: &Box<A>) {}
fn take_ref(x: &A) {}

fn nowarn_ref_take() {
    // false positive, should actually warn
    let x = box A;
    let y = &x;
    take_box(y);
}

fn nowarn_match() {
    let x = box A; // moved into a match
    match x {
        y => drop(y),
    }
}

fn warn_match() {
    let x = box A;
    match &x {
        // not moved
        ref y => (),
    }
}

fn nowarn_large_array() {
    // should not warn, is large array
    // and should not be on stack
    let x = box [1; 10000];
    match &x {
        // not moved
        ref y => (),
    }
}

/// ICE regression test
pub trait Foo {
    type Item;
}

impl<'a> Foo for &'a () {
    type Item = ();
}

pub struct PeekableSeekable<I: Foo> {
    _peeked: I::Item,
}

pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}

/// Regression for #916, #1123
///
/// This shouldn't warn for `boxed_local`as the implementation of a trait
/// can't change much about the trait definition.
trait BoxedAction {
    fn do_sth(self: Box<Self>);
}

impl BoxedAction for u64 {
    fn do_sth(self: Box<Self>) {
        println!("{}", *self)
    }
}

/// Regression for #1478
///
/// This shouldn't warn for `boxed_local`as self itself is a box type.
trait MyTrait {
    fn do_sth(self);
}

impl<T> MyTrait for Box<T> {
    fn do_sth(self) {}
}

// Issue #3739 - capture in closures
mod issue_3739 {
    use super::A;

    fn consume<T>(_: T) {}
    fn borrow<T>(_: &T) {}

    fn closure_consume(x: Box<A>) {
        let _ = move || {
            consume(x);
        };
    }

    fn closure_borrow(x: Box<A>) {
        let _ = || {
            borrow(&x);
        };
    }
}

/// Issue #5542
///
/// This shouldn't warn for `boxed_local` as it is intended to called from non-Rust code.
pub extern "C" fn do_not_warn_me(_c_pointer: Box<String>) -> () {}
<|endoftext|>


<commit_before>
pub struct Dendrite {
    source: Neuron,
    weight: i32
}

pub struct Neuron {
    sources: Vec<Dendrite>,
    threshold: i32,
    result_new: i32,
    pub result: i32
}

impl Neuron {
    pub fn new(threshold: i32) -> Neuron {
        Neuron {
            sources: Vec::new(),
            threshold: threshold,
            result_new: 0,
            result: 0
        }
    }

    pub fn add_dendrite(&self, dendrite: Dendrite) {
        //TODO: Impl add_dendrite
    }

    pub fn remove_dendrite(&self, dendrite: Dendrite) {
        //TODO: impl.
    }

    pub fn do_calculation() {
        //TODO: Impl do_calculation
    }

    pub fn expose_result(&self) {
        self.result = self.result_new;
    }
}

#[test]
fn hello() {
    println!("Hello!");
}

<commit_msg>Add neuron implementation<commit_after>
pub struct Dendrite {
    source: Neuron,
    weight: i32
}

pub struct Neuron {
    dendrites: Vec<Dendrite>,
    threshold: i32,
    result_new: i32,
    pub result: i32
}

impl PartialEq for Neuron {
    fn eq(&self, other: &Neuron) -> bool {
        self == other
    }

    fn ne(&self, other: &Neuron) -> bool {
        !self.eq(other)
    }
}

impl Neuron {
    pub fn new(threshold: i32) -> Neuron {
        Neuron {
            dendrites: Vec::new(),
            threshold: threshold,
            result_new: 0,
            result: 0
        }
    }

    fn search_dendrite(&self, neuron: &Neuron) -> Option<usize> {
        let mut iter = self.dendrites.iter();
        let mut cnt: usize = 0;

        loop {
            match iter.next() {
                Some(dendrite) => {
                    if dendrite.source == *neuron {
                        return Some(cnt);
                    }
                },
                None => return None,
            }

            cnt += 1;
        }
    }

    pub fn add_dendrite(&mut self, dendrite: Dendrite) {
        let idx = self.search_dendrite(&dendrite.source);

        match idx {
            Some(_) => panic!("Duplicated dendrites!"),
            None => self.dendrites.push(dendrite),
        }
    }

    pub fn remove_dendrite(&mut self, neuron: Neuron) {
        let idx = self.search_dendrite(&neuron);

        match idx {
            Some(x) => {
                self.dendrites.remove(x);
            },
            None => return,
        }
    }

    pub fn do_calculation(&mut self) {
        let mut iter = self.dendrites.iter();
        let mut acc: i32 = 0;

        loop {
            match iter.next() {
                Some(dendrite) => {
                    acc += dendrite.source.result * dendrite.weight;
                },
                None => break,
            }
        }

        if acc > self.threshold {
            self.result_new = 1;
        } else {
            self.result_new = 0;
        }
    }

    pub fn expose_result(&mut self) {
        self.result = self.result_new;
    }
}

#[test]
fn hello() {
    println!("Hello!");
}

<|endoftext|>