<commit_before>use alloc::vec::Vec;
use core::fmt::Debug;

use crate::read::{ByteString, Bytes, Error, ReadError, Result};
use crate::{pe, LittleEndian as LE, U16Bytes, U32Bytes};

/// Where an export is pointing to.
#[derive(Clone, Copy)]
pub enum ExportTarget<'data> {
    /// The address of the export, relative to the image base.
    Address(u32),
    /// Forwarded to an export ordinal in another DLL.
    ///
    /// This gives the name of the DLL, and the ordinal.
    ForwardByOrdinal(&'data [u8], u32),
    /// Forwarded to an export name in another DLL.
    ///
    /// This gives the name of the DLL, and the export name.
    ForwardByName(&'data [u8], &'data [u8]),
}

impl<'data> ExportTarget<'data> {
    /// Returns true if the target is an address.
    pub fn is_address(&self) -> bool {
        match self {
            ExportTarget::Address(_) => true,
            _ => false,
        }
    }

    /// Returns true if the export is forwarded to another DLL.
    pub fn is_forward(&self) -> bool {
        !self.is_address()
    }
}

/// An export from a PE file.
///
/// There are multiple kinds of PE exports (with or without a name, and local or forwarded).
#[derive(Clone, Copy)]
pub struct Export<'data> {
    /// The ordinal of the export.
    ///
    /// These are sequential, starting at a base specified in the DLL.
    pub ordinal: u32,
    /// The name of the export, if known.
    pub name: Option<&'data [u8]>,
    /// The target of this export.
    pub target: ExportTarget<'data>,
}

impl<'a> Debug for Export<'a> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
        f.debug_struct("Export")
            .field("ordinal", &self.ordinal)
            .field("name", &self.name.map(ByteString))
            .field("target", &self.target)
            .finish()
    }
}

impl<'a> Debug for ExportTarget<'a> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
        match self {
            ExportTarget::Address(address) => write!(f, "Address({:#x})", address),
            ExportTarget::ForwardByOrdinal(library, ordinal) => write!(
                f,
                "ForwardByOrdinal({:?}.#{})",
                ByteString(library),
                ordinal
            ),
            ExportTarget::ForwardByName(library, name) => write!(
                f,
                "ForwardByName({:?}.{:?})",
                ByteString(library),
                ByteString(name)
            ),
        }
    }
}

/// A partially parsed PE export table.
#[derive(Debug, Clone)]
pub struct ExportTable<'data> {
    data: Bytes<'data>,
    virtual_address: u32,
    directory: &'data pe::ImageExportDirectory,
    addresses: &'data [U32Bytes<LE>],
    names: &'data [U32Bytes<LE>],
    name_ordinals: &'data [U16Bytes<LE>],
}

impl<'data> ExportTable<'data> {
    /// Parse the export table given its section data and address.
    pub fn parse(data: &'data [u8], virtual_address: u32) -> Result<Self> {
        let data = Bytes(data);
        let directory = data
            .read_at::<pe::ImageExportDirectory>(0)
            .read_error("Invalid PE export dir size")?;
        let addresses = data
            .read_slice_at::<U32Bytes<_>>(
                directory
                    .address_of_functions
                    .get(LE)
                    .wrapping_sub(virtual_address) as usize,
                directory.number_of_functions.get(LE) as usize,
            )
            .read_error("Invalid PE export address table")?;
        let mut names = &[][..];
        let mut name_ordinals = &[][..];
        let number = directory.number_of_names.get(LE) as usize;
        if number != 0 {
            names = data
                .read_slice_at::<U32Bytes<_>>(
                    directory
                        .address_of_names
                        .get(LE)
                        .wrapping_sub(virtual_address) as usize,
                    number,
                )
                .read_error("Invalid PE export name pointer table")?;
            name_ordinals = data
                .read_slice_at::<U16Bytes<_>>(
                    directory
                        .address_of_name_ordinals
                        .get(LE)
                        .wrapping_sub(virtual_address) as usize,
                    number,
                )
                .read_error("Invalid PE export ordinal table")?;
        }
        Ok(ExportTable {
            data,
            virtual_address,
            directory,
            addresses,
            names,
            name_ordinals,
        })
    }

    /// Returns the header of the export table.
    pub fn directory(&self) -> &'data pe::ImageExportDirectory {
        self.directory
    }

    /// Returns the base value of ordinals.
    ///
    /// Adding this to an address index will give an ordinal.
    pub fn ordinal_base(&self) -> u32 {
        self.directory.base.get(LE)
    }

    /// Returns the unparsed address table.
    ///
    /// An address table entry may be a local address, or the address of a forwarded export entry.
    /// See [`Self::is_forward`] and [`Self::target_from_address`].
    pub fn addresses(&self) -> &'data [U32Bytes<LE>] {
        self.addresses
    }

    /// Returns the unparsed name pointer table.
    ///
    /// A name pointer table entry can be used with [`Self::name_from_pointer`].
    pub fn name_pointers(&self) -> &'data [U32Bytes<LE>] {
        self.names
    }

    /// Returns the unparsed ordinal table.
    ///
    /// An ordinal table entry is a 0-based index into the address table.
    /// See [`Self::address_by_index`] and [`Self::target_by_index`].
    pub fn name_ordinals(&self) -> &'data [U16Bytes<LE>] {
        self.name_ordinals
    }

    /// Returns an iterator for the entries in the name pointer table and ordinal table.
    ///
    /// A name pointer table entry can be used with [`Self::name_from_pointer`].
    ///
    /// An ordinal table entry is a 0-based index into the address table.
    /// See [`Self::address_by_index`] and [`Self::target_by_index`].
    pub fn name_iter(&self) -> impl Iterator<Item = (u32, u16)> + 'data {
        self.names
            .iter()
            .map(|x| x.get(LE))
            .zip(self.name_ordinals.iter().map(|x| x.get(LE)))
    }

    /// Returns the export address table entry at the given address index.
    ///
    /// This may be a local address, or the address of a forwarded export entry.
    /// See [`Self::is_forward`] and [`Self::target_from_address`].
    ///
    /// `index` is a 0-based index into the export address table.
    pub fn address_by_index(&self, index: u32) -> Result<u32> {
        Ok(self
            .addresses
            .get(index as usize)
            .read_error("Invalid PE export address index")?
            .get(LE))
    }

    /// Returns the export address table entry at the given ordinal.
    ///
    /// This may be a local address, or the address of a forwarded export entry.
    /// See [`Self::is_forward`] and [`Self::target_from_address`].
    pub fn address_by_ordinal(&self, ordinal: u32) -> Result<u32> {
        self.address_by_index(ordinal.wrapping_sub(self.ordinal_base()))
    }

    /// Returns the target of the export at the given address index.
    ///
    /// `index` is a 0-based index into the export address table.
    pub fn target_by_index(&self, index: u32) -> Result<ExportTarget<'data>> {
        self.target_from_address(self.address_by_index(index)?)
    }

    /// Returns the target of the export at the given ordinal.
    pub fn target_by_ordinal(&self, ordinal: u32) -> Result<ExportTarget<'data>> {
        self.target_from_address(self.address_by_ordinal(ordinal)?)
    }

    /// Convert an export address table entry into a target.
    pub fn target_from_address(&self, address: u32) -> Result<ExportTarget<'data>> {
        Ok(if let Some(forward) = self.forward_string(address)? {
            let i = forward
                .iter()
                .position(|x| *x == b'.')
                .read_error("Missing PE forwarded export separator")?;
            let library = &forward[..i];
            match &forward[i + 1..] {
                [b'#', digits @ ..] => {
                    let ordinal =
                        parse_ordinal(digits).read_error("Invalid PE forwarded export ordinal")?;
                    ExportTarget::ForwardByOrdinal(library, ordinal)
                }
                [] => {
                    return Err(Error("Missing PE forwarded export name"));
                }
                name => ExportTarget::ForwardByName(library, name),
            }
        } else {
            ExportTarget::Address(address)
        })
    }

    fn forward_offset(&self, address: u32) -> Option<usize> {
        let offset = address.wrapping_sub(self.virtual_address) as usize;
        if offset < self.data.len() {
            Some(offset)
        } else {
            None
        }
    }

    /// Return true if the export address table entry is a forward.
    pub fn is_forward(&self, address: u32) -> bool {
        self.forward_offset(address).is_some()
    }

    /// Return the forward string if the export address table entry is a forward.
    pub fn forward_string(&self, address: u32) -> Result<Option<&'data [u8]>> {
        if let Some(offset) = self.forward_offset(address) {
            self.data
                .read_string_at(offset)
                .read_error("Invalid PE forwarded export address")
                .map(Some)
        } else {
            Ok(None)
        }
    }

    /// Convert an export name pointer table entry into a name.
    pub fn name_from_pointer(&self, name_pointer: u32) -> Result<&'data [u8]> {
        let offset = name_pointer.wrapping_sub(self.virtual_address);
        self.data
            .read_string_at(offset as usize)
            .read_error("Invalid PE export name pointer")
    }

    /// Returns the parsed exports in this table.
    pub fn exports(&self) -> Result<Vec<Export<'data>>> {
        // First, let's list all exports.
        let mut exports = Vec::new();
        let ordinal_base = self.ordinal_base();
        for (i, address) in self.addresses.iter().enumerate() {
            // Convert from an array index to an ordinal.
            let ordinal = ordinal_base.wrapping_add(i as u32);
            let target = self.target_from_address(address.get(LE))?;
            exports.push(Export {
                ordinal,
                target,
                // Might be populated later.
                name: None,
            });
        }

        // Now, check whether some (or all) of them have an associated name.
        // `ordinal_index` is a 0-based index into `addresses`.
        for (name_pointer, ordinal_index) in self.name_iter() {
            let name = self.name_from_pointer(name_pointer)?;
            exports
                .get_mut(ordinal_index as usize)
                .read_error("Invalid PE export ordinal")?
                .name = Some(name);
        }

        Ok(exports)
    }
}

fn parse_ordinal(digits: &[u8]) -> Option<u32> {
    if digits.is_empty() {
        return None;
    }
    let mut result: u32 = 0;
    for &c in digits {
        let x = (c as char).to_digit(10)?;
        result = result.checked_mul(10)?.checked_add(x)?;
    }
    Some(result)
}
<commit_msg>read/pe: fix parsing of empty export tables (#362)<commit_after>use alloc::vec::Vec;
use core::fmt::Debug;

use crate::read::{ByteString, Bytes, Error, ReadError, Result};
use crate::{pe, LittleEndian as LE, U16Bytes, U32Bytes};

/// Where an export is pointing to.
#[derive(Clone, Copy)]
pub enum ExportTarget<'data> {
    /// The address of the export, relative to the image base.
    Address(u32),
    /// Forwarded to an export ordinal in another DLL.
    ///
    /// This gives the name of the DLL, and the ordinal.
    ForwardByOrdinal(&'data [u8], u32),
    /// Forwarded to an export name in another DLL.
    ///
    /// This gives the name of the DLL, and the export name.
    ForwardByName(&'data [u8], &'data [u8]),
}

impl<'data> ExportTarget<'data> {
    /// Returns true if the target is an address.
    pub fn is_address(&self) -> bool {
        match self {
            ExportTarget::Address(_) => true,
            _ => false,
        }
    }

    /// Returns true if the export is forwarded to another DLL.
    pub fn is_forward(&self) -> bool {
        !self.is_address()
    }
}

/// An export from a PE file.
///
/// There are multiple kinds of PE exports (with or without a name, and local or forwarded).
#[derive(Clone, Copy)]
pub struct Export<'data> {
    /// The ordinal of the export.
    ///
    /// These are sequential, starting at a base specified in the DLL.
    pub ordinal: u32,
    /// The name of the export, if known.
    pub name: Option<&'data [u8]>,
    /// The target of this export.
    pub target: ExportTarget<'data>,
}

impl<'a> Debug for Export<'a> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
        f.debug_struct("Export")
            .field("ordinal", &self.ordinal)
            .field("name", &self.name.map(ByteString))
            .field("target", &self.target)
            .finish()
    }
}

impl<'a> Debug for ExportTarget<'a> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::result::Result<(), core::fmt::Error> {
        match self {
            ExportTarget::Address(address) => write!(f, "Address({:#x})", address),
            ExportTarget::ForwardByOrdinal(library, ordinal) => write!(
                f,
                "ForwardByOrdinal({:?}.#{})",
                ByteString(library),
                ordinal
            ),
            ExportTarget::ForwardByName(library, name) => write!(
                f,
                "ForwardByName({:?}.{:?})",
                ByteString(library),
                ByteString(name)
            ),
        }
    }
}

/// A partially parsed PE export table.
#[derive(Debug, Clone)]
pub struct ExportTable<'data> {
    data: Bytes<'data>,
    virtual_address: u32,
    directory: &'data pe::ImageExportDirectory,
    addresses: &'data [U32Bytes<LE>],
    names: &'data [U32Bytes<LE>],
    name_ordinals: &'data [U16Bytes<LE>],
}

impl<'data> ExportTable<'data> {
    /// Parse the export table given its section data and address.
    pub fn parse(data: &'data [u8], virtual_address: u32) -> Result<Self> {
        let data = Bytes(data);
        let directory = data
            .read_at::<pe::ImageExportDirectory>(0)
            .read_error("Invalid PE export dir size")?;

        let mut addresses = &[][..];
        let address_of_functions = directory.address_of_functions.get(LE);
        if address_of_functions != 0 {
            addresses = data
                .read_slice_at::<U32Bytes<_>>(
                    address_of_functions.wrapping_sub(virtual_address) as usize,
                    directory.number_of_functions.get(LE) as usize,
                )
                .read_error("Invalid PE export address table")?;
        }

        let mut names = &[][..];
        let mut name_ordinals = &[][..];
        let address_of_names = directory.address_of_names.get(LE);
        let address_of_name_ordinals = directory.address_of_name_ordinals.get(LE);
        if address_of_names != 0 {
            if address_of_name_ordinals == 0 {
                return Err(Error("Missing PE export ordinal table"));
            }

            let number = directory.number_of_names.get(LE) as usize;
            names = data
                .read_slice_at::<U32Bytes<_>>(
                    address_of_names.wrapping_sub(virtual_address) as usize,
                    number,
                )
                .read_error("Invalid PE export name pointer table")?;
            name_ordinals = data
                .read_slice_at::<U16Bytes<_>>(
                    address_of_name_ordinals.wrapping_sub(virtual_address) as usize,
                    number,
                )
                .read_error("Invalid PE export ordinal table")?;
        }

        Ok(ExportTable {
            data,
            virtual_address,
            directory,
            addresses,
            names,
            name_ordinals,
        })
    }

    /// Returns the header of the export table.
    pub fn directory(&self) -> &'data pe::ImageExportDirectory {
        self.directory
    }

    /// Returns the base value of ordinals.
    ///
    /// Adding this to an address index will give an ordinal.
    pub fn ordinal_base(&self) -> u32 {
        self.directory.base.get(LE)
    }

    /// Returns the unparsed address table.
    ///
    /// An address table entry may be a local address, or the address of a forwarded export entry.
    /// See [`Self::is_forward`] and [`Self::target_from_address`].
    pub fn addresses(&self) -> &'data [U32Bytes<LE>] {
        self.addresses
    }

    /// Returns the unparsed name pointer table.
    ///
    /// A name pointer table entry can be used with [`Self::name_from_pointer`].
    pub fn name_pointers(&self) -> &'data [U32Bytes<LE>] {
        self.names
    }

    /// Returns the unparsed ordinal table.
    ///
    /// An ordinal table entry is a 0-based index into the address table.
    /// See [`Self::address_by_index`] and [`Self::target_by_index`].
    pub fn name_ordinals(&self) -> &'data [U16Bytes<LE>] {
        self.name_ordinals
    }

    /// Returns an iterator for the entries in the name pointer table and ordinal table.
    ///
    /// A name pointer table entry can be used with [`Self::name_from_pointer`].
    ///
    /// An ordinal table entry is a 0-based index into the address table.
    /// See [`Self::address_by_index`] and [`Self::target_by_index`].
    pub fn name_iter(&self) -> impl Iterator<Item = (u32, u16)> + 'data {
        self.names
            .iter()
            .map(|x| x.get(LE))
            .zip(self.name_ordinals.iter().map(|x| x.get(LE)))
    }

    /// Returns the export address table entry at the given address index.
    ///
    /// This may be a local address, or the address of a forwarded export entry.
    /// See [`Self::is_forward`] and [`Self::target_from_address`].
    ///
    /// `index` is a 0-based index into the export address table.
    pub fn address_by_index(&self, index: u32) -> Result<u32> {
        Ok(self
            .addresses
            .get(index as usize)
            .read_error("Invalid PE export address index")?
            .get(LE))
    }

    /// Returns the export address table entry at the given ordinal.
    ///
    /// This may be a local address, or the address of a forwarded export entry.
    /// See [`Self::is_forward`] and [`Self::target_from_address`].
    pub fn address_by_ordinal(&self, ordinal: u32) -> Result<u32> {
        self.address_by_index(ordinal.wrapping_sub(self.ordinal_base()))
    }

    /// Returns the target of the export at the given address index.
    ///
    /// `index` is a 0-based index into the export address table.
    pub fn target_by_index(&self, index: u32) -> Result<ExportTarget<'data>> {
        self.target_from_address(self.address_by_index(index)?)
    }

    /// Returns the target of the export at the given ordinal.
    pub fn target_by_ordinal(&self, ordinal: u32) -> Result<ExportTarget<'data>> {
        self.target_from_address(self.address_by_ordinal(ordinal)?)
    }

    /// Convert an export address table entry into a target.
    pub fn target_from_address(&self, address: u32) -> Result<ExportTarget<'data>> {
        Ok(if let Some(forward) = self.forward_string(address)? {
            let i = forward
                .iter()
                .position(|x| *x == b'.')
                .read_error("Missing PE forwarded export separator")?;
            let library = &forward[..i];
            match &forward[i + 1..] {
                [b'#', digits @ ..] => {
                    let ordinal =
                        parse_ordinal(digits).read_error("Invalid PE forwarded export ordinal")?;
                    ExportTarget::ForwardByOrdinal(library, ordinal)
                }
                [] => {
                    return Err(Error("Missing PE forwarded export name"));
                }
                name => ExportTarget::ForwardByName(library, name),
            }
        } else {
            ExportTarget::Address(address)
        })
    }

    fn forward_offset(&self, address: u32) -> Option<usize> {
        let offset = address.wrapping_sub(self.virtual_address) as usize;
        if offset < self.data.len() {
            Some(offset)
        } else {
            None
        }
    }

    /// Return true if the export address table entry is a forward.
    pub fn is_forward(&self, address: u32) -> bool {
        self.forward_offset(address).is_some()
    }

    /// Return the forward string if the export address table entry is a forward.
    pub fn forward_string(&self, address: u32) -> Result<Option<&'data [u8]>> {
        if let Some(offset) = self.forward_offset(address) {
            self.data
                .read_string_at(offset)
                .read_error("Invalid PE forwarded export address")
                .map(Some)
        } else {
            Ok(None)
        }
    }

    /// Convert an export name pointer table entry into a name.
    pub fn name_from_pointer(&self, name_pointer: u32) -> Result<&'data [u8]> {
        let offset = name_pointer.wrapping_sub(self.virtual_address);
        self.data
            .read_string_at(offset as usize)
            .read_error("Invalid PE export name pointer")
    }

    /// Returns the parsed exports in this table.
    pub fn exports(&self) -> Result<Vec<Export<'data>>> {
        // First, let's list all exports.
        let mut exports = Vec::new();
        let ordinal_base = self.ordinal_base();
        for (i, address) in self.addresses.iter().enumerate() {
            // Convert from an array index to an ordinal.
            let ordinal = ordinal_base.wrapping_add(i as u32);
            let target = self.target_from_address(address.get(LE))?;
            exports.push(Export {
                ordinal,
                target,
                // Might be populated later.
                name: None,
            });
        }

        // Now, check whether some (or all) of them have an associated name.
        // `ordinal_index` is a 0-based index into `addresses`.
        for (name_pointer, ordinal_index) in self.name_iter() {
            let name = self.name_from_pointer(name_pointer)?;
            exports
                .get_mut(ordinal_index as usize)
                .read_error("Invalid PE export ordinal")?
                .name = Some(name);
        }

        Ok(exports)
    }
}

fn parse_ordinal(digits: &[u8]) -> Option<u32> {
    if digits.is_empty() {
        return None;
    }
    let mut result: u32 = 0;
    for &c in digits {
        let x = (c as char).to_digit(10)?;
        result = result.checked_mul(10)?.checked_add(x)?;
    }
    Some(result)
}
<|endoftext|>


<commit_before>#![cfg_attr(test, feature(test))]

#[cfg(test)] extern crate test;

pub mod nfa;

use self::nfa::NFA;
use self::nfa::CharacterClass;
use std::collections::BTreeMap;
use std::collections::btree_map;
use std::cmp::Ordering;
use std::ops::Index;

#[derive(Clone)]
struct Metadata {
    statics: u32,
    dynamics: u32,
    stars: u32,
    param_names: Vec<String>,
}

impl Metadata {
    pub fn new() -> Metadata {
        Metadata{ statics: 0, dynamics: 0, stars: 0, param_names: Vec::new() }
    }
}

impl Ord for Metadata {
    fn cmp(&self, other: &Metadata) -> Ordering {
        if self.stars > other.stars {
            Ordering::Less
        } else if self.stars < other.stars {
            Ordering::Greater
        } else if self.dynamics > other.dynamics {
            Ordering::Less
        } else if self.dynamics < other.dynamics {
            Ordering::Greater
        } else if self.statics > other.statics {
            Ordering::Less
        } else if self.statics < other.statics {
            Ordering::Greater
        } else {
            Ordering::Equal
        }
    }
}

impl PartialOrd for Metadata {
    fn partial_cmp(&self, other: &Metadata) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Metadata {
    fn eq(&self, other: &Metadata) -> bool {
        self.statics == other.statics && self.dynamics == other.dynamics && self.stars == other.stars
    }
}

impl Eq for Metadata {}

#[derive(PartialEq, Clone, Debug)]
pub struct Params {
    map: BTreeMap<String, String>
}

impl Params {
    pub fn new() -> Params {
        Params{ map: BTreeMap::new() }
    }

    pub fn insert(&mut self, key: String, value: String) {
        self.map.insert(key, value);
    }

    pub fn get(&self, key: &str) -> Option<&str> {
        self.map.get(key).map(|s| &s[..])
    }

    pub fn iter<'a>(&'a self) -> Iter<'a> {
        Iter(self.map.iter())
    }
}

impl<'a> Index<&'a str> for Params {
    type Output = String;
    fn index(&self, index: &'a str) -> &String {
        match self.map.get(index) {
            None => panic!(format!("params[{}] did not exist", index)),
            Some(s) => s,
        }
    }
}

impl<'a> IntoIterator for &'a Params {
    type IntoIter = Iter<'a>;
    type Item = (&'a str, &'a str);

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

pub struct Iter<'a>(btree_map::Iter<'a, String, String>);

impl<'a> Iterator for Iter<'a> {
    type Item = (&'a str, &'a str);

    #[inline]
    fn next(&mut self) -> Option<(&'a str, &'a str)> {
        self.0.next().map(|(k, v)| (&**k, &**v))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.size_hint()
    }
}

pub struct Match<T> {
    pub handler: T,
    pub params: Params
}

impl<T> Match<T> {
    pub fn new(handler: T, params: Params) -> Match<T> {
        Match{ handler: handler, params: params }
    }
}

#[derive(Clone)]
pub struct Router<T> {
    nfa: NFA<Metadata>,
    handlers: BTreeMap<usize, T>
}

impl<T> Router<T> {
    pub fn new() -> Router<T> {
        Router{ nfa: NFA::new(), handlers: BTreeMap::new() }
    }

    pub fn add(&mut self, mut route: &str, dest: T) {
        if route.len() != 0 && route.as_bytes()[0] == b'/' {
            route = &route[1..];
        }

        let nfa = &mut self.nfa;
        let mut state = 0;
        let mut metadata = Metadata::new();

        for (i, segment) in route.split('/').enumerate() {
            if i > 0 { state = nfa.put(state, CharacterClass::valid_char('/')); }

            if segment.len() > 0 && segment.as_bytes()[0] == b':' {
                state = process_dynamic_segment(nfa, state);
                metadata.dynamics += 1;
                metadata.param_names.push(segment[1..].to_string());
            } else if segment.len() > 0 && segment.as_bytes()[0] == b'*' {
                state = process_star_state(nfa, state);
                metadata.stars += 1;
                metadata.param_names.push(segment[1..].to_string());
            } else {
                state = process_static_segment(segment, nfa, state);
                metadata.statics += 1;
            }
        }

        nfa.acceptance(state);
        nfa.metadata(state, metadata);
        self.handlers.insert(state, dest);
    }

    pub fn recognize<'a>(&'a self, mut path: &str) -> Result<Match<&'a T>, String> {
        if path.len() != 0 && path.as_bytes()[0] == b'/' {
            path = &path[1..];
        }

        let nfa = &self.nfa;
        let result = nfa.process(path, |index| nfa.get(index).metadata.as_ref().unwrap());

        match result {
            Ok(nfa_match) => {
                let mut map = Params::new();
                let state = &nfa.get(nfa_match.state);
                let metadata = state.metadata.as_ref().unwrap();
                let param_names = metadata.param_names.clone();

                for (i, capture) in nfa_match.captures.iter().enumerate() {
                    map.insert(param_names[i].to_string(), capture.to_string());
                }

                let handler = self.handlers.get(&nfa_match.state).unwrap();
                Ok(Match::new(handler, map))
            },
            Err(str) => Err(str)
        }
    }
}

fn process_static_segment<T>(segment: &str, nfa: &mut NFA<T>, mut state: usize) -> usize {
    for char in segment.chars() {
        state = nfa.put(state, CharacterClass::valid_char(char));
    }

    state
}

fn process_dynamic_segment<T>(nfa: &mut NFA<T>, mut state: usize) -> usize {
    state = nfa.put(state, CharacterClass::invalid_char('/'));
    nfa.put_state(state, state);
    nfa.start_capture(state);
    nfa.end_capture(state);

    state
}

fn process_star_state<T>(nfa: &mut NFA<T>, mut state: usize) -> usize {
    state = nfa.put(state, CharacterClass::any());
    nfa.put_state(state, state);
    nfa.start_capture(state);
    nfa.end_capture(state);

    state
}

#[test]
fn basic_router() {
    let mut router = Router::new();

    router.add("/thomas", "Thomas".to_string());
    router.add("/tom", "Tom".to_string());
    router.add("/wycats", "Yehuda".to_string());

    let m = router.recognize("/thomas").unwrap();

    assert_eq!(*m.handler, "Thomas".to_string());
    assert_eq!(m.params, Params::new());
}

#[test]
fn root_router() {
    let mut router = Router::new();
    router.add("/", 10);
    assert_eq!(*router.recognize("/").unwrap().handler, 10)
}

#[test]
fn empty_path() {
  let mut router = Router::new();
  router.add("/", 12);
  assert_eq!(*router.recognize("").unwrap().handler, 12)
}

#[test]
fn empty_route() {
  let mut router = Router::new();
  router.add("", 12);
  assert_eq!(*router.recognize("/").unwrap().handler, 12)
}

#[test]
fn ambiguous_router() {
    let mut router = Router::new();

    router.add("/posts/new", "new".to_string());
    router.add("/posts/:id", "id".to_string());

    let id = router.recognize("/posts/1").unwrap();

    assert_eq!(*id.handler, "id".to_string());
    assert_eq!(id.params, params("id", "1"));

    let new = router.recognize("/posts/new").unwrap();
    assert_eq!(*new.handler, "new".to_string());
    assert_eq!(new.params, Params::new());
}

#[test]
fn ambiguous_router_b() {
    let mut router = Router::new();

    router.add("/posts/:id", "id".to_string());
    router.add("/posts/new", "new".to_string());

    let id = router.recognize("/posts/1").unwrap();

    assert_eq!(*id.handler, "id".to_string());
    assert_eq!(id.params, params("id", "1"));

    let new = router.recognize("/posts/new").unwrap();
    assert_eq!(*new.handler, "new".to_string());
    assert_eq!(new.params, Params::new());
}

#[test]
fn multiple_params() {
    let mut router = Router::new();

    router.add("/posts/:post_id/comments/:id", "comment".to_string());
    router.add("/posts/:post_id/comments", "comments".to_string());

    let com = router.recognize("/posts/12/comments/100").unwrap();
    let coms = router.recognize("/posts/12/comments").unwrap();

    assert_eq!(*com.handler, "comment".to_string());
    assert_eq!(com.params, two_params("post_id", "12", "id", "100"));

    assert_eq!(*coms.handler, "comments".to_string());
    assert_eq!(coms.params, params("post_id", "12"));
    assert_eq!(coms.params["post_id"], "12".to_string());
}

#[test]
fn star() {
    let mut router = Router::new();

    router.add("*foo", "test".to_string());
    router.add("/bar/*foo", "test2".to_string());

    let m = router.recognize("/test").unwrap();
    assert_eq!(*m.handler, "test".to_string());
    assert_eq!(m.params, params("foo", "test"));

    let m = router.recognize("/foo/bar").unwrap();
    assert_eq!(*m.handler, "test".to_string());
    assert_eq!(m.params, params("foo", "foo/bar"));

    let m = router.recognize("/bar/foo").unwrap();
    assert_eq!(*m.handler, "test".to_string());
    assert_eq!(m.params, params("foo", "bar/foo"));
}

#[bench]
fn benchmark(b: &mut test::Bencher) {
    let mut router = Router::new();
    router.add("/posts/:post_id/comments/:id", "comment".to_string());
    router.add("/posts/:post_id/comments", "comments".to_string());
    router.add("/posts/:post_id", "post".to_string());
    router.add("/posts", "posts".to_string());
    router.add("/comments", "comments2".to_string());
    router.add("/comments/:id", "comment2".to_string());

    b.iter(|| {
        router.recognize("/posts/100/comments/200")
    });
}

#[allow(dead_code)]
fn params(key: &str, val: &str) -> Params {
    let mut map = Params::new();
    map.insert(key.to_string(), val.to_string());
    map
}

#[allow(dead_code)]
fn two_params(k1: &str, v1: &str, k2: &str, v2: &str) -> Params {
    let mut map = Params::new();
    map.insert(k1.to_string(), v1.to_string());
    map.insert(k2.to_string(), v2.to_string());
    map
}
<commit_msg>fix a bug in PathParams get method return value.<commit_after>#![cfg_attr(test, feature(test))]

#[cfg(test)] extern crate test;

pub mod nfa;

use self::nfa::NFA;
use self::nfa::CharacterClass;
use std::collections::BTreeMap;
use std::collections::btree_map;
use std::cmp::Ordering;
use std::ops::Index;

#[derive(Clone)]
struct Metadata {
    statics: u32,
    dynamics: u32,
    stars: u32,
    param_names: Vec<String>,
}

impl Metadata {
    pub fn new() -> Metadata {
        Metadata{ statics: 0, dynamics: 0, stars: 0, param_names: Vec::new() }
    }
}

impl Ord for Metadata {
    fn cmp(&self, other: &Metadata) -> Ordering {
        if self.stars > other.stars {
            Ordering::Less
        } else if self.stars < other.stars {
            Ordering::Greater
        } else if self.dynamics > other.dynamics {
            Ordering::Less
        } else if self.dynamics < other.dynamics {
            Ordering::Greater
        } else if self.statics > other.statics {
            Ordering::Less
        } else if self.statics < other.statics {
            Ordering::Greater
        } else {
            Ordering::Equal
        }
    }
}

impl PartialOrd for Metadata {
    fn partial_cmp(&self, other: &Metadata) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Metadata {
    fn eq(&self, other: &Metadata) -> bool {
        self.statics == other.statics && self.dynamics == other.dynamics && self.stars == other.stars
    }
}

impl Eq for Metadata {}

#[derive(PartialEq, Clone, Debug)]
pub struct Params {
    map: BTreeMap<String, String>
}

impl Params {
    pub fn new() -> Params {
        Params{ map: BTreeMap::new() }
    }

    pub fn insert(&mut self, key: String, value: String) {
        self.map.insert(key, value);
    }

    pub fn get(&self, key: &str) -> Option<Vec<&str>> {
        self.map.get(key).map(|s| vec![&s[..]])
    }

    pub fn iter<'a>(&'a self) -> Iter<'a> {
        Iter(self.map.iter())
    }
}

impl<'a> Index<&'a str> for Params {
    type Output = String;
    fn index(&self, index: &'a str) -> &String {
        match self.map.get(index) {
            None => panic!(format!("params[{}] did not exist", index)),
            Some(s) => s,
        }
    }
}

impl<'a> IntoIterator for &'a Params {
    type IntoIter = Iter<'a>;
    type Item = (&'a str, &'a str);

    fn into_iter(self) -> Iter<'a> {
        self.iter()
    }
}

pub struct Iter<'a>(btree_map::Iter<'a, String, String>);

impl<'a> Iterator for Iter<'a> {
    type Item = (&'a str, &'a str);

    #[inline]
    fn next(&mut self) -> Option<(&'a str, &'a str)> {
        self.0.next().map(|(k, v)| (&**k, &**v))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.0.size_hint()
    }
}

pub struct Match<T> {
    pub handler: T,
    pub params: Params
}

impl<T> Match<T> {
    pub fn new(handler: T, params: Params) -> Match<T> {
        Match{ handler: handler, params: params }
    }
}

#[derive(Clone)]
pub struct Router<T> {
    nfa: NFA<Metadata>,
    handlers: BTreeMap<usize, T>
}

impl<T> Router<T> {
    pub fn new() -> Router<T> {
        Router{ nfa: NFA::new(), handlers: BTreeMap::new() }
    }

    pub fn add(&mut self, mut route: &str, dest: T) {
        if route.len() != 0 && route.as_bytes()[0] == b'/' {
            route = &route[1..];
        }

        let nfa = &mut self.nfa;
        let mut state = 0;
        let mut metadata = Metadata::new();

        for (i, segment) in route.split('/').enumerate() {
            if i > 0 { state = nfa.put(state, CharacterClass::valid_char('/')); }

            if segment.len() > 0 && segment.as_bytes()[0] == b':' {
                state = process_dynamic_segment(nfa, state);
                metadata.dynamics += 1;
                metadata.param_names.push(segment[1..].to_string());
            } else if segment.len() > 0 && segment.as_bytes()[0] == b'*' {
                state = process_star_state(nfa, state);
                metadata.stars += 1;
                metadata.param_names.push(segment[1..].to_string());
            } else {
                state = process_static_segment(segment, nfa, state);
                metadata.statics += 1;
            }
        }

        nfa.acceptance(state);
        nfa.metadata(state, metadata);
        self.handlers.insert(state, dest);
    }

    pub fn recognize<'a>(&'a self, mut path: &str) -> Result<Match<&'a T>, String> {
        if path.len() != 0 && path.as_bytes()[0] == b'/' {
            path = &path[1..];
        }

        let nfa = &self.nfa;
        let result = nfa.process(path, |index| nfa.get(index).metadata.as_ref().unwrap());

        match result {
            Ok(nfa_match) => {
                let mut map = Params::new();
                let state = &nfa.get(nfa_match.state);
                let metadata = state.metadata.as_ref().unwrap();
                let param_names = metadata.param_names.clone();

                for (i, capture) in nfa_match.captures.iter().enumerate() {
                    map.insert(param_names[i].to_string(), capture.to_string());
                }

                let handler = self.handlers.get(&nfa_match.state).unwrap();
                Ok(Match::new(handler, map))
            },
            Err(str) => Err(str)
        }
    }
}

fn process_static_segment<T>(segment: &str, nfa: &mut NFA<T>, mut state: usize) -> usize {
    for char in segment.chars() {
        state = nfa.put(state, CharacterClass::valid_char(char));
    }

    state
}

fn process_dynamic_segment<T>(nfa: &mut NFA<T>, mut state: usize) -> usize {
    state = nfa.put(state, CharacterClass::invalid_char('/'));
    nfa.put_state(state, state);
    nfa.start_capture(state);
    nfa.end_capture(state);

    state
}

fn process_star_state<T>(nfa: &mut NFA<T>, mut state: usize) -> usize {
    state = nfa.put(state, CharacterClass::any());
    nfa.put_state(state, state);
    nfa.start_capture(state);
    nfa.end_capture(state);

    state
}

#[test]
fn basic_router() {
    let mut router = Router::new();

    router.add("/thomas", "Thomas".to_string());
    router.add("/tom", "Tom".to_string());
    router.add("/wycats", "Yehuda".to_string());

    let m = router.recognize("/thomas").unwrap();

    assert_eq!(*m.handler, "Thomas".to_string());
    assert_eq!(m.params, Params::new());
}

#[test]
fn root_router() {
    let mut router = Router::new();
    router.add("/", 10);
    assert_eq!(*router.recognize("/").unwrap().handler, 10)
}

#[test]
fn empty_path() {
  let mut router = Router::new();
  router.add("/", 12);
  assert_eq!(*router.recognize("").unwrap().handler, 12)
}

#[test]
fn empty_route() {
  let mut router = Router::new();
  router.add("", 12);
  assert_eq!(*router.recognize("/").unwrap().handler, 12)
}

#[test]
fn ambiguous_router() {
    let mut router = Router::new();

    router.add("/posts/new", "new".to_string());
    router.add("/posts/:id", "id".to_string());

    let id = router.recognize("/posts/1").unwrap();

    assert_eq!(*id.handler, "id".to_string());
    assert_eq!(id.params, params("id", "1"));

    let new = router.recognize("/posts/new").unwrap();
    assert_eq!(*new.handler, "new".to_string());
    assert_eq!(new.params, Params::new());
}

#[test]
fn ambiguous_router_b() {
    let mut router = Router::new();

    router.add("/posts/:id", "id".to_string());
    router.add("/posts/new", "new".to_string());

    let id = router.recognize("/posts/1").unwrap();

    assert_eq!(*id.handler, "id".to_string());
    assert_eq!(id.params, params("id", "1"));

    let new = router.recognize("/posts/new").unwrap();
    assert_eq!(*new.handler, "new".to_string());
    assert_eq!(new.params, Params::new());
}

#[test]
fn multiple_params() {
    let mut router = Router::new();

    router.add("/posts/:post_id/comments/:id", "comment".to_string());
    router.add("/posts/:post_id/comments", "comments".to_string());

    let com = router.recognize("/posts/12/comments/100").unwrap();
    let coms = router.recognize("/posts/12/comments").unwrap();

    assert_eq!(*com.handler, "comment".to_string());
    assert_eq!(com.params, two_params("post_id", "12", "id", "100"));

    assert_eq!(*coms.handler, "comments".to_string());
    assert_eq!(coms.params, params("post_id", "12"));
    assert_eq!(coms.params["post_id"], "12".to_string());
}

#[test]
fn star() {
    let mut router = Router::new();

    router.add("*foo", "test".to_string());
    router.add("/bar/*foo", "test2".to_string());

    let m = router.recognize("/test").unwrap();
    assert_eq!(*m.handler, "test".to_string());
    assert_eq!(m.params, params("foo", "test"));

    let m = router.recognize("/foo/bar").unwrap();
    assert_eq!(*m.handler, "test".to_string());
    assert_eq!(m.params, params("foo", "foo/bar"));

    let m = router.recognize("/bar/foo").unwrap();
    assert_eq!(*m.handler, "test".to_string());
    assert_eq!(m.params, params("foo", "bar/foo"));
}

#[bench]
fn benchmark(b: &mut test::Bencher) {
    let mut router = Router::new();
    router.add("/posts/:post_id/comments/:id", "comment".to_string());
    router.add("/posts/:post_id/comments", "comments".to_string());
    router.add("/posts/:post_id", "post".to_string());
    router.add("/posts", "posts".to_string());
    router.add("/comments", "comments2".to_string());
    router.add("/comments/:id", "comment2".to_string());

    b.iter(|| {
        router.recognize("/posts/100/comments/200")
    });
}

#[allow(dead_code)]
fn params(key: &str, val: &str) -> Params {
    let mut map = Params::new();
    map.insert(key.to_string(), val.to_string());
    map
}

#[allow(dead_code)]
fn two_params(k1: &str, v1: &str, k2: &str, v2: &str) -> Params {
    let mut map = Params::new();
    map.insert(k1.to_string(), v1.to_string());
    map.insert(k2.to_string(), v2.to_string());
    map
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::mem;
use std::ptr;
use std::sync::Arc;
use smallvec::SmallVec;

use device::Device;
use format::FormatDesc;
use framebuffer::RenderPass;
use framebuffer::LayoutAttachmentDescription;
use framebuffer::LayoutPassDescription;
use framebuffer::LayoutPassDependencyDescription;
use image::traits::Image;
use image::traits::ImageView;

use OomError;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// Defines the layout of multiple subpasses.
pub struct UnsafeRenderPass {
    renderpass: vk::RenderPass,
    device: Arc<Device>,
}

impl UnsafeRenderPass {
    /// Builds a new renderpass.
    ///
    /// This function calls the methods of the `Layout` implementation and builds the
    /// corresponding Vulkan object.
    ///
    /// # Safety
    ///
    /// This function doesn't check whether all the restrictions in the attachments, passes and
    /// passes dependencies were enforced.
    ///
    /// See the documentation of the structs of this module for more info about these restrictions.
    ///
    /// # Panic
    ///
    /// Can panick if it detects some violations in the restrictions. Only unexpensive checks are
    /// performed. `debug_assert!` is used, so some restrictions are only checked in debug
    /// mode.
    ///
    pub unsafe fn new<Ia, Ip, Id>(device: &Arc<Device>, attachments: Ia, passes: Ip,
                                  pass_dependencies: Id)
               -> Result<UnsafeRenderPass, OomError>
        where Ia: ExactSizeIterator<Item = LayoutAttachmentDescription> + Clone,        // with specialization we can handle the "Clone" restriction internally
              Ip: ExactSizeIterator<Item = LayoutPassDescription> + Clone,      // with specialization we can handle the "Clone" restriction internally
              Id: ExactSizeIterator<Item = LayoutPassDependencyDescription>
    {
        let vk = device.pointers();

        // TODO: check the validity of the renderpass layout with debug_assert!

        let attachments = attachments.clone().map(|attachment| {
            vk::AttachmentDescription {
                flags: 0,       // FIXME: may alias flag
                format: attachment.format as u32,
                samples: attachment.samples,
                loadOp: attachment.load as u32,
                storeOp: attachment.store as u32,
                stencilLoadOp: attachment.load as u32,       // TODO: allow user to choose
                stencilStoreOp: attachment.store as u32,      // TODO: allow user to choose
                initialLayout: attachment.initial_layout as u32,
                finalLayout: attachment.final_layout as u32,
            }
        }).collect::<SmallVec<[_; 16]>>();

        // We need to pass pointers to vkAttachmentReference structs when creating the renderpass.
        // Therefore we need to allocate them in advance.
        //
        // This block allocates, for each pass, in order, all color attachment references, then all
        // input attachment references, then all resolve attachment references, then the depth
        // stencil attachment reference.
        let attachment_references = passes.clone().flat_map(|pass| {
            debug_assert!(pass.resolve_attachments.is_empty() ||
                          pass.resolve_attachments.len() == pass.color_attachments.len());
            let resolve = pass.resolve_attachments.into_iter().map(|(offset, img_la)| {
                debug_assert!(offset < attachments.len());
                vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, }
            });

            let color = pass.color_attachments.into_iter().map(|(offset, img_la)| {
                debug_assert!(offset < attachments.len());
                vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, }
            });

            let input = pass.input_attachments.into_iter().map(|(offset, img_la)| {
                debug_assert!(offset < attachments.len());
                vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, }
            });

            let depthstencil = if let Some((offset, img_la)) = pass.depth_stencil {
                Some(vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, })
            } else {
                None
            }.into_iter();

            color.chain(input).chain(resolve).chain(depthstencil)
        }).collect::<SmallVec<[_; 16]>>();

        // Same as `attachment_references` but only for the preserve attachments.
        // This is separate because attachment references are u32s and not `vkAttachmentReference`
        // structs.
        let preserve_attachments_references = passes.clone().flat_map(|pass| {
            pass.preserve_attachments.into_iter().map(|offset| offset as u32)
        }).collect::<SmallVec<[_; 16]>>();

        // Now iterating over passes.
        // `ref_index` and `preserve_ref_index` are increased during the loop and point to the
        // next element to use in respectively `attachment_references` and
        // `preserve_attachments_references`.
        let mut ref_index = 0usize;
        let mut preserve_ref_index = 0usize;
        let passes = passes.clone().map(|pass| {
            assert!(pass.color_attachments.len() as u32 <=
                    device.physical_device().limits().max_color_attachments());

            let color_attachments = attachment_references.as_ptr().offset(ref_index as isize);
            ref_index += pass.color_attachments.len();
            let input_attachments = attachment_references.as_ptr().offset(ref_index as isize);
            ref_index += pass.input_attachments.len();
            let resolve_attachments = attachment_references.as_ptr().offset(ref_index as isize);
            ref_index += pass.resolve_attachments.len();
            let depth_stencil = if pass.depth_stencil.is_some() {
                let a = attachment_references.as_ptr().offset(ref_index as isize);
                ref_index += 1;
                a
            } else {
                ptr::null()
            };

            let preserve_attachments = preserve_attachments_references.as_ptr().offset(preserve_ref_index as isize);
            preserve_ref_index += pass.preserve_attachments.len();

            vk::SubpassDescription {
                flags: 0,   // reserved
                pipelineBindPoint: vk::PIPELINE_BIND_POINT_GRAPHICS,
                inputAttachmentCount: pass.input_attachments.len() as u32,
                pInputAttachments: if pass.input_attachments.is_empty() { ptr::null() }
                                   else { input_attachments },
                colorAttachmentCount: pass.color_attachments.len() as u32,
                pColorAttachments: if pass.color_attachments.is_empty() { ptr::null() }
                                   else { color_attachments },
                pResolveAttachments: if pass.resolve_attachments.is_empty() { ptr::null() }
                                     else { resolve_attachments },
                pDepthStencilAttachment: depth_stencil,
                preserveAttachmentCount: pass.preserve_attachments.len() as u32,
                pPreserveAttachments: if pass.preserve_attachments.is_empty() { ptr::null() }
                                      else { preserve_attachments },
            }
        }).collect::<SmallVec<[_; 16]>>();

        assert!(!passes.is_empty());
        // If these assertions fails, there's a serious bug in the code above ^.
        debug_assert!(ref_index == attachment_references.len());
        debug_assert!(preserve_ref_index == preserve_attachments_references.len());

        let dependencies = pass_dependencies.map(|dependency| {
            debug_assert!(dependency.source_subpass < passes.len());
            debug_assert!(dependency.destination_subpass < passes.len());

            vk::SubpassDependency {
                srcSubpass: dependency.source_subpass as u32,
                dstSubpass: dependency.destination_subpass as u32,
                srcStageMask: vk::PIPELINE_STAGE_ALL_GRAPHICS_BIT,      // FIXME:
                dstStageMask: vk::PIPELINE_STAGE_ALL_GRAPHICS_BIT,      // FIXME:
                srcAccessMask: 0x0001FFFF,       // FIXME:
                dstAccessMask: 0x0001FFFF,       // FIXME:
                dependencyFlags: if dependency.by_region { vk::DEPENDENCY_BY_REGION_BIT } else { 0 },
            }
        }).collect::<SmallVec<[_; 16]>>();

        let renderpass = {
            let infos = vk::RenderPassCreateInfo {
                sType: vk::STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                attachmentCount: attachments.len() as u32,
                pAttachments: if attachments.is_empty() { ptr::null() }
                              else { attachments.as_ptr() },
                subpassCount: passes.len() as u32,
                pSubpasses: if passes.is_empty() { ptr::null() } else { passes.as_ptr() },
                dependencyCount: dependencies.len() as u32,
                pDependencies: if dependencies.is_empty() { ptr::null() }
                               else { dependencies.as_ptr() },
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateRenderPass(device.internal_object(), &infos,
                                                  ptr::null(), &mut output)));
            output
        };

        Ok(UnsafeRenderPass {
            device: device.clone(),
            renderpass: renderpass,
        })
    }

    /// Returns the device that was used to create this render pass.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }
}

unsafe impl VulkanObject for UnsafeRenderPass {
    type Object = vk::RenderPass;

    #[inline]
    fn internal_object(&self) -> vk::RenderPass {
        self.renderpass
    }
}

unsafe impl RenderPass for UnsafeRenderPass {
    #[inline]
    fn render_pass(&self) -> &UnsafeRenderPass {
        self
    }
}

impl Drop for UnsafeRenderPass {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroyRenderPass(self.device.internal_object(), self.renderpass, ptr::null());
        }
    }
}
<commit_msg>Perform some debug checks on the render pass parameters<commit_after>// Copyright (c) 2016 The vulkano 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. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::mem;
use std::ptr;
use std::sync::Arc;
use smallvec::SmallVec;

use device::Device;
use format::FormatDesc;
use framebuffer::RenderPass;
use framebuffer::LayoutAttachmentDescription;
use framebuffer::LayoutPassDescription;
use framebuffer::LayoutPassDependencyDescription;
use image::traits::Image;
use image::traits::ImageView;

use OomError;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;

/// Defines the layout of multiple subpasses.
pub struct UnsafeRenderPass {
    renderpass: vk::RenderPass,
    device: Arc<Device>,
}

impl UnsafeRenderPass {
    /// Builds a new renderpass.
    ///
    /// This function calls the methods of the `Layout` implementation and builds the
    /// corresponding Vulkan object.
    ///
    /// # Safety
    ///
    /// This function doesn't check whether all the restrictions in the attachments, passes and
    /// passes dependencies were enforced.
    ///
    /// See the documentation of the structs of this module for more info about these restrictions.
    ///
    /// # Panic
    ///
    /// Can panick if it detects some violations in the restrictions. Only unexpensive checks are
    /// performed. `debug_assert!` is used, so some restrictions are only checked in debug
    /// mode.
    ///
    pub unsafe fn new<Ia, Ip, Id>(device: &Arc<Device>, attachments: Ia, passes: Ip,
                                  pass_dependencies: Id)
               -> Result<UnsafeRenderPass, OomError>
        where Ia: ExactSizeIterator<Item = LayoutAttachmentDescription> + Clone,        // with specialization we can handle the "Clone" restriction internally
              Ip: ExactSizeIterator<Item = LayoutPassDescription> + Clone,      // with specialization we can handle the "Clone" restriction internally
              Id: ExactSizeIterator<Item = LayoutPassDependencyDescription>
    {
        let vk = device.pointers();

        // TODO: check the validity of the renderpass layout with debug_assert!
        //   - If the first use of an attachment in this render pass is as an input attachment, and the attachment is not also used as a color or depth/stencil attachment in the same subpass, then loadOp must not be VK_ATTACHMENT_LOAD_OP_CLEAR

        let attachments = attachments.clone().map(|attachment| {
            debug_assert!(attachment.samples.is_power_of_two());

            vk::AttachmentDescription {
                flags: 0,       // FIXME: may alias flag
                format: attachment.format as u32,
                samples: attachment.samples,
                loadOp: attachment.load as u32,
                storeOp: attachment.store as u32,
                stencilLoadOp: attachment.load as u32,       // TODO: allow user to choose
                stencilStoreOp: attachment.store as u32,      // TODO: allow user to choose
                initialLayout: attachment.initial_layout as u32,
                finalLayout: attachment.final_layout as u32,
            }
        }).collect::<SmallVec<[_; 16]>>();

        // We need to pass pointers to vkAttachmentReference structs when creating the renderpass.
        // Therefore we need to allocate them in advance.
        //
        // This block allocates, for each pass, in order, all color attachment references, then all
        // input attachment references, then all resolve attachment references, then the depth
        // stencil attachment reference.
        let attachment_references = passes.clone().flat_map(|pass| {
            // Performing some validation with debug asserts.
            debug_assert!(pass.resolve_attachments.is_empty() ||
                          pass.resolve_attachments.len() == pass.color_attachments.len());
            debug_assert!(pass.resolve_attachments.iter().all(|a| {
                              attachments[a.0].samples == 1
                          }));
            debug_assert!(pass.resolve_attachments.is_empty() ||
                          pass.color_attachments.iter().all(|a| {
                              attachments[a.0].samples > 1
                          }));
            debug_assert!(pass.resolve_attachments.is_empty() ||
                          pass.resolve_attachments.iter().zip(pass.color_attachments.iter())
                              .all(|(r, c)| {
                                  attachments[r.0].format == attachments[c.0].format
                              }));
            debug_assert!(pass.color_attachments.iter().cloned()
                              .chain(pass.depth_stencil.clone().into_iter())
                              .chain(pass.input_attachments.iter().cloned())
                              .chain(pass.resolve_attachments.iter().cloned())
                              .all(|(a, _)| {
                                  pass.preserve_attachments.iter().find(|&&b| a == b).is_none()
                              }));
            debug_assert!(pass.color_attachments.iter().cloned()
                              .chain(pass.depth_stencil.clone().into_iter())
                              .all(|(atch, layout)| {
                                  if let Some(r) = pass.input_attachments.iter()
                                                                         .find(|r| r.0 == atch)
                                  {
                                      r.1 == layout
                                  } else {
                                      true
                                  }
                              }));

            let resolve = pass.resolve_attachments.into_iter().map(|(offset, img_la)| {
                debug_assert!(offset < attachments.len());
                vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, }
            });

            let color = pass.color_attachments.into_iter().map(|(offset, img_la)| {
                debug_assert!(offset < attachments.len());
                vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, }
            });

            let input = pass.input_attachments.into_iter().map(|(offset, img_la)| {
                debug_assert!(offset < attachments.len());
                vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, }
            });

            let depthstencil = if let Some((offset, img_la)) = pass.depth_stencil {
                Some(vk::AttachmentReference { attachment: offset as u32, layout: img_la as u32, })
            } else {
                None
            }.into_iter();

            color.chain(input).chain(resolve).chain(depthstencil)
        }).collect::<SmallVec<[_; 16]>>();

        // Same as `attachment_references` but only for the preserve attachments.
        // This is separate because attachment references are u32s and not `vkAttachmentReference`
        // structs.
        let preserve_attachments_references = passes.clone().flat_map(|pass| {
            pass.preserve_attachments.into_iter().map(|offset| offset as u32)
        }).collect::<SmallVec<[_; 16]>>();

        // Now iterating over passes.
        // `ref_index` and `preserve_ref_index` are increased during the loop and point to the
        // next element to use in respectively `attachment_references` and
        // `preserve_attachments_references`.
        let mut ref_index = 0usize;
        let mut preserve_ref_index = 0usize;
        let passes = passes.clone().map(|pass| {
            assert!(pass.color_attachments.len() as u32 <=
                    device.physical_device().limits().max_color_attachments());

            let color_attachments = attachment_references.as_ptr().offset(ref_index as isize);
            ref_index += pass.color_attachments.len();
            let input_attachments = attachment_references.as_ptr().offset(ref_index as isize);
            ref_index += pass.input_attachments.len();
            let resolve_attachments = attachment_references.as_ptr().offset(ref_index as isize);
            ref_index += pass.resolve_attachments.len();
            let depth_stencil = if pass.depth_stencil.is_some() {
                let a = attachment_references.as_ptr().offset(ref_index as isize);
                ref_index += 1;
                a
            } else {
                ptr::null()
            };

            let preserve_attachments = preserve_attachments_references.as_ptr().offset(preserve_ref_index as isize);
            preserve_ref_index += pass.preserve_attachments.len();

            vk::SubpassDescription {
                flags: 0,   // reserved
                pipelineBindPoint: vk::PIPELINE_BIND_POINT_GRAPHICS,
                inputAttachmentCount: pass.input_attachments.len() as u32,
                pInputAttachments: if pass.input_attachments.is_empty() { ptr::null() }
                                   else { input_attachments },
                colorAttachmentCount: pass.color_attachments.len() as u32,
                pColorAttachments: if pass.color_attachments.is_empty() { ptr::null() }
                                   else { color_attachments },
                pResolveAttachments: if pass.resolve_attachments.is_empty() { ptr::null() }
                                     else { resolve_attachments },
                pDepthStencilAttachment: depth_stencil,
                preserveAttachmentCount: pass.preserve_attachments.len() as u32,
                pPreserveAttachments: if pass.preserve_attachments.is_empty() { ptr::null() }
                                      else { preserve_attachments },
            }
        }).collect::<SmallVec<[_; 16]>>();

        assert!(!passes.is_empty());
        // If these assertions fails, there's a serious bug in the code above ^.
        debug_assert!(ref_index == attachment_references.len());
        debug_assert!(preserve_ref_index == preserve_attachments_references.len());

        let dependencies = pass_dependencies.map(|dependency| {
            debug_assert!(dependency.source_subpass < passes.len());
            debug_assert!(dependency.destination_subpass < passes.len());

            vk::SubpassDependency {
                srcSubpass: dependency.source_subpass as u32,
                dstSubpass: dependency.destination_subpass as u32,
                srcStageMask: vk::PIPELINE_STAGE_ALL_GRAPHICS_BIT,      // FIXME:
                dstStageMask: vk::PIPELINE_STAGE_ALL_GRAPHICS_BIT,      // FIXME:
                srcAccessMask: 0x0001FFFF,       // FIXME:
                dstAccessMask: 0x0001FFFF,       // FIXME:
                dependencyFlags: if dependency.by_region { vk::DEPENDENCY_BY_REGION_BIT } else { 0 },
            }
        }).collect::<SmallVec<[_; 16]>>();

        let renderpass = {
            let infos = vk::RenderPassCreateInfo {
                sType: vk::STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
                pNext: ptr::null(),
                flags: 0,   // reserved
                attachmentCount: attachments.len() as u32,
                pAttachments: if attachments.is_empty() { ptr::null() }
                              else { attachments.as_ptr() },
                subpassCount: passes.len() as u32,
                pSubpasses: if passes.is_empty() { ptr::null() } else { passes.as_ptr() },
                dependencyCount: dependencies.len() as u32,
                pDependencies: if dependencies.is_empty() { ptr::null() }
                               else { dependencies.as_ptr() },
            };

            let mut output = mem::uninitialized();
            try!(check_errors(vk.CreateRenderPass(device.internal_object(), &infos,
                                                  ptr::null(), &mut output)));
            output
        };

        Ok(UnsafeRenderPass {
            device: device.clone(),
            renderpass: renderpass,
        })
    }

    /// Returns the device that was used to create this render pass.
    #[inline]
    pub fn device(&self) -> &Arc<Device> {
        &self.device
    }
}

unsafe impl VulkanObject for UnsafeRenderPass {
    type Object = vk::RenderPass;

    #[inline]
    fn internal_object(&self) -> vk::RenderPass {
        self.renderpass
    }
}

unsafe impl RenderPass for UnsafeRenderPass {
    #[inline]
    fn render_pass(&self) -> &UnsafeRenderPass {
        self
    }
}

impl Drop for UnsafeRenderPass {
    #[inline]
    fn drop(&mut self) {
        unsafe {
            let vk = self.device.pointers();
            vk.DestroyRenderPass(self.device.internal_object(), self.renderpass, ptr::null());
        }
    }
}
<|endoftext|>


<commit_before>use std::ffi::{CString, CStr};
use libc::c_char;
use get_error;

use sys;

/// Clipboard utility functions. Access with `VideoSubsystem::clipboard()`.
///
/// These functions require the video subsystem to be initialized.
///
/// ```no_run
/// let sdl_context = sdl2::init().unwrap();
/// let video_subsystem = sdl_context.video().unwrap();
///
/// video_subsystem.clipboard().set_clipboard_text("Hello World!").unwrap();
/// ```
pub struct ClipboardUtil {
    _subsystem: ::VideoSubsystem
}

impl ::VideoSubsystem {
    #[inline]
    pub fn clipboard(&self) -> ClipboardUtil {
        ClipboardUtil {
            _subsystem: self.clone()
        }
    }
}

impl ClipboardUtil {
    pub fn set_clipboard_text(&self, text: &str) -> Result<(), String> {
        unsafe {
            let text = CString::new(text).unwrap();
            let result = sys::SDL_SetClipboardText(text.as_ptr() as *const c_char);

            if result == 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }

    pub fn clipboard_text(&self) -> Result<String, String> {
        unsafe {
            let buf = sys::SDL_GetClipboardText();

            if buf.is_null() {
                Err(get_error())
            } else {
                Ok(CStr::from_ptr(buf as *const _).to_str().unwrap().to_owned())
            }
        }
    }

    pub fn has_clipboard_text(&self) -> bool {
        unsafe { sys::SDL_HasClipboardText() == sys::SDL_bool::SDL_TRUE }
    }
}
<commit_msg>Error checking fix for `ClipboardUtil::set_clipboard_text`<commit_after>use std::ffi::{CString, CStr};
use libc::c_char;
use get_error;

use sys;

/// Clipboard utility functions. Access with `VideoSubsystem::clipboard()`.
///
/// These functions require the video subsystem to be initialized.
///
/// ```no_run
/// let sdl_context = sdl2::init().unwrap();
/// let video_subsystem = sdl_context.video().unwrap();
///
/// video_subsystem.clipboard().set_clipboard_text("Hello World!").unwrap();
/// ```
pub struct ClipboardUtil {
    _subsystem: ::VideoSubsystem
}

impl ::VideoSubsystem {
    #[inline]
    pub fn clipboard(&self) -> ClipboardUtil {
        ClipboardUtil {
            _subsystem: self.clone()
        }
    }
}

impl ClipboardUtil {
    pub fn set_clipboard_text(&self, text: &str) -> Result<(), String> {
        unsafe {
            let text = CString::new(text).unwrap();
            let result = sys::SDL_SetClipboardText(text.as_ptr() as *const c_char);

            if result != 0 {
                Err(get_error())
            } else {
                Ok(())
            }
        }
    }

    pub fn clipboard_text(&self) -> Result<String, String> {
        unsafe {
            let buf = sys::SDL_GetClipboardText();

            if buf.is_null() {
                Err(get_error())
            } else {
                Ok(CStr::from_ptr(buf as *const _).to_str().unwrap().to_owned())
            }
        }
    }

    pub fn has_clipboard_text(&self) -> bool {
        unsafe { sys::SDL_HasClipboardText() == sys::SDL_bool::SDL_TRUE }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io::IoResult;
use std::io::stdio::{self, StdWriter};
use board::{Board, CellRelation, CellType};
use geom::{Geom, Point, UP, LEFT};
use hint::Hint;
use term::{self, Terminal, WriterWrapper};
use term::color::{self, Color};

enum Output<T> {
    Pretty(Box<Terminal<WriterWrapper> + Send>),
    Raw(T)
}

fn type_to_color(ty: CellType) -> (Color, Color) {
    match ty {
        CellType::Inside   => (color::BRIGHT_YELLOW,        color::BLACK),
        CellType::Outside  => (color::BLACK,        color::BRIGHT_WHITE),
        CellType::Unknown  => (color::BRIGHT_WHITE, color::BLACK),
        CellType::Conflict => (color::RED,          color::BRIGHT_WHITE),
    }
}

struct Printer<'a> {
    output: Output<StdWriter>,
    board: &'a mut Board,
    hint: &'a Hint
}

impl<'a> Printer<'a> {
    fn new(board: &'a mut Board, hint: &'a Hint) -> Printer<'a> {
        let output = match term::stdout() {
            Some(t) => Output::Pretty(t),
            None    => Output::Raw(stdio::stdout_raw())
        };
        Printer {
            output: output,
            board: board,
            hint: hint
        }
    }

    fn write_pretty(&mut self, ty: CellType, s: &str) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => {
                let (bg, fg) = type_to_color(ty);
                try!(term.fg(fg));
                try!(term.bg(bg));
                try!(term.write(s.as_bytes()));
                try!(term.reset());
                Ok(())
            }
            Output::Raw(ref mut stdout) => stdout.write(s.as_bytes())
        }
    }
    fn write_pretty_fmt(&mut self, ty: CellType, fmt: fmt::Arguments) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => {
                let (bg, fg) = type_to_color(ty);
                try!(term.fg(fg));
                try!(term.bg(bg));
                try!(term.write_fmt(fmt));
                try!(term.reset());
                Ok(())
            }
            Output::Raw(ref mut stdout) => stdout.write_fmt(fmt)
        }
    }
    fn write_plain(&mut self, s: &str) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => term.write(s.as_bytes()),
            Output::Raw(ref mut stdout) => stdout.write(s.as_bytes())
        }
    }

    fn write_plain_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => term.write_fmt(fmt),
            Output::Raw(ref mut stdout) => stdout.write_fmt(fmt)
        }
    }

    fn print(&mut self) -> IoResult<()> {
        let row = self.board.row();
        try!(self.label_row());
        for y in (0 .. row) {
            try!(self.edge_row(y));
            try!(self.cell_row(y));
        }
        try!(self.edge_row(row));
        try!(self.label_row());
        Ok(())
    }

    fn label_row(&mut self) -> IoResult<()> {
        try!(self.write_plain("  "));
        for x in 0 .. self.board.column() {
            try!(self.write_plain_fmt(format_args!(" {:2}", x)));
        }
        try!(self.write_plain("\n"));
        Ok(())
    }

    fn edge_row(&mut self, y: i32) -> IoResult<()> {
        let col = self.board.column();
        try!(self.write_plain("  "));
        for x in (0 .. col) {
            try!(self.corner(Point(y, x)));
            try!(self.edge_h(Point(y, x)));
        }
        try!(self.corner(Point(y, col)));
        try!(self.write_plain("\n"));
        Ok(())
    }

    fn cell_row(&mut self, y: i32) -> IoResult<()> {
        let col = self.board.column();
        try!(self.write_plain_fmt(format_args!("{:2}", y)));
        for x in (0 .. col) {
            try!(self.edge_v(Point(y, x)));
            try!(self.cell(Point(y, x)));
        }
        try!(self.edge_v(Point(y, col)));
        try!(self.write_plain_fmt(format_args!("{:2}\n", y)));
        Ok(())
    }

    fn corner(&mut self, p: Point) -> IoResult<()> {
        let ps = &[p, p + UP, p + LEFT, p + UP + LEFT];
        let ty = if self.board.is_same_all(ps) {
            self.board.get_type(p)
        } else {
            CellType::Unknown
        };
        try!(self.write_pretty(ty, "+"));
        Ok(())
    }

    fn edge_h(&mut self, p: Point) -> IoResult<()> {
        let ty = if self.board.is_same(p, p + UP) {
            self.board.get_type(p)
        } else {
            CellType::Unknown
        };
        let s = match self.board.get_relation(p, p + UP) {
            CellRelation::Same      => " ",
            CellRelation::Different => "-",
            CellRelation::Unknown   => "~",
            CellRelation::Conflict  => "!"
        };
        try!(self.write_pretty_fmt(ty, format_args!("{}{}", s, s)));
        Ok(())
    }

    fn edge_v(&mut self, p: Point) -> IoResult<()> {
        let ty = if self.board.is_same(p, p + LEFT) {
            self.board.get_type(p)
        } else {
            CellType::Unknown
        };
        let s = match self.board.get_relation(p, p + LEFT) {
            CellRelation::Same      => " ",
            CellRelation::Different => "|",
            CellRelation::Unknown   => "?",
            CellRelation::Conflict  => "!"
        };
        try!(self.write_pretty(ty, s));
        Ok(())
    }

    fn cell(&mut self, p: Point) -> IoResult<()> {
        let ty = self.board.get_type(p);
        match self.hint[p] {
            Some(x) => try!(self.write_pretty_fmt(ty, format_args!("{} ", x))),
            None    => try!(self.write_pretty(ty, "  "))
        }
        Ok(())
    }
}

pub fn print(board: &mut Board, hint: &Hint) -> IoResult<()> {
    Printer::new(board, hint).print()
}

<commit_msg>pprint: make output colorful only when stdout is a tty<commit_after>use std::fmt;
use std::io::IoResult;
use std::io::stdio::{self, StdWriter};
use board::{Board, CellRelation, CellType};
use geom::{Geom, Point, UP, LEFT};
use hint::Hint;
use term::{self, Terminal, WriterWrapper};
use term::color::{self, Color};

enum Output<T> {
    Pretty(Box<Terminal<WriterWrapper> + Send>),
    Raw(T)
}

fn type_to_color(ty: CellType) -> (Color, Color) {
    match ty {
        CellType::Inside   => (color::BRIGHT_YELLOW,        color::BLACK),
        CellType::Outside  => (color::BLACK,        color::BRIGHT_WHITE),
        CellType::Unknown  => (color::BRIGHT_WHITE, color::BLACK),
        CellType::Conflict => (color::RED,          color::BRIGHT_WHITE),
    }
}

struct Printer<'a> {
    output: Output<StdWriter>,
    board: &'a mut Board,
    hint: &'a Hint
}

impl<'a> Printer<'a> {
    fn new(board: &'a mut Board, hint: &'a Hint) -> Printer<'a> {
        let output = if stdio::stdout_raw().isatty() {
            match term::stdout() {
                Some(t) => Output::Pretty(t),
                None    => Output::Raw(stdio::stdout_raw())
            }
        } else {
            Output::Raw(stdio::stdout_raw())
        };
        Printer {
            output: output,
            board: board,
            hint: hint
        }
    }

    fn write_pretty(&mut self, ty: CellType, s: &str) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => {
                let (bg, fg) = type_to_color(ty);
                try!(term.fg(fg));
                try!(term.bg(bg));
                try!(term.write(s.as_bytes()));
                try!(term.reset());
                Ok(())
            }
            Output::Raw(ref mut stdout) => stdout.write(s.as_bytes())
        }
    }
    fn write_pretty_fmt(&mut self, ty: CellType, fmt: fmt::Arguments) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => {
                let (bg, fg) = type_to_color(ty);
                try!(term.fg(fg));
                try!(term.bg(bg));
                try!(term.write_fmt(fmt));
                try!(term.reset());
                Ok(())
            }
            Output::Raw(ref mut stdout) => stdout.write_fmt(fmt)
        }
    }
    fn write_plain(&mut self, s: &str) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => term.write(s.as_bytes()),
            Output::Raw(ref mut stdout) => stdout.write(s.as_bytes())
        }
    }

    fn write_plain_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> {
        match self.output {
            Output::Pretty(ref mut term) => term.write_fmt(fmt),
            Output::Raw(ref mut stdout) => stdout.write_fmt(fmt)
        }
    }

    fn print(&mut self) -> IoResult<()> {
        let row = self.board.row();
        try!(self.label_row());
        for y in (0 .. row) {
            try!(self.edge_row(y));
            try!(self.cell_row(y));
        }
        try!(self.edge_row(row));
        try!(self.label_row());
        Ok(())
    }

    fn label_row(&mut self) -> IoResult<()> {
        try!(self.write_plain("  "));
        for x in 0 .. self.board.column() {
            try!(self.write_plain_fmt(format_args!(" {:2}", x)));
        }
        try!(self.write_plain("\n"));
        Ok(())
    }

    fn edge_row(&mut self, y: i32) -> IoResult<()> {
        let col = self.board.column();
        try!(self.write_plain("  "));
        for x in (0 .. col) {
            try!(self.corner(Point(y, x)));
            try!(self.edge_h(Point(y, x)));
        }
        try!(self.corner(Point(y, col)));
        try!(self.write_plain("\n"));
        Ok(())
    }

    fn cell_row(&mut self, y: i32) -> IoResult<()> {
        let col = self.board.column();
        try!(self.write_plain_fmt(format_args!("{:2}", y)));
        for x in (0 .. col) {
            try!(self.edge_v(Point(y, x)));
            try!(self.cell(Point(y, x)));
        }
        try!(self.edge_v(Point(y, col)));
        try!(self.write_plain_fmt(format_args!("{:2}\n", y)));
        Ok(())
    }

    fn corner(&mut self, p: Point) -> IoResult<()> {
        let ps = &[p, p + UP, p + LEFT, p + UP + LEFT];
        let ty = if self.board.is_same_all(ps) {
            self.board.get_type(p)
        } else {
            CellType::Unknown
        };
        try!(self.write_pretty(ty, "+"));
        Ok(())
    }

    fn edge_h(&mut self, p: Point) -> IoResult<()> {
        let ty = if self.board.is_same(p, p + UP) {
            self.board.get_type(p)
        } else {
            CellType::Unknown
        };
        let s = match self.board.get_relation(p, p + UP) {
            CellRelation::Same      => " ",
            CellRelation::Different => "-",
            CellRelation::Unknown   => "~",
            CellRelation::Conflict  => "!"
        };
        try!(self.write_pretty_fmt(ty, format_args!("{}{}", s, s)));
        Ok(())
    }

    fn edge_v(&mut self, p: Point) -> IoResult<()> {
        let ty = if self.board.is_same(p, p + LEFT) {
            self.board.get_type(p)
        } else {
            CellType::Unknown
        };
        let s = match self.board.get_relation(p, p + LEFT) {
            CellRelation::Same      => " ",
            CellRelation::Different => "|",
            CellRelation::Unknown   => "?",
            CellRelation::Conflict  => "!"
        };
        try!(self.write_pretty(ty, s));
        Ok(())
    }

    fn cell(&mut self, p: Point) -> IoResult<()> {
        let ty = self.board.get_type(p);
        match self.hint[p] {
            Some(x) => try!(self.write_pretty_fmt(ty, format_args!("{} ", x))),
            None    => try!(self.write_pretty(ty, "  "))
        }
        Ok(())
    }
}

pub fn print(board: &mut Board, hint: &Hint) -> IoResult<()> {
    Printer::new(board, hint).print()
}

<|endoftext|>


<commit_before>use std::path::{PathBuf, Path};
use std::fs;
use std::error::Error;
use std::fmt;

#[cfg(feature = "cache")]
use time::{self, Timespec};
#[cfg(feature = "cache")]
use std::time::Duration;

use iron::prelude::*;
use iron::{Handler, Url, status};
#[cfg(feature = "cache")]
use iron::modifier::Modifier;
use iron::modifiers::Redirect;
use mount::OriginalUrl;
use requested_path::RequestedPath;

/// The static file-serving `Handler`.
///
/// This handler serves files from a single filesystem path, which may be absolute or relative.
/// Incoming requests are mapped onto the filesystem by appending their URL path to the handler's
/// root path. If the filesystem path corresponds to a regular file, the handler will attempt to
/// serve it. Otherwise, if the path corresponds to a directory containing an `index.html`,
/// the handler will attempt to serve that instead.
///
/// ## Errors
///
/// If the path doesn't match any real object in the filesystem, the handler will return
/// a Response with `status::NotFound`. If an IO error occurs whilst attempting to serve
/// a file, `FileError(IoError)` will be returned.
#[derive(Clone)]
pub struct Static {
    /// The path this handler is serving files from.
    pub root: PathBuf,
    #[cfg(feature = "cache")]
    cache: Option<Cache>,
}

impl Static {
    /// Create a new instance of `Static` with a given root path.
    ///
    /// If `Path::new("")` is given, files will be served from the current directory.
    #[cfg(feature = "cache")]
    pub fn new<P: Into<PathBuf>>(root: P) -> Static {
        Static {
            root: root.into(),
            cache: None
        }
    }

    /// Create a new instance of `Static` with a given root path.
    ///
    /// If `Path::new("")` is given, files will be served from the current directory.
    #[cfg(not(feature = "cache"))]
    pub fn new<P: Into<PathBuf>>(root: P) -> Static {
        Static {
            root: root.into(),
        }
    }

    /// Specify the response's `cache-control` header with a given duration. Internally, this is
    /// a helper function to set a `Cache` on an instance of `Static`.
    ///
    /// ## Example
    ///
    /// ```ignore
    /// let cached_static_handler = Static::new(path).cache(Duration::from_secs(30*24*60*60));
    /// ```
    #[cfg(feature = "cache")]
    pub fn cache(self, duration: Duration) -> Static {
        self.set(Cache::new(duration))
    }

    #[cfg(feature = "cache")]
    fn try_cache<P: AsRef<Path>>(&self, req: &mut Request, path: P) -> IronResult<Response> {
        match self.cache {
            None => Ok(Response::with((status::Ok, path.as_ref()))),
            Some(ref cache) => cache.handle(req, path.as_ref()),
        }
    }
}

impl Handler for Static {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        use std::io;

        let requested_path = RequestedPath::new(&self.root, req);

        let metadata = match fs::metadata(&requested_path.path) {
            Ok(meta) => meta,
            Err(e) => {
                let status = match e.kind() {
                    io::ErrorKind::NotFound => status::NotFound,
                    io::ErrorKind::PermissionDenied => status::Forbidden,
                    _ => status::InternalServerError,
                };

                return Err(IronError::new(e, status))
            },
        };

        // If the URL ends in a slash, serve the file directly.
        // Otherwise, redirect to the directory equivalent of the URL.
        if requested_path.should_redirect(&metadata, req) {
            // Perform an HTTP 301 Redirect.
            let mut original_url = match req.extensions.get::<OriginalUrl>() {
                None => &req.url,
                Some(original_url) => original_url,
            }.clone().into_generic_url();

            // Append the trailing slash
            //
            // rust-url automatically turns an empty string in the last
            // slot in the path into a trailing slash.
            original_url.path_segments_mut().unwrap().push("");
            let redirect_path = Url::from_generic_url(original_url).unwrap();

            return Ok(Response::with((status::MovedPermanently,
                                      format!("Redirecting to {}", redirect_path),
                                      Redirect(redirect_path))));
        }

        match requested_path.get_file(&metadata) {
            // If no file is found, return a 404 response.
            None => Err(IronError::new(NoFile, status::NotFound)),
            // Won't panic because we know the file exists from get_file.
            #[cfg(feature = "cache")]
            Some(path) => self.try_cache(req, path),
            #[cfg(not(feature = "cache"))]
            Some(path) => {
                let path: &Path = &path;
                Ok(Response::with((status::Ok, path)))
            },
        }
    }
}

impl Set for Static {}

/// A modifier for `Static` to specify a response's `cache-control`.
#[cfg(feature = "cache")]
#[derive(Clone)]
pub struct Cache {
    /// The length of time the file should be cached for.
    pub duration: Duration,
}

#[cfg(feature = "cache")]
impl Cache {
    /// Create a new instance of `Cache` with a given duration.
    pub fn new(duration: Duration) -> Cache {
        Cache { duration: duration }
    }

    fn handle<P: AsRef<Path>>(&self, req: &mut Request, path: P) -> IronResult<Response> {
        use iron::headers::{IfModifiedSince, HttpDate};

        let path = path.as_ref();

        let last_modified_time = match fs::metadata(path) {
            Err(error) => return Err(IronError::new(error, status::InternalServerError)),
            Ok(metadata) => {
                use filetime::FileTime;

                let time = FileTime::from_last_modification_time(&metadata);
                Timespec::new(time.seconds() as i64, time.nanoseconds() as i32)
            },
        };

        let if_modified_since = match req.headers.get::<IfModifiedSince>().cloned() {
            None => return Ok(self.response_with_cache(req, path, last_modified_time)),
            Some(IfModifiedSince(HttpDate(time))) => time.to_timespec(),
        };

        if last_modified_time <= if_modified_since {
            Ok(Response::with(status::NotModified))
        } else {
            Ok(self.response_with_cache(req, path, last_modified_time))
        }
    }

    fn response_with_cache<P: AsRef<Path>>(&self, req: &mut Request, path: P, modified: Timespec) -> Response {
        use iron::headers::{CacheControl, LastModified, CacheDirective, HttpDate};
        use iron::headers::{ContentLength, ContentType};
        use iron::method::Method;
        use iron::mime::{Mime, TopLevel, SubLevel};
        use iron::modifiers::Header;

        let mut response: Response;
        let seconds = self.duration.as_secs() as u32;
        let cache = vec![CacheDirective::Public, CacheDirective::MaxAge(seconds)];
        let metadata = fs::metadata(path.as_ref()).unwrap();

        if req.method == Method::Head {
            let has_ct = req.headers.get::<ContentType>();
            let cont_type = match has_ct {
                None => ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])),
                Some(t) => t.clone()
            };
            response = Response::with((status::Ok, Header(cont_type), Header(ContentLength(metadata.len()))))
        } else {
            response = Response::with((status::Ok, path.as_ref()));
        }

        response.headers.set(CacheControl(cache));
        response.headers.set(LastModified(HttpDate(time::at(modified))));

        response
    }
}

#[cfg(feature = "cache")]
impl Modifier<Static> for Cache {
    fn modify(self, static_handler: &mut Static) {
        static_handler.cache = Some(self);
    }
}

/// Thrown if no file is found. It is always accompanied by a NotFound response.
#[derive(Debug)]
pub struct NoFile;

impl Error for NoFile {
    fn description(&self) -> &str { "File not found" }
}

impl fmt::Display for NoFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}
<commit_msg>(fix) address CR comments<commit_after>use std::path::{PathBuf, Path};
use std::fs;
use std::error::Error;
use std::fmt;

#[cfg(feature = "cache")]
use time::{self, Timespec};
#[cfg(feature = "cache")]
use std::time::Duration;

use iron::prelude::*;
use iron::{Handler, Url, status};
#[cfg(feature = "cache")]
use iron::modifier::Modifier;
use iron::modifiers::Redirect;
use mount::OriginalUrl;
use requested_path::RequestedPath;

/// The static file-serving `Handler`.
///
/// This handler serves files from a single filesystem path, which may be absolute or relative.
/// Incoming requests are mapped onto the filesystem by appending their URL path to the handler's
/// root path. If the filesystem path corresponds to a regular file, the handler will attempt to
/// serve it. Otherwise, if the path corresponds to a directory containing an `index.html`,
/// the handler will attempt to serve that instead.
///
/// ## Errors
///
/// If the path doesn't match any real object in the filesystem, the handler will return
/// a Response with `status::NotFound`. If an IO error occurs whilst attempting to serve
/// a file, `FileError(IoError)` will be returned.
#[derive(Clone)]
pub struct Static {
    /// The path this handler is serving files from.
    pub root: PathBuf,
    #[cfg(feature = "cache")]
    cache: Option<Cache>,
}

impl Static {
    /// Create a new instance of `Static` with a given root path.
    ///
    /// If `Path::new("")` is given, files will be served from the current directory.
    #[cfg(feature = "cache")]
    pub fn new<P: Into<PathBuf>>(root: P) -> Static {
        Static {
            root: root.into(),
            cache: None
        }
    }

    /// Create a new instance of `Static` with a given root path.
    ///
    /// If `Path::new("")` is given, files will be served from the current directory.
    #[cfg(not(feature = "cache"))]
    pub fn new<P: Into<PathBuf>>(root: P) -> Static {
        Static {
            root: root.into(),
        }
    }

    /// Specify the response's `cache-control` header with a given duration. Internally, this is
    /// a helper function to set a `Cache` on an instance of `Static`.
    ///
    /// ## Example
    ///
    /// ```ignore
    /// let cached_static_handler = Static::new(path).cache(Duration::from_secs(30*24*60*60));
    /// ```
    #[cfg(feature = "cache")]
    pub fn cache(self, duration: Duration) -> Static {
        self.set(Cache::new(duration))
    }

    #[cfg(feature = "cache")]
    fn try_cache<P: AsRef<Path>>(&self, req: &mut Request, path: P) -> IronResult<Response> {
        match self.cache {
            None => Ok(Response::with((status::Ok, path.as_ref()))),
            Some(ref cache) => cache.handle(req, path.as_ref()),
        }
    }
}

impl Handler for Static {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        use std::io;

        let requested_path = RequestedPath::new(&self.root, req);

        let metadata = match fs::metadata(&requested_path.path) {
            Ok(meta) => meta,
            Err(e) => {
                let status = match e.kind() {
                    io::ErrorKind::NotFound => status::NotFound,
                    io::ErrorKind::PermissionDenied => status::Forbidden,
                    _ => status::InternalServerError,
                };

                return Err(IronError::new(e, status))
            },
        };

        // If the URL ends in a slash, serve the file directly.
        // Otherwise, redirect to the directory equivalent of the URL.
        if requested_path.should_redirect(&metadata, req) {
            // Perform an HTTP 301 Redirect.
            let mut original_url = match req.extensions.get::<OriginalUrl>() {
                None => &req.url,
                Some(original_url) => original_url,
            }.clone().into_generic_url();

            // Append the trailing slash
            //
            // rust-url automatically turns an empty string in the last
            // slot in the path into a trailing slash.
            original_url.path_segments_mut().unwrap().push("");
            let redirect_path = Url::from_generic_url(original_url).unwrap();

            return Ok(Response::with((status::MovedPermanently,
                                      format!("Redirecting to {}", redirect_path),
                                      Redirect(redirect_path))));
        }

        match requested_path.get_file(&metadata) {
            // If no file is found, return a 404 response.
            None => Err(IronError::new(NoFile, status::NotFound)),
            // Won't panic because we know the file exists from get_file.
            #[cfg(feature = "cache")]
            Some(path) => self.try_cache(req, path),
            #[cfg(not(feature = "cache"))]
            Some(path) => {
                let path: &Path = &path;
                Ok(Response::with((status::Ok, path)))
            },
        }
    }
}

impl Set for Static {}

/// A modifier for `Static` to specify a response's `cache-control`.
#[cfg(feature = "cache")]
#[derive(Clone)]
pub struct Cache {
    /// The length of time the file should be cached for.
    pub duration: Duration,
}

#[cfg(feature = "cache")]
impl Cache {
    /// Create a new instance of `Cache` with a given duration.
    pub fn new(duration: Duration) -> Cache {
        Cache { duration: duration }
    }

    fn handle<P: AsRef<Path>>(&self, req: &mut Request, path: P) -> IronResult<Response> {
        use iron::headers::{IfModifiedSince, HttpDate};

        let path = path.as_ref();

        let last_modified_time = match fs::metadata(path) {
            Err(error) => return Err(IronError::new(error, status::InternalServerError)),
            Ok(metadata) => {
                use filetime::FileTime;

                let time = FileTime::from_last_modification_time(&metadata);
                Timespec::new(time.seconds() as i64, time.nanoseconds() as i32)
            },
        };

        let if_modified_since = match req.headers.get::<IfModifiedSince>().cloned() {
            None => return Ok(self.response_with_cache(req, path, last_modified_time)),
            Some(IfModifiedSince(HttpDate(time))) => time.to_timespec(),
        };

        if last_modified_time <= if_modified_since {
            Ok(Response::with(status::NotModified))
        } else {
            Ok(self.response_with_cache(req, path, last_modified_time))
        }
    }

    fn response_with_cache<P: AsRef<Path>>(&self,
                                           req: &mut Request,
                                           path: P,
                                           modified: Timespec) -> IronResult<Response> {
        use iron::headers::{CacheControl, LastModified, CacheDirective, HttpDate};
        use iron::headers::{ContentLength, ContentType};
        use iron::method::Method;
        use iron::mime::{Mime, TopLevel, SubLevel};
        use iron::modifiers::Header;

        let seconds = self.duration.as_secs() as u32;
        let cache = vec![CacheDirective::Public, CacheDirective::MaxAge(seconds)];
        let metadata = fs::metadata(path.as_ref());

        if metadata.is_err() {
            return Err(IronError::new(metadata.unwrap_err(), status::InternalServerError))
        }

        let mut response = if req.method == Method::Head {
            let has_ct = req.headers.get::<ContentType>();
            let cont_type = match has_ct {
                None => ContentType(Mime(TopLevel::Text, SubLevel::Plain, vec![])),
                Some(t) => t.clone()
            };
            Response::with((status::Ok, Header(cont_type), Header(ContentLength(metadata.len()))))
        } else {
            Response::with((status::Ok, path.as_ref()));
        };

        response.headers.set(CacheControl(cache));
        response.headers.set(LastModified(HttpDate(time::at(modified))));

        Ok(response)
    }
}

#[cfg(feature = "cache")]
impl Modifier<Static> for Cache {
    fn modify(self, static_handler: &mut Static) {
        static_handler.cache = Some(self);
    }
}

/// Thrown if no file is found. It is always accompanied by a NotFound response.
#[derive(Debug)]
pub struct NoFile;

impl Error for NoFile {
    fn description(&self) -> &str { "File not found" }
}

impl fmt::Display for NoFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}
<|endoftext|>