<commit_before>//! This module aims to provide simple parsing for generic netlink attributes, including parsing
//! for nested attributes.
//!
//! Due to netlink's loose treatment of types, parsing attributes can be hard to model in
//! Rust. `neli`'s current solution is the following:
//!
//! ```no_run
//! // This was received from the socket
//! let nlmsg = neli::nl::Nlmsghdr::new(None, neli::consts::GenlId::Ctrl, Vec::new(), None, None,
//!         neli::genl::Genlmsghdr::new(neli::consts::CtrlCmd::Unspec, 2, Vec::new()).unwrap());
//!
//! // Get parsing handler for the attributes in this message where the next call
//! // to either get_nested_attributes() or get_payload_with() will expect a u16 type
//! // to be provided
//! let mut handle = nlmsg.nl_payload.get_attr_handle();
//!
//! // Get the nested attribute where the Nlattr field of nla_type is equal to 1 and return
//! // a handler containing only this nested attribute internally
//! let mut next = handle.get_nested_attributes::<u16>(1).unwrap();
//!
//! // Get the nested attribute where the Nlattr field of nla_type is equal to 1 and return
//! // the payload of this attribute as a u32
//! let thirty_two_bit_integer = next.get_attr_payload_as::<u32>(1).unwrap();
//! ```
//!
//! # Design decisions
//!
//! Nested attributes are represented by `Vec<u8>` payloads inside top level attributes. They are
//! parsed during traversal to provide the ability to parse one attribute header using a different
//! generic type for the nested attribute type parameters, the typical case when parsing nested
//! attributes. To
//! traverse a nested attribute, look at the documentation for `.get_nested_attributes()` and
//! `AttrHandle` as well as the `examples/` directory for code examples of how to traverse nested
//! attributes.

use std::io::{Read,Write};
use std::slice;

use buffering::copy::{StreamReadBuffer,StreamWriteBuffer};
use libc;

use Nl;
use err::{DeError,NlError,SerError};
use consts::{alignto,NlAttrType};

impl<T, P> Nl for Vec<Nlattr<T, P>> where T: NlAttrType, P: Nl {
    fn serialize(&self, mem: &mut StreamWriteBuffer) -> Result<(), SerError> {
        for item in self.iter() {
            item.serialize(mem)?;
        }
        Ok(())
    }

    fn deserialize<B>(mem: &mut StreamReadBuffer<B>) -> Result<Self, DeError>
            where B: AsRef<[u8]> {
        let mut vec = Vec::new();
        let mut size_hint = mem.take_size_hint().unwrap_or(0);
        while size_hint > 0 || !mem.at_end() {
            let next = Nlattr::<T, P>::deserialize(mem)?;
            if size_hint > 0 {
                size_hint -= next.asize();
            }
            vec.push(next);
        }
        Ok(vec)
    }

    fn size(&self) -> usize {
        self.iter().fold(0, |acc, next| {
            acc + alignto(next.size())
        })
    }
}

/// Struct representing netlink attributes and payloads
#[derive(Debug,PartialEq)]
pub struct Nlattr<T, P> {
    /// Length of the attribute header and payload together
    pub nla_len: u16,
    /// Enum representing the type of the attribute payload
    pub nla_type: T,
    /// Payload of the attribute - either parsed or a binary buffer
    pub payload: P,
}

impl<T, P> Nlattr<T, P> where T: NlAttrType, P: Nl {
    /// Get the size of the payload only
    pub fn payload_size(&self) -> usize {
        self.payload.size()
    }
}

impl<T> Nlattr<T, Vec<u8>> where T: NlAttrType {
    /// This function will serialize the provided payload
    pub fn new<P>(nla_len: Option<u16>, nla_type: T, payload: P) -> Result<Self, SerError>
            where P: Nl {
        let mut attr = Nlattr {
            nla_len: nla_len.unwrap_or(0),
            nla_type,
            payload: Vec::new(),
        };
        attr.set_payload(payload)?;
        Ok(attr)
    }

    /// Set the payload to a data type that implements `Nl` -
    /// this function will overwrite the current payload
    pub fn set_payload<P>(&mut self, payload: P) -> Result<(), SerError> where P: Nl {
        let mut buffer = StreamWriteBuffer::new_growable_ref(&mut self.payload);
        payload.serialize(&mut buffer)?;

        // Update `Nlattr` with new length
        self.nla_len = (self.nla_len.size() + self.nla_type.size() + payload.size()) as u16;

        Ok(())
    }

    /// Add a nested attribute to the end of the payload
    pub fn add_nested_attribute<TT, P>(&mut self, attr: Nlattr<TT, P>) -> Result<(), SerError>
            where TT: NlAttrType, P: Nl {
        let size = self.payload.len() as u64;
        let mut buffer = StreamWriteBuffer::new_growable_ref(&mut self.payload);
        buffer.set_position(size);
        attr.serialize(&mut buffer)?;
        Ok(())
    }

    /// Get an `Nlattr` payload as a provided type
    pub fn get_payload_as<R>(&self) -> Result<R, DeError> where R: Nl {
        let mut buf = StreamReadBuffer::new(&self.payload);
        buf.set_size_hint(self.payload_size());
        R::deserialize(&mut buf)
    }

    /// Return an `AttrHandle` for attributes nested in the given attribute payload
    pub fn get_nested_attributes<'a, R>(&'a self) -> Result<AttrHandle<'a, T>, DeError> where R: Nl {
        Ok(AttrHandle::new(Vec::<Nlattr<T, Vec<u8>>>::deserialize(
            &mut StreamReadBuffer::new(&self.payload)
        )?))
    }
}

impl<T, P> Nl for Nlattr<T, P> where T: NlAttrType, P: Nl {
    fn serialize(&self, mem: &mut StreamWriteBuffer) -> Result<(), SerError> {
        self.nla_len.serialize(mem)?;
        self.nla_type.serialize(mem)?;
        self.payload.serialize(mem)?;
        let padding_len = alignto(self.nla_len as usize) - self.nla_len as usize;
        if padding_len > 0 {
            mem.write(&mut [0u8; 4][0..padding_len])?;
        }
        Ok(())
    }

    fn deserialize<B>(mem: &mut StreamReadBuffer<B>) -> Result<Self, DeError> where B: AsRef<[u8]> {
        let nla_len = u16::deserialize(mem)?;
        let nla_type = T::deserialize(mem)?;
        mem.set_size_hint(nla_len as usize - (nla_len.size() + nla_type.size()));
        let payload = P::deserialize(mem)?;
        let padding_len = alignto(nla_len as usize) - nla_len as usize;
        if padding_len > 0 {
            let padding = &mut [0u8; libc::NLA_ALIGNTO as usize][0..padding_len];
            let _ = mem.read_exact(padding)?;
        }
        let nla = Nlattr {
            nla_len,
            nla_type,
            payload,
        };
        Ok(nla)
    }

    fn size(&self) -> usize {
        self.nla_len.size() + self.nla_type.size() + self.payload.size()
    }
}

/// Handle returned by `Genlmsghdr` for traversing nested attribute structures
pub enum AttrHandle<'a, T> {
    /// Owned vector
    Owned(Vec<Nlattr<T, Vec<u8>>>),
    /// Vector reference
    Borrowed(&'a Vec<Nlattr<T, Vec<u8>>>),
}

impl<'a, T> AttrHandle<'a, T> where T: NlAttrType {
    /// Create new `AttrHandle`
    pub fn new(vec: Vec<Nlattr<T, Vec<u8>>>) -> Self {
        AttrHandle::Owned(vec)
    }

    /// Create new borrowed `AttrHandle`
    pub fn new_borrowed(vec: &'a Vec<Nlattr<T, Vec<u8>>>) -> Self {
        AttrHandle::Borrowed(vec)
    }

    /// Get the underlying `Vec` as a reference
    pub fn get_vec(&self) -> &Vec<Nlattr<T, Vec<u8>>> {
        match *self {
            AttrHandle::Owned(ref v) => v,
            AttrHandle::Borrowed(ref v) => v,
        }
    }

    /// Get the underlying `Vec` as a mutable reference or return `None`
    pub fn get_vec_mut(&mut self) -> Option<&mut Vec<Nlattr<T, Vec<u8>>>> {
        match self {
            AttrHandle::Owned(ref mut v) => Some(v),
            AttrHandle::Borrowed(_) => None,
        }
    }

    /// Get size of buffer required to hold attributes
    pub fn size(&self) -> usize {
        self.get_vec().size()
    }

    /// If attributes are parsed, pass back iterator over attributes
    pub fn iter(&self) -> slice::Iter<Nlattr<T, Vec<u8>>> {
        self.get_vec().iter()
    }

    /// Get the payload of an attribute as a handle for parsing nested attributes
    pub fn get_nested_attributes<S>(&mut self, subattr: T) -> Result<AttrHandle<S>, NlError>
            where S: NlAttrType {
        Ok(AttrHandle::new(Vec::<Nlattr::<S, Vec<u8>>>::deserialize(&mut StreamReadBuffer::new(
            &self.get_attribute(subattr).ok_or(NlError::new(
                "Couldn't find specified attribute"
            ))?.payload
        ))?))
    }

    /// Get nested attributes from a parsed handle
    pub fn get_attribute<'b>(&'b self, t: T) -> Option<&'b Nlattr<T, Vec<u8>>> {
        for item in self.get_vec().iter() {
            if item.nla_type == t {
                return Some(&item);
            }
        }
        None
    }

    /// Mutably get nested attributes from a parsed handle
    pub fn get_attribute_mut<'b>(&'b mut self, t: T) -> Option<&'b mut Nlattr<T, Vec<u8>>> {
        let vec_mut = self.get_vec_mut()?;
        for item in vec_mut.iter_mut() {
            if item.nla_type == t {
                return Some(item);
            }
        }
        None
    }

    /// Parse binary payload as a type that implements `Nl` using `deserialize` with an option size
    /// hint
    pub fn get_attr_payload_as<R>(&self, attr: T) -> Result<R, DeError>
            where R: Nl {
        match self.get_attribute(attr) {
            Some(a) => {
                a.get_payload_as::<R>()
            },
            _ => Err(DeError::new("Failed to find specified attribute")),
        }
    }
}

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

    extern crate byteorder;

    use std::io::Cursor;

    use byteorder::{NativeEndian,WriteBytesExt};

    use consts::CtrlAttr;

    #[test]
    fn test_padding_size_calculation() {
        let nlattr = Nlattr::new(None, CtrlAttr::Unspec, 4u16).unwrap();
        assert_eq!(nlattr.size(), 6);
        assert_eq!(nlattr.asize(), 8);
    }

    #[test]
    fn test_nl_nlattr() {
        let nlattr = Nlattr::new(None, CtrlAttr::Unspec, 4u16).unwrap();
        let mut nlattr_serialized = StreamWriteBuffer::new_growable(Some(nlattr.asize()));
        nlattr.serialize(&mut nlattr_serialized).unwrap();

        let mut nlattr_desired_serialized = Cursor::new(vec![0; nlattr.size()]);
        nlattr_desired_serialized.write_u16::<NativeEndian>(6).unwrap();
        nlattr_desired_serialized.write_u16::<NativeEndian>(CtrlAttr::Unspec.into()).unwrap();
        nlattr_desired_serialized.write_u16::<NativeEndian>(4).unwrap();
        nlattr_desired_serialized.write(&[0, 0]).unwrap();

        assert_eq!(nlattr_serialized.as_ref(), nlattr_desired_serialized.into_inner().as_slice());

        let nlattr_desired_deserialized = Nlattr { nla_len: 6, nla_type: CtrlAttr::Unspec,
                                                   payload: 4u16 };

        let mut nlattr_deserialize_buffer = Cursor::new(
            vec![0; nlattr_desired_deserialized.asize()]
        );
        nlattr_deserialize_buffer.write_u16::<NativeEndian>(6).unwrap();
        nlattr_deserialize_buffer.write_u16::<NativeEndian>(CtrlAttr::Unspec.into()).unwrap();
        nlattr_deserialize_buffer.write_u16::<NativeEndian>(4).unwrap();
        nlattr_deserialize_buffer.write(&[0, 0]).unwrap();
        let mut reader = StreamReadBuffer::new(nlattr_deserialize_buffer.into_inner());
        let nlattr_deserialized = Nlattr::<CtrlAttr, u16>::deserialize(&mut reader).unwrap();
        assert_eq!(nlattr_deserialized, nlattr_desired_deserialized);
    }
}
<commit_msg>Use get_nested_attributes R type for message type<commit_after>//! This module aims to provide simple parsing for generic netlink attributes, including parsing
//! for nested attributes.
//!
//! Due to netlink's loose treatment of types, parsing attributes can be hard to model in
//! Rust. `neli`'s current solution is the following:
//!
//! ```no_run
//! // This was received from the socket
//! let nlmsg = neli::nl::Nlmsghdr::new(None, neli::consts::GenlId::Ctrl, Vec::new(), None, None,
//!         neli::genl::Genlmsghdr::new(neli::consts::CtrlCmd::Unspec, 2, Vec::new()).unwrap());
//!
//! // Get parsing handler for the attributes in this message where the next call
//! // to either get_nested_attributes() or get_payload_with() will expect a u16 type
//! // to be provided
//! let mut handle = nlmsg.nl_payload.get_attr_handle();
//!
//! // Get the nested attribute where the Nlattr field of nla_type is equal to 1 and return
//! // a handler containing only this nested attribute internally
//! let mut next = handle.get_nested_attributes::<u16>(1).unwrap();
//!
//! // Get the nested attribute where the Nlattr field of nla_type is equal to 1 and return
//! // the payload of this attribute as a u32
//! let thirty_two_bit_integer = next.get_attr_payload_as::<u32>(1).unwrap();
//! ```
//!
//! # Design decisions
//!
//! Nested attributes are represented by `Vec<u8>` payloads inside top level attributes. They are
//! parsed during traversal to provide the ability to parse one attribute header using a different
//! generic type for the nested attribute type parameters, the typical case when parsing nested
//! attributes. To
//! traverse a nested attribute, look at the documentation for `.get_nested_attributes()` and
//! `AttrHandle` as well as the `examples/` directory for code examples of how to traverse nested
//! attributes.

use std::io::{Read,Write};
use std::slice;

use buffering::copy::{StreamReadBuffer,StreamWriteBuffer};
use libc;

use Nl;
use err::{DeError,NlError,SerError};
use consts::{alignto,NlAttrType};

impl<T, P> Nl for Vec<Nlattr<T, P>> where T: NlAttrType, P: Nl {
    fn serialize(&self, mem: &mut StreamWriteBuffer) -> Result<(), SerError> {
        for item in self.iter() {
            item.serialize(mem)?;
        }
        Ok(())
    }

    fn deserialize<B>(mem: &mut StreamReadBuffer<B>) -> Result<Self, DeError>
            where B: AsRef<[u8]> {
        let mut vec = Vec::new();
        let mut size_hint = mem.take_size_hint().unwrap_or(0);
        while size_hint > 0 || !mem.at_end() {
            let next = Nlattr::<T, P>::deserialize(mem)?;
            if size_hint > 0 {
                size_hint -= next.asize();
            }
            vec.push(next);
        }
        Ok(vec)
    }

    fn size(&self) -> usize {
        self.iter().fold(0, |acc, next| {
            acc + alignto(next.size())
        })
    }
}

/// Struct representing netlink attributes and payloads
#[derive(Debug,PartialEq)]
pub struct Nlattr<T, P> {
    /// Length of the attribute header and payload together
    pub nla_len: u16,
    /// Enum representing the type of the attribute payload
    pub nla_type: T,
    /// Payload of the attribute - either parsed or a binary buffer
    pub payload: P,
}

impl<T, P> Nlattr<T, P> where T: NlAttrType, P: Nl {
    /// Get the size of the payload only
    pub fn payload_size(&self) -> usize {
        self.payload.size()
    }
}

impl<T> Nlattr<T, Vec<u8>> where T: NlAttrType {
    /// This function will serialize the provided payload
    pub fn new<P>(nla_len: Option<u16>, nla_type: T, payload: P) -> Result<Self, SerError>
            where P: Nl {
        let mut attr = Nlattr {
            nla_len: nla_len.unwrap_or(0),
            nla_type,
            payload: Vec::new(),
        };
        attr.set_payload(payload)?;
        Ok(attr)
    }

    /// Set the payload to a data type that implements `Nl` -
    /// this function will overwrite the current payload
    pub fn set_payload<P>(&mut self, payload: P) -> Result<(), SerError> where P: Nl {
        let mut buffer = StreamWriteBuffer::new_growable_ref(&mut self.payload);
        payload.serialize(&mut buffer)?;

        // Update `Nlattr` with new length
        self.nla_len = (self.nla_len.size() + self.nla_type.size() + payload.size()) as u16;

        Ok(())
    }

    /// Add a nested attribute to the end of the payload
    pub fn add_nested_attribute<TT, P>(&mut self, attr: Nlattr<TT, P>) -> Result<(), SerError>
            where TT: NlAttrType, P: Nl {
        let size = self.payload.len() as u64;
        let mut buffer = StreamWriteBuffer::new_growable_ref(&mut self.payload);
        buffer.set_position(size);
        attr.serialize(&mut buffer)?;
        Ok(())
    }

    /// Get an `Nlattr` payload as a provided type
    pub fn get_payload_as<R>(&self) -> Result<R, DeError> where R: Nl {
        let mut buf = StreamReadBuffer::new(&self.payload);
        buf.set_size_hint(self.payload_size());
        R::deserialize(&mut buf)
    }

    /// Return an `AttrHandle` for attributes nested in the given attribute payload
    pub fn get_nested_attributes<'a, R>(&'a self) -> Result<AttrHandle<'a, R>, DeError> where R: NlAttrType {
        Ok(AttrHandle::new(Vec::<Nlattr<R, Vec<u8>>>::deserialize(
            &mut StreamReadBuffer::new(&self.payload)
        )?))
    }
}

impl<T, P> Nl for Nlattr<T, P> where T: NlAttrType, P: Nl {
    fn serialize(&self, mem: &mut StreamWriteBuffer) -> Result<(), SerError> {
        self.nla_len.serialize(mem)?;
        self.nla_type.serialize(mem)?;
        self.payload.serialize(mem)?;
        let padding_len = alignto(self.nla_len as usize) - self.nla_len as usize;
        if padding_len > 0 {
            mem.write(&mut [0u8; 4][0..padding_len])?;
        }
        Ok(())
    }

    fn deserialize<B>(mem: &mut StreamReadBuffer<B>) -> Result<Self, DeError> where B: AsRef<[u8]> {
        let nla_len = u16::deserialize(mem)?;
        let nla_type = T::deserialize(mem)?;
        mem.set_size_hint(nla_len as usize - (nla_len.size() + nla_type.size()));
        let payload = P::deserialize(mem)?;
        let padding_len = alignto(nla_len as usize) - nla_len as usize;
        if padding_len > 0 {
            let padding = &mut [0u8; libc::NLA_ALIGNTO as usize][0..padding_len];
            let _ = mem.read_exact(padding)?;
        }
        let nla = Nlattr {
            nla_len,
            nla_type,
            payload,
        };
        Ok(nla)
    }

    fn size(&self) -> usize {
        self.nla_len.size() + self.nla_type.size() + self.payload.size()
    }
}

/// Handle returned by `Genlmsghdr` for traversing nested attribute structures
pub enum AttrHandle<'a, T> {
    /// Owned vector
    Owned(Vec<Nlattr<T, Vec<u8>>>),
    /// Vector reference
    Borrowed(&'a Vec<Nlattr<T, Vec<u8>>>),
}

impl<'a, T> AttrHandle<'a, T> where T: NlAttrType {
    /// Create new `AttrHandle`
    pub fn new(vec: Vec<Nlattr<T, Vec<u8>>>) -> Self {
        AttrHandle::Owned(vec)
    }

    /// Create new borrowed `AttrHandle`
    pub fn new_borrowed(vec: &'a Vec<Nlattr<T, Vec<u8>>>) -> Self {
        AttrHandle::Borrowed(vec)
    }

    /// Get the underlying `Vec` as a reference
    pub fn get_vec(&self) -> &Vec<Nlattr<T, Vec<u8>>> {
        match *self {
            AttrHandle::Owned(ref v) => v,
            AttrHandle::Borrowed(ref v) => v,
        }
    }

    /// Get the underlying `Vec` as a mutable reference or return `None`
    pub fn get_vec_mut(&mut self) -> Option<&mut Vec<Nlattr<T, Vec<u8>>>> {
        match self {
            AttrHandle::Owned(ref mut v) => Some(v),
            AttrHandle::Borrowed(_) => None,
        }
    }

    /// Get size of buffer required to hold attributes
    pub fn size(&self) -> usize {
        self.get_vec().size()
    }

    /// If attributes are parsed, pass back iterator over attributes
    pub fn iter(&self) -> slice::Iter<Nlattr<T, Vec<u8>>> {
        self.get_vec().iter()
    }

    /// Get the payload of an attribute as a handle for parsing nested attributes
    pub fn get_nested_attributes<S>(&mut self, subattr: T) -> Result<AttrHandle<S>, NlError>
            where S: NlAttrType {
        Ok(AttrHandle::new(Vec::<Nlattr::<S, Vec<u8>>>::deserialize(&mut StreamReadBuffer::new(
            &self.get_attribute(subattr).ok_or(NlError::new(
                "Couldn't find specified attribute"
            ))?.payload
        ))?))
    }

    /// Get nested attributes from a parsed handle
    pub fn get_attribute<'b>(&'b self, t: T) -> Option<&'b Nlattr<T, Vec<u8>>> {
        for item in self.get_vec().iter() {
            if item.nla_type == t {
                return Some(&item);
            }
        }
        None
    }

    /// Mutably get nested attributes from a parsed handle
    pub fn get_attribute_mut<'b>(&'b mut self, t: T) -> Option<&'b mut Nlattr<T, Vec<u8>>> {
        let vec_mut = self.get_vec_mut()?;
        for item in vec_mut.iter_mut() {
            if item.nla_type == t {
                return Some(item);
            }
        }
        None
    }

    /// Parse binary payload as a type that implements `Nl` using `deserialize` with an option size
    /// hint
    pub fn get_attr_payload_as<R>(&self, attr: T) -> Result<R, DeError>
            where R: Nl {
        match self.get_attribute(attr) {
            Some(a) => {
                a.get_payload_as::<R>()
            },
            _ => Err(DeError::new("Failed to find specified attribute")),
        }
    }
}

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

    extern crate byteorder;

    use std::io::Cursor;

    use byteorder::{NativeEndian,WriteBytesExt};

    use consts::CtrlAttr;

    #[test]
    fn test_padding_size_calculation() {
        let nlattr = Nlattr::new(None, CtrlAttr::Unspec, 4u16).unwrap();
        assert_eq!(nlattr.size(), 6);
        assert_eq!(nlattr.asize(), 8);
    }

    #[test]
    fn test_nl_nlattr() {
        let nlattr = Nlattr::new(None, CtrlAttr::Unspec, 4u16).unwrap();
        let mut nlattr_serialized = StreamWriteBuffer::new_growable(Some(nlattr.asize()));
        nlattr.serialize(&mut nlattr_serialized).unwrap();

        let mut nlattr_desired_serialized = Cursor::new(vec![0; nlattr.size()]);
        nlattr_desired_serialized.write_u16::<NativeEndian>(6).unwrap();
        nlattr_desired_serialized.write_u16::<NativeEndian>(CtrlAttr::Unspec.into()).unwrap();
        nlattr_desired_serialized.write_u16::<NativeEndian>(4).unwrap();
        nlattr_desired_serialized.write(&[0, 0]).unwrap();

        assert_eq!(nlattr_serialized.as_ref(), nlattr_desired_serialized.into_inner().as_slice());

        let nlattr_desired_deserialized = Nlattr { nla_len: 6, nla_type: CtrlAttr::Unspec,
                                                   payload: 4u16 };

        let mut nlattr_deserialize_buffer = Cursor::new(
            vec![0; nlattr_desired_deserialized.asize()]
        );
        nlattr_deserialize_buffer.write_u16::<NativeEndian>(6).unwrap();
        nlattr_deserialize_buffer.write_u16::<NativeEndian>(CtrlAttr::Unspec.into()).unwrap();
        nlattr_deserialize_buffer.write_u16::<NativeEndian>(4).unwrap();
        nlattr_deserialize_buffer.write(&[0, 0]).unwrap();
        let mut reader = StreamReadBuffer::new(nlattr_deserialize_buffer.into_inner());
        let nlattr_deserialized = Nlattr::<CtrlAttr, u16>::deserialize(&mut reader).unwrap();
        assert_eq!(nlattr_deserialized, nlattr_desired_deserialized);
    }
}
<|endoftext|>


<commit_before>use rand::distributions::normal::Normal;
use rand;
use rand::distributions::IndependentSample;
use nalgebra::DMatrix;

/// Network type which shows how the Network looks like
pub struct Network {
    layers: Vec<u8>,
    weights: Vec<DMatrix<f32>>,
    biases: Vec<Vec<f32>>,
}

/// Implements Constructor and some getter functions
impl Network {
    /// build a new Network by given parameters
    pub fn new(sizes: Vec<u8>) -> Result<Network, &'static str> {
        use rand::distributions::{Normal, IndependentSample};

        if sizes.len() < 3 {
            return Err("Not enough layers");
        }

        // Store the weights and biases in lists
        // We will not need weights or biases for input layer, so ignore that (hence -1)
        let mut weights: Vec<DMatrix<f32>> = Vec::with_capacity(sizes.len() - 1);
        let mut biases: Vec<Vec<f32>> = Vec::with_capacity(sizes.len() - 1);

        let mut rng = rand::thread_rng();
        let normal = Normal::new(0.0, 1.0);

        for i in 1..sizes.len() {
            biases.push(Vec::with_capacity(sizes[i] as usize));
            for _ in 0..sizes[i] {
                biases[i - 1].push(normal.ind_sample(&mut rng) as f32);
            }
            weights.push(DMatrix::new_random_normal(sizes[i - 1] as usize, sizes[i] as usize));
        }

        Ok(Network {
            layers: sizes,
            weights: weights,
            biases: biases,
        })
    }

    pub fn get_layers(&self) -> &Vec<u8> {
        &self.layers
    }

    pub fn get_weights(&self) -> &Vec<DMatrix<f32>> {
        &self.weights
    }

    pub fn get_biases(&self) -> &Vec<Vec<f32>> {
        &self.biases
    }
}

/// trait for implementing random values from 0 to 1
trait Random {
    fn new_random_normal(rows: usize, columns: usize) -> DMatrix<f32>;
}

/// Implements random number initializer for DMatrix
impl Random for DMatrix<f32> {
    fn new_random_normal(rows: usize, columns: usize) -> DMatrix<f32> {

        // Init the DMatrix with rows and columns
        let mut res;
        unsafe {
            res = DMatrix::new_uninitialized(rows, columns);
        }

        // random number between 0 and 1
        let mut rng = rand::thread_rng();
        let normal = Normal::new(0.0, 1.0);

        // fills matrix with random numbers from 0 and 1
        for i in 0..rows {
            for j in 0..columns {
                res[(i, j)] = normal.ind_sample(&mut rng) as f32;
            }
        }

        res
    }
}
<commit_msg>added some comments and changed nn types for nalgebra<commit_after>use rand::distributions::normal::Normal;
use rand;
use rand::distributions::IndependentSample;
use nalgebra::{DMatrix, DVector};

/// Network type which shows how the Network looks like
pub struct Network {
    /// a Vec giving the sizes of the layers
    /// the first entry corresponds to the inputlayer
    /// and the last entry corresponds to the outputlayer
    layers: Vec<u8>,
    /// a Vec that contains the weights of the respective layer
    weights: Vec<DMatrix<f32>>,
    ///
    biases: Vec<DVector<f32>>,
}

impl Network {
    /// build a new Network by given parameters
    pub fn new(sizes: Vec<u8>) -> Result<Network, &'static str> {
        use rand::distributions::{Normal, IndependentSample};

        if sizes.len() < 3 {
            return Err("Not enough layers");
        }

        // Store the weights and biases in lists
        // We will not need weights or biases for input layer, so ignore that (hence -1)
        let mut weights: Vec<DMatrix<f32>> = Vec::with_capacity(sizes.len() - 1);
        let mut biases: Vec<DVector<f32>> = Vec::with_capacity(sizes.len() - 1);

        let mut rng = rand::thread_rng();
        let normal = Normal::new(0.0, 1.0);

        for i in 1..sizes.len() {

            unsafe {
                biases.push(DVector::new_uninitialized(sizes[i] as usize));
            }

            for j in 0..sizes[i] {
                biases[i - 1][j as usize] = normal.ind_sample(&mut rng) as f32;
            }
            weights.push(DMatrix::new_random_normal(sizes[i - 1] as usize, sizes[i] as usize));
        }

        Ok(Network {
            layers: sizes,
            weights: weights,
            biases: biases,
        })
    }

    /// returns the layers
    pub fn get_layers(&self) -> &Vec<u8> {
        &self.layers
    }

    /// returns the weights
    pub fn get_weights(&self) -> &Vec<DMatrix<f32>> {
        &self.weights
    }

    /// returns the biases
    pub fn get_biases(&self) -> &Vec<DVector<f32>> {
        &self.biases
    }
}

/// trait for implementing random values from 0 to 1
trait Random {
    fn new_random_normal(rows: usize, columns: usize) -> DMatrix<f32>;
}

impl Random for DMatrix<f32> {
    /// Implements random number initializer for DMatrix
    fn new_random_normal(rows: usize, columns: usize) -> DMatrix<f32> {

        // Init the DMatrix with rows and columns
        let mut res;
        unsafe {
            res = DMatrix::new_uninitialized(rows, columns);
        }

        // random number between 0 and 1
        let mut rng = rand::thread_rng();
        let normal = Normal::new(0.0, 1.0);

        // fills matrix with random numbers from 0 and 1
        for i in 0..rows {
            for j in 0..columns {
                res[(i, j)] = normal.ind_sample(&mut rng) as f32;
            }
        }

        res
    }
}
<|endoftext|>


<commit_before>use std;
use std::ffi::CString;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::ptr;
use std::slice;

use class::ClassId;
use ctxt::{SemContext, get_ctxt};
use gc::root::IndirectObj;
use mem;
use vtable::VTable;

pub struct Header {
    // ptr to class
    vtable: *mut VTable,

    // additional information>
    // bit 0 - marked flag
    info: usize,
}

impl Header {
    pub fn size() -> i32 {
        std::mem::size_of::<Header>() as i32
    }

    pub fn vtblptr(&self) -> *mut VTable {
        self.vtable
    }

    pub fn vtbl(&self) -> &mut VTable {
        unsafe { &mut *self.vtable }
    }

    pub fn set_mark(&mut self, value: bool) {
        self.info = if value {
            self.info | 1
        } else {
            self.info & (!1)
        };
    }

    pub fn marked(&self) -> bool {
        if (self.info & 1) != 0 { true } else { false }
    }
}

// is used to reference any object
pub struct Obj {
    header: Header,
    data: u8,
}

impl Obj {
    pub fn header(&self) -> &Header {
        &self.header
    }

    pub fn header_mut(&mut self) -> &mut Header {
        &mut self.header
    }

    pub fn data(&self) -> *const u8 {
        &self.data as *const u8
    }

    pub fn size(&self) -> usize {
        let cls = self.header().vtbl().class();
        let size = cls.size;

        if size > 0 {
            return size as usize;
        }

        let ty = cls.ty;

        if cls.is_array {
            let handle: Handle<ByteArray> = Handle { ptr: self as *const Obj as *const ByteArray };

            let value = Header::size() as usize + mem::ptr_width() as usize +
                        cls.element_size as usize * handle.len() as usize;

            return mem::align_usize(value, mem::ptr_width() as usize);
        }

        if ty.cls_id() == get_ctxt().primitive_classes.str_class {
            let handle: Handle<Str> = Handle { ptr: self as *const Obj as *const Str };
            mem::align_usize(handle.size(), mem::ptr_width() as usize)
        } else {
            panic!("size unknown");
        }
    }

    pub fn visit_reference_fields<F>(&mut self, mut f: F)
        where F: FnMut(IndirectObj)
    {
        let classptr = self.header().vtbl().classptr;
        let cls = unsafe { &*classptr };

        if cls.is_object_array {
            let array = unsafe { &*(self as *const _ as *const StrArray) };

            // walk through all objects in array
            let mut ptr = array.data() as *mut *mut Obj;
            let last = unsafe { ptr.offset(array.len() as isize) };

            while ptr < last {
                f((ptr as usize).into());

                unsafe { ptr = ptr.offset(1) }
            }

            return;
        }

        for &offset in &cls.ref_fields {
            let obj = (self as *mut Obj as usize) + offset as usize;
            f(obj.into());
        }
    }
}

pub struct Handle<T> {
    ptr: *const T,
}

unsafe impl<T> Send for Handle<T> {}

impl<T> Handle<T> {
    pub fn null() -> Handle<T> {
        Handle { ptr: ptr::null() }
    }

    pub fn raw(&self) -> *const T {
        self.ptr
    }
}

// known limitation of #[derive(Copy, Clone)]
// traits need to be implemented manually
impl<T> Copy for Handle<T> {}
impl<T> Clone for Handle<T> {
    fn clone(&self) -> Handle<T> {
        *self
    }
}

impl<T> Deref for Handle<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.ptr }
    }
}

impl<T> DerefMut for Handle<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *(self.ptr as *mut T) }
    }
}

impl<T> Into<Handle<T>> for usize {
    fn into(self) -> Handle<T> {
        Handle { ptr: self as *const T }
    }
}

pub struct Str {
    header: Header,
    length: usize,
    data: u8,
}

impl Str {
    pub fn header(&self) -> &Header {
        &self.header
    }

    pub fn header_mut(&mut self) -> &mut Header {
        &mut self.header
    }

    pub fn len(&self) -> usize {
        self.length
    }

    pub fn data(&self) -> *const u8 {
        &self.data as *const u8
    }

    pub fn to_cstring(&self) -> CString {
        let view = unsafe { slice::from_raw_parts(self.data(), self.len()) };

        CString::new(view).unwrap()
    }

    pub fn size(&self) -> usize {
        Header::size() as usize         // Object header
            + mem::ptr_width() as usize // length field
            + self.len() + 1 // string content
    }

    /// allocates string from buffer in permanent space
    pub fn from_buffer_in_perm(ctxt: &SemContext, buf: &[u8]) -> Handle<Str> {
        let mut handle = str_alloc_perm(ctxt, buf.len());
        handle.length = buf.len();

        unsafe {
            let data = handle.data() as *mut u8;

            // copy buffer content into Str
            ptr::copy_nonoverlapping(buf.as_ptr(), data, buf.len());

            // string should end with 0 for C compatibility
            *(data.offset(buf.len() as isize)) = 0;
        }

        handle
    }

    /// allocates string from buffer in permanent space
    pub fn from_buffer(ctxt: &SemContext, buf: &[u8]) -> Handle<Str> {
        let mut handle = str_alloc_heap(ctxt, buf.len());
        handle.length = buf.len();

        unsafe {
            let data = handle.data() as *mut u8;

            // copy buffer content into Str
            ptr::copy_nonoverlapping(buf.as_ptr(), data, buf.len());

            // string should end with 0 for C compatibility
            *(data.offset(buf.len() as isize)) = 0;
        }

        handle
    }

    pub fn concat(ctxt: &SemContext, lhs: Handle<Str>, rhs: Handle<Str>) -> Handle<Str> {
        let len = lhs.len() + rhs.len();
        let mut handle = str_alloc_heap(ctxt, len);

        unsafe {
            handle.length = len;

            ptr::copy_nonoverlapping(lhs.data(), handle.data() as *mut u8, lhs.len());
            ptr::copy_nonoverlapping(rhs.data(),
                                     handle.data().offset(lhs.len() as isize) as *mut u8,
                                     rhs.len());

            *(handle.data().offset(len as isize) as *mut u8) = 0;
        }

        handle
    }

    // duplicate string into a new object
    pub fn dup(&self, ctxt: &SemContext) -> Handle<Str> {
        let len = self.len();
        let mut handle = str_alloc_heap(ctxt, len);

        unsafe {
            handle.length = len;

            ptr::copy_nonoverlapping(self.data(), handle.data() as *mut u8, len);
            *(handle.data().offset(len as isize) as *mut u8) = 0;
        }

        handle
    }
}

fn str_alloc_heap(ctxt: &SemContext, len: usize) -> Handle<Str> {
    str_alloc(ctxt,
              len,
              |ctxt, size| ctxt.gc.alloc(ctxt, size) as *const u8)
}

fn str_alloc_perm(ctxt: &SemContext, len: usize) -> Handle<Str> {
    str_alloc(ctxt, len, |ctxt, size| ctxt.gc.alloc_perm(size))
}

fn str_alloc<F>(ctxt: &SemContext, len: usize, alloc: F) -> Handle<Str>
    where F: FnOnce(&SemContext, usize) -> *const u8
{
    let size = Header::size() as usize     // Object header
                + mem::ptr_width() as usize // length field
                + len + 1; // string content

    let size = mem::align_usize(size, mem::ptr_width() as usize);
    let ptr = alloc(ctxt, size) as usize;

    let clsid = ctxt.primitive_classes.str_class;
    let cls = ctxt.classes[clsid].borrow();
    let vtable: *const VTable = &**cls.vtable.as_ref().unwrap();
    let mut handle: Handle<Str> = ptr.into();
    handle.header_mut().vtable = vtable as *mut VTable;

    handle
}

pub struct Array<T: Copy> {
    header: Header,
    length: usize,
    data: u8,
    phantom: PhantomData<T>,
}

impl<T> Array<T>
    where T: Copy
{
    pub fn header(&self) -> &Header {
        &self.header
    }

    pub fn header_mut(&mut self) -> &mut Header {
        &mut self.header
    }

    pub fn len(&self) -> usize {
        self.length
    }

    pub fn data(&self) -> *const T {
        &self.data as *const u8 as *const T
    }

    pub fn data_mut(&mut self) -> *mut T {
        &self.data as *const u8 as *mut T
    }

    pub fn size(&self) -> usize {
        Header::size() as usize         // Object header
            + mem::ptr_width() as usize // length field
            + self.len() * std::mem::size_of::<T>() // array content
    }

    pub fn alloc(ctxt: &SemContext, len: usize, elem: T, clsid: ClassId) -> Handle<Array<T>> {
        let size = Header::size() as usize        // Object header
                   + mem::ptr_width() as usize    // length field
                   + len * std::mem::size_of::<T>(); // array content

        let ptr = ctxt.gc.alloc(ctxt, size) as usize;
        let cls = ctxt.classes[clsid].borrow();
        let vtable: *const VTable = &**cls.vtable.as_ref().unwrap();
        let mut handle: Handle<Array<T>> = ptr.into();
        handle.header_mut().vtable = vtable as *mut VTable;
        handle.length = len;

        for i in 0..handle.len() {
            unsafe {
                *handle.data_mut().offset(i as isize) = elem;
            }
        }

        handle
    }
}

pub fn offset_of_array_length() -> i32 {
    offset_of!(Array<i32>, length) as i32
}

pub fn offset_of_array_data() -> i32 {
    offset_of!(Array<i32>, data) as i32
}

pub type BoolArray = Array<bool>;
pub type ByteArray = Array<u8>;
pub type CharArray = Array<char>;
pub type IntArray = Array<i32>;
pub type LongArray = Array<i64>;
pub type FloatArray = Array<f32>;
pub type DoubleArray = Array<f64>;
pub type StrArray = Array<Handle<Str>>;
<commit_msg>added age() to Obj<commit_after>use std;
use std::ffi::CString;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::ptr;
use std::slice;

use class::ClassId;
use ctxt::{SemContext, get_ctxt};
use gc::root::IndirectObj;
use mem;
use vtable::VTable;

pub struct Header {
    // ptr to class
    vtable: *mut VTable,

    // additional information>
    // bit 0 - marked flag
    // bit 1-4 - age (4 bits)
    info: usize,
}

impl Header {
    pub fn size() -> i32 {
        std::mem::size_of::<Header>() as i32
    }

    pub fn vtblptr(&self) -> *mut VTable {
        self.vtable
    }

    pub fn vtbl(&self) -> &mut VTable {
        unsafe { &mut *self.vtable }
    }

    pub fn set_mark(&mut self, value: bool) {
        self.info = if value {
            self.info | 1
        } else {
            self.info & (!1)
        };
    }

    pub fn marked(&self) -> bool {
        if (self.info & 1) != 0 { true } else { false }
    }

    pub fn age(&self) -> u32 {
        (self.info & 0x1E) as u32 >> 1
    }

    pub fn set_age(&mut self, age: u32) {
        assert!(age <= 15);
        self.info = (self.info & !0x1E) | (age << 1) as usize;
    }
}

#[test]
fn test_age() {
    let hdr = Header { vtable: ptr::null_mut(), info: 3 << 1 };
    assert_eq!(3, hdr.age());

    let hdr = Header { vtable: ptr::null_mut(), info: 15 << 1 };
    assert_eq!(15, hdr.age());

    let hdr = Header { vtable: ptr::null_mut(), info: 0 };
    assert_eq!(0, hdr.age());

    let hdr = Header { vtable: ptr::null_mut(), info: 0xFFFF };
    assert_eq!(15, hdr.age());
}

#[test]
fn test_set_age() {
    let mut hdr = Header { vtable: ptr::null_mut(), info: 0 };

    hdr.set_age(15);
    assert_eq!(15, hdr.age());

    hdr.set_age(0);
    assert_eq!(0, hdr.age());

    hdr.set_age(2);
    assert_eq!(2, hdr.age());
}

#[test]
#[should_panic]
fn test_set_age_over_15() {
    let mut hdr = Header { vtable: ptr::null_mut(), info: 0 };
    hdr.set_age(16);
}

// is used to reference any object
pub struct Obj {
    header: Header,
    data: u8,
}

impl Obj {
    pub fn header(&self) -> &Header {
        &self.header
    }

    pub fn header_mut(&mut self) -> &mut Header {
        &mut self.header
    }

    pub fn data(&self) -> *const u8 {
        &self.data as *const u8
    }

    pub fn size(&self) -> usize {
        let cls = self.header().vtbl().class();
        let size = cls.size;

        if size > 0 {
            return size as usize;
        }

        let ty = cls.ty;

        if cls.is_array {
            let handle: Handle<ByteArray> = Handle { ptr: self as *const Obj as *const ByteArray };

            let value = Header::size() as usize + mem::ptr_width() as usize +
                        cls.element_size as usize * handle.len() as usize;

            return mem::align_usize(value, mem::ptr_width() as usize);
        }

        if ty.cls_id() == get_ctxt().primitive_classes.str_class {
            let handle: Handle<Str> = Handle { ptr: self as *const Obj as *const Str };
            mem::align_usize(handle.size(), mem::ptr_width() as usize)
        } else {
            panic!("size unknown");
        }
    }

    pub fn visit_reference_fields<F>(&mut self, mut f: F)
        where F: FnMut(IndirectObj)
    {
        let classptr = self.header().vtbl().classptr;
        let cls = unsafe { &*classptr };

        if cls.is_object_array {
            let array = unsafe { &*(self as *const _ as *const StrArray) };

            // walk through all objects in array
            let mut ptr = array.data() as *mut *mut Obj;
            let last = unsafe { ptr.offset(array.len() as isize) };

            while ptr < last {
                f((ptr as usize).into());

                unsafe { ptr = ptr.offset(1) }
            }

            return;
        }

        for &offset in &cls.ref_fields {
            let obj = (self as *mut Obj as usize) + offset as usize;
            f(obj.into());
        }
    }
}

pub struct Handle<T> {
    ptr: *const T,
}

unsafe impl<T> Send for Handle<T> {}

impl<T> Handle<T> {
    pub fn null() -> Handle<T> {
        Handle { ptr: ptr::null() }
    }

    pub fn raw(&self) -> *const T {
        self.ptr
    }
}

// known limitation of #[derive(Copy, Clone)]
// traits need to be implemented manually
impl<T> Copy for Handle<T> {}
impl<T> Clone for Handle<T> {
    fn clone(&self) -> Handle<T> {
        *self
    }
}

impl<T> Deref for Handle<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.ptr }
    }
}

impl<T> DerefMut for Handle<T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *(self.ptr as *mut T) }
    }
}

impl<T> Into<Handle<T>> for usize {
    fn into(self) -> Handle<T> {
        Handle { ptr: self as *const T }
    }
}

pub struct Str {
    header: Header,
    length: usize,
    data: u8,
}

impl Str {
    pub fn header(&self) -> &Header {
        &self.header
    }

    pub fn header_mut(&mut self) -> &mut Header {
        &mut self.header
    }

    pub fn len(&self) -> usize {
        self.length
    }

    pub fn data(&self) -> *const u8 {
        &self.data as *const u8
    }

    pub fn to_cstring(&self) -> CString {
        let view = unsafe { slice::from_raw_parts(self.data(), self.len()) };

        CString::new(view).unwrap()
    }

    pub fn size(&self) -> usize {
        Header::size() as usize         // Object header
            + mem::ptr_width() as usize // length field
            + self.len() + 1 // string content
    }

    /// allocates string from buffer in permanent space
    pub fn from_buffer_in_perm(ctxt: &SemContext, buf: &[u8]) -> Handle<Str> {
        let mut handle = str_alloc_perm(ctxt, buf.len());
        handle.length = buf.len();

        unsafe {
            let data = handle.data() as *mut u8;

            // copy buffer content into Str
            ptr::copy_nonoverlapping(buf.as_ptr(), data, buf.len());

            // string should end with 0 for C compatibility
            *(data.offset(buf.len() as isize)) = 0;
        }

        handle
    }

    /// allocates string from buffer in permanent space
    pub fn from_buffer(ctxt: &SemContext, buf: &[u8]) -> Handle<Str> {
        let mut handle = str_alloc_heap(ctxt, buf.len());
        handle.length = buf.len();

        unsafe {
            let data = handle.data() as *mut u8;

            // copy buffer content into Str
            ptr::copy_nonoverlapping(buf.as_ptr(), data, buf.len());

            // string should end with 0 for C compatibility
            *(data.offset(buf.len() as isize)) = 0;
        }

        handle
    }

    pub fn concat(ctxt: &SemContext, lhs: Handle<Str>, rhs: Handle<Str>) -> Handle<Str> {
        let len = lhs.len() + rhs.len();
        let mut handle = str_alloc_heap(ctxt, len);

        unsafe {
            handle.length = len;

            ptr::copy_nonoverlapping(lhs.data(), handle.data() as *mut u8, lhs.len());
            ptr::copy_nonoverlapping(rhs.data(),
                                     handle.data().offset(lhs.len() as isize) as *mut u8,
                                     rhs.len());

            *(handle.data().offset(len as isize) as *mut u8) = 0;
        }

        handle
    }

    // duplicate string into a new object
    pub fn dup(&self, ctxt: &SemContext) -> Handle<Str> {
        let len = self.len();
        let mut handle = str_alloc_heap(ctxt, len);

        unsafe {
            handle.length = len;

            ptr::copy_nonoverlapping(self.data(), handle.data() as *mut u8, len);
            *(handle.data().offset(len as isize) as *mut u8) = 0;
        }

        handle
    }
}

fn str_alloc_heap(ctxt: &SemContext, len: usize) -> Handle<Str> {
    str_alloc(ctxt,
              len,
              |ctxt, size| ctxt.gc.alloc(ctxt, size) as *const u8)
}

fn str_alloc_perm(ctxt: &SemContext, len: usize) -> Handle<Str> {
    str_alloc(ctxt, len, |ctxt, size| ctxt.gc.alloc_perm(size))
}

fn str_alloc<F>(ctxt: &SemContext, len: usize, alloc: F) -> Handle<Str>
    where F: FnOnce(&SemContext, usize) -> *const u8
{
    let size = Header::size() as usize     // Object header
                + mem::ptr_width() as usize // length field
                + len + 1; // string content

    let size = mem::align_usize(size, mem::ptr_width() as usize);
    let ptr = alloc(ctxt, size) as usize;

    let clsid = ctxt.primitive_classes.str_class;
    let cls = ctxt.classes[clsid].borrow();
    let vtable: *const VTable = &**cls.vtable.as_ref().unwrap();
    let mut handle: Handle<Str> = ptr.into();
    handle.header_mut().vtable = vtable as *mut VTable;

    handle
}

pub struct Array<T: Copy> {
    header: Header,
    length: usize,
    data: u8,
    phantom: PhantomData<T>,
}

impl<T> Array<T>
    where T: Copy
{
    pub fn header(&self) -> &Header {
        &self.header
    }

    pub fn header_mut(&mut self) -> &mut Header {
        &mut self.header
    }

    pub fn len(&self) -> usize {
        self.length
    }

    pub fn data(&self) -> *const T {
        &self.data as *const u8 as *const T
    }

    pub fn data_mut(&mut self) -> *mut T {
        &self.data as *const u8 as *mut T
    }

    pub fn size(&self) -> usize {
        Header::size() as usize         // Object header
            + mem::ptr_width() as usize // length field
            + self.len() * std::mem::size_of::<T>() // array content
    }

    pub fn alloc(ctxt: &SemContext, len: usize, elem: T, clsid: ClassId) -> Handle<Array<T>> {
        let size = Header::size() as usize        // Object header
                   + mem::ptr_width() as usize    // length field
                   + len * std::mem::size_of::<T>(); // array content

        let ptr = ctxt.gc.alloc(ctxt, size) as usize;
        let cls = ctxt.classes[clsid].borrow();
        let vtable: *const VTable = &**cls.vtable.as_ref().unwrap();
        let mut handle: Handle<Array<T>> = ptr.into();
        handle.header_mut().vtable = vtable as *mut VTable;
        handle.length = len;

        for i in 0..handle.len() {
            unsafe {
                *handle.data_mut().offset(i as isize) = elem;
            }
        }

        handle
    }
}

pub fn offset_of_array_length() -> i32 {
    offset_of!(Array<i32>, length) as i32
}

pub fn offset_of_array_data() -> i32 {
    offset_of!(Array<i32>, data) as i32
}

pub type BoolArray = Array<bool>;
pub type ByteArray = Array<u8>;
pub type CharArray = Array<char>;
pub type IntArray = Array<i32>;
pub type LongArray = Array<i64>;
pub type FloatArray = Array<f32>;
pub type DoubleArray = Array<f64>;
pub type StrArray = Array<Handle<Str>>;
<|endoftext|>


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

#![crate_id="macro_platformtree"]
#![crate_type="dylib"]

#![feature(plugin_registrar, quote, managed_boxes)]

extern crate rustc;
extern crate syntax;
extern crate platformtree;

use rustc::plugin::Registry;
use syntax::ast::TokenTree;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult};
use syntax::ext::base;
use syntax::ext::quote::rt::{ToTokens, ToSource, ExtParseUtils};

use platformtree::parser::Parser;
use platformtree::context;

/// Register available macros.
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
  reg.register_macro("platformtree_parse", platformtree_parse);
}

/// platformtree_parse parses a platfrom tree into node::Node struct.
pub fn platformtree_parse(cx: &mut ExtCtxt, _: Span, tts: &[TokenTree])
    -> Box<MacResult> {
  base::MacExpr::new(quote_expr!(&*cx, true))
}
<commit_msg>platform tree!() macro is now functional.<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Vladimir "farcaller" Pouzanov <farcaller@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![crate_id="macro_platformtree"]
#![crate_type="dylib"]

#![feature(plugin_registrar, quote, managed_boxes)]

extern crate rustc;
extern crate syntax;
extern crate debug;
extern crate platformtree;

use std::gc::Gc;
use rustc::plugin::Registry;
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult};
use syntax::util::small_vector::SmallVector;

use platformtree::parser::Parser;
use platformtree::builder::build_platformtree;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
  reg.register_macro("platformtree", macro_platformtree);
}

pub fn macro_platformtree(cx: &mut ExtCtxt, _: Span, tts: &[ast::TokenTree])
    -> Box<MacResult> {
  let pt = Parser::new(cx, tts).parse_platformtree();
  let builder = build_platformtree(cx, &pt.unwrap());

  let items = builder.emit_items(cx);
  MacItems::new(items)
}

pub struct MacItems {
  items: Vec<Gc<ast::Item>>
}

impl MacItems {
  pub fn new(items: Vec<Gc<ast::Item>>) -> Box<MacResult> {
    box MacItems { items: items } as Box<MacResult>
  }
}
impl MacResult for MacItems {
  fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
    Some(SmallVector::many(self.items.clone()))
  }
}
<|endoftext|>


<commit_before>extern crate lancelot;
extern crate log;

use std::collections::HashMap;
use std::env;
use std::ops::Range;
use std::process;

use better_panic;
use failure::{Error, Fail};
use goblin::Object;
use log::{error, info, trace};

use lancelot::workspace::Workspace;

#[derive(Debug, Fail)]
pub enum MainError {
    #[fail(display = "foo")]
    Foo,
    #[fail(display = "Invalid PE file")]
    InvalidFile,
}

pub struct Config {
    pub filename: String,
}

impl Config {
    pub fn from_args(args: env::Args) -> Result<Config, &'static str> {
        let args: Vec<String> = args.collect();

        if args.len() < 2 {
            return Err("not enough arguments: provide `mapa.exe /path/to/input.exe`");
        }

        let filename = args[1].clone();
        trace!("config: parsed filename: {:?}", filename);

        Ok(Config { filename })
    }
}

pub fn setup_logging(_args: &Config) {
    simplelog::TermLogger::init(simplelog::LevelFilter::Info, simplelog::Config::default())
        .expect("failed to setup logging");
}

/*
    File
      Header,
        IMAGE_DOS_HEADER,
        IMAGE_NT_HEADERS,
          IMAGE_FILE_HEADER,
          IMAGE_OPTIONAL_HEADER,
      IMAGE_SECTION_HEADER,
      Section(u32),
*/

#[derive(Debug)]
enum Structure {
    /// the complete file
    File,
    /// the file headers.
    Header,
    IMAGE_DOS_HEADER,
    IMAGE_NT_HEADERS,
    IMAGE_FILE_HEADER,
    IMAGE_OPTIONAL_HEADER,
    IMAGE_SECTION_HEADER(u16),
    /// a section's content
    Section(u16),
}

pub fn run(args: &Config) -> Result<(), Error> {
    info!("filename: {:?}", args.filename);

    let ws = Workspace::from_file(&args.filename)?.load()?;
    let pe = match Object::parse(&ws.buf) {
        Ok(Object::PE(pe)) => pe,
        _ => return Err(MainError::InvalidFile.into()),
    };

    let mut locations: HashMap<Range<u64>, Structure> = HashMap::new();

    locations.insert(
        Range {
            start: 0,
            end: ws.buf.len() as u64,
        },
        Structure::File,
    );

    let sizeof_IMAGE_DOS_HEADER = 0x40;
    locations.insert(
        Range {
            start: 0,
            end: sizeof_IMAGE_DOS_HEADER,
        },
        Structure::IMAGE_DOS_HEADER,
    );

    let offset_IMAGE_NT_HEADERS = pe.header.dos_header.pe_pointer as u64;
    let sizeof_Signature: u64 = 0x4;
    let sizeof_IMAGE_FILE_HEADER: u64 = 0x14;
    locations.insert(
        Range {
            start: offset_IMAGE_NT_HEADERS,
            end: offset_IMAGE_NT_HEADERS
                + sizeof_Signature
                + sizeof_IMAGE_FILE_HEADER
                + (pe.header.coff_header.size_of_optional_header as u64),
        },
        Structure::IMAGE_NT_HEADERS,
    );

    let offset_IMAGE_FILE_HEADER = offset_IMAGE_NT_HEADERS + sizeof_Signature;
    locations.insert(
        Range {
            start: offset_IMAGE_FILE_HEADER,
            end: offset_IMAGE_FILE_HEADER + sizeof_IMAGE_FILE_HEADER,
        },
        Structure::IMAGE_FILE_HEADER,
    );

    let end_of_headers = if pe.header.coff_header.size_of_optional_header > 0 {
        let offset_IMAGE_OPTIONAL_HEADER = offset_IMAGE_FILE_HEADER + sizeof_IMAGE_FILE_HEADER;
        let end_of_headers = offset_IMAGE_OPTIONAL_HEADER
                    + (pe.header.coff_header.size_of_optional_header as u64);
        locations.insert(
            Range {
                start: offset_IMAGE_OPTIONAL_HEADER,
                end: end_of_headers,
            },
            Structure::IMAGE_OPTIONAL_HEADER,
        );
        locations.insert(
            Range {
                start: 0x0,
                end: offset_IMAGE_OPTIONAL_HEADER
                    + (pe.header.coff_header.size_of_optional_header as u64),
            },
            Structure::Header,
        );
        end_of_headers
    } else {
        let end_of_headers = offset_IMAGE_FILE_HEADER + sizeof_IMAGE_FILE_HEADER;
        locations.insert(
            Range {
                start: 0x0,
                end: end_of_headers,
            },
            Structure::Header,
        );
        end_of_headers
    };

    let mut offset_IMAGE_SECTION_HEADER = end_of_headers;
    let sizeof_IMAGE_SECTION_HEADER = 0x28;
    for i in 0..pe.header.coff_header.number_of_sections {
        locations.insert(
            Range {
                start: offset_IMAGE_SECTION_HEADER,
                end: offset_IMAGE_SECTION_HEADER + sizeof_IMAGE_SECTION_HEADER,
            },
            Structure::IMAGE_SECTION_HEADER(i)
        );

        let section = &pe.sections[i as usize];
        locations.insert(
            Range {
                start: section.pointer_to_raw_data as u64,
                end: (section.pointer_to_raw_data + section.size_of_raw_data) as u64,
            },
            Structure::Section(i),
        );

        offset_IMAGE_SECTION_HEADER += sizeof_IMAGE_SECTION_HEADER;
    }

    let mut ranges: Vec<Range<u64>> = locations.keys().cloned().collect();
    ranges.sort_by(|a, b|
        if a.start < b.start {
            std::cmp::Ordering::Less
        } else if a.start > b.start {
            std::cmp::Ordering::Greater
        } else if a.end < b.end {
            std::cmp::Ordering::Greater
        } else if a.end > b.end {
            std::cmp::Ordering::Less
        } else {
            std::cmp::Ordering::Equal
        }
    );

    for range in ranges.iter() {
        println!("{:?} {:?}", range, locations[range]);
    }

    Ok(())
}

fn main() {
    better_panic::install();

    let args = Config::from_args(env::args()).unwrap_or_else(|err| {
        eprintln!("error parsing arguments: {}", err);
        process::exit(1);
    });

    setup_logging(&args);

    if let Err(e) = run(&args) {
        error!("{:?}", e)
    }
}
<commit_msg>mapa: show strings in sections<commit_after>extern crate log;
extern crate lancelot;

use std::collections::HashMap;
use std::env;
use std::ops::Range;
use std::process;

use better_panic;
use failure::{Error, Fail};
use goblin::Object;
use log::{error, info, trace};
use regex::bytes::Regex;

use lancelot::util;
use lancelot::workspace::Workspace;

#[derive(Debug, Fail)]
pub enum MainError {
    #[fail(display = "foo")]
    Foo,
    #[fail(display = "Invalid PE file")]
    InvalidFile,
}

pub struct Config {
    pub filename: String,
}

impl Config {
    pub fn from_args(args: env::Args) -> Result<Config, &'static str> {
        let args: Vec<String> = args.collect();

        if args.len() < 2 {
            return Err("not enough arguments: provide `mapa.exe /path/to/input.exe`");
        }

        let filename = args[1].clone();
        trace!("config: parsed filename: {:?}", filename);

        Ok(Config { filename })
    }
}

pub fn setup_logging(_args: &Config) {
    simplelog::TermLogger::init(simplelog::LevelFilter::Info, simplelog::Config::default())
        .expect("failed to setup logging");
}

/*
    File
      Header,
        IMAGE_DOS_HEADER,
        IMAGE_NT_HEADERS,
          Signature
          IMAGE_FILE_HEADER,
          IMAGE_OPTIONAL_HEADER,
      IMAGE_SECTION_HEADER,
      Section(u32),
      Slack
*/

#[derive(Debug, Copy, Clone)]
enum Structure {
    /// the complete file
    File,
    /// the file headers.
    Header,
    IMAGE_DOS_HEADER,
    IMAGE_NT_HEADERS,
    Signature,
    IMAGE_FILE_HEADER,
    IMAGE_OPTIONAL_HEADER,
    IMAGE_SECTION_HEADER(u16),
    /// a section's content
    Section(u16),
    Slack,
}

fn cmp_ranges(a: &Range<u64>, b: &Range<u64>) -> std::cmp::Ordering {
    if a.start < b.start {
        std::cmp::Ordering::Less
    } else if a.start > b.start {
        std::cmp::Ordering::Greater
    } else if a.end < b.end {
        std::cmp::Ordering::Greater
    } else if a.end > b.end {
        std::cmp::Ordering::Less
    } else {
        std::cmp::Ordering::Equal
    }
}

struct MapNode {
    range: Range<u64>,
    structure: Structure,
    children: Vec<MapNode>,
}

impl MapNode {
    fn new(range: Range<u64>, structure: Structure) -> MapNode {
        MapNode {
            range,
            structure,
            children: vec![],
        }
    }

    fn contains(&self, other: &MapNode) -> bool {
        self.range.start <= other.range.start
          && self.range.end >= other.range.end
    }

    fn insert(&mut self, other: MapNode) {
        if ! self.contains(&other) {
            panic!("this node does not contain the child")
        }

        match self.children.iter_mut().find(|child| child.contains(&other)) {
            Some(child) => child.insert(other),
            None => {
                self.children.push(other);
                self.children.sort_by(|a, b| cmp_ranges(&a.range, &b.range));
            }
        }
    }

    fn find_slack(&mut self) {
        let mut offset = self.range.start;
        let mut slacks: Vec<Range<u64>> = vec![];
        for child in self.children.iter() {
            if child.range.start > offset {
                slacks.push(Range {
                    start: offset,
                    end: child.range.start,
                });
                offset = child.range.end;
            } else {
                offset = child.range.end;
            }
        }
        for slack in slacks.iter() {
            self.insert(MapNode::new(slack.clone(), Structure::Slack));
        }
        for child in self.children.iter_mut() {
            child.find_slack();
        }
    }

    fn find_ascii_strings(buf: &[u8]) -> Vec<(u64, String)> {
        // TODO: don't do this in a loop
        let re: Regex = Regex::new("[ -~]{4,}").unwrap();

        re.captures_iter(buf)
            .map(|cap| {
                // guaranteed to have at least one hit
                let mat = cap.get(0).unwrap();

                // this had better be ASCII, and therefore able to be decoded.
                let s = String::from_utf8(mat.as_bytes().to_vec()).unwrap();

                (mat.start() as u64, s)
            })
            .collect()
    }

    fn find_unicode_strings(buf: &[u8]) -> Vec<(u64, String)> {
        // TODO: don't do this in a loop
        let re: Regex = Regex::new("([ -~]\x00){4,}").unwrap();

        re.captures_iter(buf)
            .map(|cap| {
                // guaranteed to have at least one hit
                let mat = cap.get(0).unwrap();

                // this had better be ASCII, and therefore able to be decoded.
                let bytes = mat.as_bytes();
                let words: Vec<u16> = bytes
                    .chunks_exact(2)
                    .map(|w| u16::from(w[1]) << 8 | u16::from(w[0]))
                    .collect();

                // danger: the unwrap here might feasibly fail
                let s = String::from_utf16(&words).unwrap();

                (mat.start() as u64, s)
            })
            .collect()
    }

    // TODO: move this to a util
    fn find_strings(buf: &[u8]) -> Vec<(u64, String)> {
        let mut strings: Vec<(u64, String)> = vec![];
        let mut ascii = MapNode::find_ascii_strings(buf);
        let mut unicode = MapNode::find_unicode_strings(buf);

        strings.append(&mut ascii);
        strings.append(&mut unicode);

        strings.sort_by(|a, b| a.0.cmp(&b.0));

        strings
    }

    fn render(&self, buf: &[u8]) -> Result<Vec<String>, Error> {
        let mut ret: Vec<String> = vec![];

        // ref: https://www.fileformat.info/info/unicode/block/box_drawing/list.htm
        ret.push(format!("┌── {:#08x} {:?}", self.range.start, self.structure));

        match self.structure {
            Structure::IMAGE_SECTION_HEADER(_)
            | Structure::IMAGE_DOS_HEADER
            | Structure::Signature
            | Structure::IMAGE_FILE_HEADER
            | Structure::IMAGE_OPTIONAL_HEADER
            => {
                // render hexdump
                let region = &buf[self.range.start as usize..self.range.end as usize];
                for line in lancelot::util::hexdump(region,
                                                    self.range.start as usize).split("\n") {
                    if line != "" {
                        ret.push(format!("│   {}", line))
                    }
                }
            },
            Structure::Slack
            | Structure::Section(_)
            => {
                let region = &buf[self.range.start as usize..self.range.end as usize];
                for (offset, string) in MapNode::find_strings(region).iter() {
                    ret.push(format!("│   {:#08x} string: {}", self.range.start + offset, string));
                }
            },
            _ => {
                // render children
                for (i, child) in self.children.iter().enumerate() {
                    for line in child.render(buf)?.iter() {
                        ret.push(format!("│  {}", line))
                    }
                    if i < self.children.len() - 1 {
                        ret.push(format!("│"));
                    }
                }
            }
        }

        ret.push(format!("└── {:#08x} ────────", self.range.end));

        Ok(ret)
    }
}

fn get_node(map: &Map, range: Range<u64>) -> MapNode {
    MapNode::new(range.clone(), map.locations[&range])
}

fn build_tree(map: &Map) -> Result<MapNode, Error> {
    let mut locations: Vec<Range<u64>> = map.locations.keys().cloned().collect();
    locations.sort_by(|a, b| cmp_ranges(a, b));
    locations.reverse();
    let mut root_node = get_node(map, locations.pop().unwrap());  // warning

    while !locations.is_empty() {
        root_node.insert(get_node(map, locations.pop().unwrap()));
    }

    root_node.find_slack();

    Ok(root_node)
}

struct Map {
    locations: HashMap<Range<u64>, Structure>,
}

fn compute_map(ws: &Workspace) -> Result<Map, Error> {
    let pe = match Object::parse(&ws.buf) {
        Ok(Object::PE(pe)) => pe,
        _ => return Err(MainError::InvalidFile.into()),
    };

    let mut locations: HashMap<Range<u64>, Structure> = HashMap::new();

    locations.insert(
        Range {
            start: 0,
            end: ws.buf.len() as u64,
        },
        Structure::File,
    );

    let sizeof_IMAGE_DOS_HEADER = 0x40;
    locations.insert(
        Range {
            start: 0,
            end: sizeof_IMAGE_DOS_HEADER,
        },
        Structure::IMAGE_DOS_HEADER,
    );

    let offset_IMAGE_NT_HEADERS = pe.header.dos_header.pe_pointer as u64;
    let sizeof_Signature: u64 = 0x4;
    let sizeof_IMAGE_FILE_HEADER: u64 = 0x14;
    locations.insert(
        Range {
            start: offset_IMAGE_NT_HEADERS,
            end: offset_IMAGE_NT_HEADERS
                + sizeof_Signature
                + sizeof_IMAGE_FILE_HEADER
                + (pe.header.coff_header.size_of_optional_header as u64),
        },
        Structure::IMAGE_NT_HEADERS,
    );

    locations.insert(
        Range {
            start: offset_IMAGE_NT_HEADERS,
            end: offset_IMAGE_NT_HEADERS + sizeof_Signature,
        },
        Structure::Signature,
    );

    let offset_IMAGE_FILE_HEADER = offset_IMAGE_NT_HEADERS + sizeof_Signature;
    locations.insert(
        Range {
            start: offset_IMAGE_FILE_HEADER,
            end: offset_IMAGE_FILE_HEADER + sizeof_IMAGE_FILE_HEADER,
        },
        Structure::IMAGE_FILE_HEADER,
    );

    let end_of_headers = if pe.header.coff_header.size_of_optional_header > 0 {
        let offset_IMAGE_OPTIONAL_HEADER = offset_IMAGE_FILE_HEADER + sizeof_IMAGE_FILE_HEADER;
        let end_of_headers = offset_IMAGE_OPTIONAL_HEADER
                    + (pe.header.coff_header.size_of_optional_header as u64);
        locations.insert(
            Range {
                start: offset_IMAGE_OPTIONAL_HEADER,
                end: end_of_headers,
            },
            Structure::IMAGE_OPTIONAL_HEADER,
        );
        locations.insert(
            Range {
                start: 0x0,
                end: offset_IMAGE_OPTIONAL_HEADER
                    + (pe.header.coff_header.size_of_optional_header as u64),
            },
            Structure::Header,
        );
        end_of_headers
    } else {
        let end_of_headers = offset_IMAGE_FILE_HEADER + sizeof_IMAGE_FILE_HEADER;
        locations.insert(
            Range {
                start: 0x0,
                end: end_of_headers,
            },
            Structure::Header,
        );
        end_of_headers
    };

    let mut offset_IMAGE_SECTION_HEADER = end_of_headers;
    let sizeof_IMAGE_SECTION_HEADER = 0x28;
    for i in 0..pe.header.coff_header.number_of_sections {
        locations.insert(
            Range {
                start: offset_IMAGE_SECTION_HEADER,
                end: offset_IMAGE_SECTION_HEADER + sizeof_IMAGE_SECTION_HEADER,
            },
            Structure::IMAGE_SECTION_HEADER(i)
        );

        let section = &pe.sections[i as usize];
        locations.insert(
            Range {
                start: section.pointer_to_raw_data as u64,
                end: (section.pointer_to_raw_data + section.size_of_raw_data) as u64,
            },
            Structure::Section(i),
        );

        offset_IMAGE_SECTION_HEADER += sizeof_IMAGE_SECTION_HEADER;
    }

    Ok(Map {
        locations
    })

}

fn render_map(map: &Map, buf: &[u8]) -> Result<(), Error> {
    let tree = build_tree(&map)?;
    for line in tree.render(buf)?.iter() {
        println!("{}", line);
    }

    Ok(())
}

pub fn run(args: &Config) -> Result<(), Error> {
    info!("filename: {:?}", args.filename);

    let ws = Workspace::from_file(&args.filename)?.load()?;
    let map = compute_map(&ws)?;
    render_map(&map, &ws.buf)?;

    Ok(())
}

fn main() {
    better_panic::install();

    let args = Config::from_args(env::args()).unwrap_or_else(|err| {
        eprintln!("error parsing arguments: {}", err);
        process::exit(1);
    });

    setup_logging(&args);

    if let Err(e) = run(&args) {
        error!("{:?}", e)
    }
}
<|endoftext|>


<commit_before>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use hyper;
use serde;
use serde_json;

use Version;
use Projects;
use Groups;
use GroupListing;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
    pagination: Pagination,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                client: {:?}, pagination: {:?} }}",
               self.scheme,
               self.domain,
               self.port,
               self.client,
               self.pagination)
    }
}

impl GitLab {
    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port: port,
            private_token: private_token.to_string(),
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
            pagination: Pagination {
                page: 1,
                per_page: 20,
            },
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", &domain, 80, &private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", &domain, 443, &private_token)
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com:443/api/v3/projects?private_token=XXXXXXXXXXXXX&page=1&per_page=20";
    ///
    /// let gl = GitLab::new_https("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("projects"), expected_url);
    /// ```
    pub fn build_url(&self, command: &str) -> String {
        format!("{}://{}:{}/api/v{}/{}?private_token={}&page={}&per_page={}",
                self.scheme,
                self.domain,
                self.port,
                API_VERSION,
                command,
                self.private_token,
                self.pagination.page,
                self.pagination.per_page)
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url("version");
        // Close connections after each GET.
        let res = self.client.get(&url).header(hyper::header::Connection::close()).send();

        res
    }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = pagination;
    }

    pub fn get<T>(&self, command: &str) -> Result<T, serde_json::Error>
        where T: serde::Deserialize
    {

        let url = self.build_url(command);
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();

        serde_json::from_str(&body.as_str())
    }

    pub fn version(&self) -> Result<Version, serde_json::Error> {
        self.get("version")
    }

    pub fn groups(&self) -> Result<Groups, serde_json::Error> {
        self.get("groups")
    }

    pub fn projects(&self) -> Result<Projects, serde_json::Error> {
        self.get("projects")
    }

    // pub fn groups_listing(&mut self) -> GroupListing {
    pub fn groups_listing(&mut self) {
        // Default::default()
        // GroupListing { options: Default::default() }
    }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<commit_msg>Split 'expected_url' from doctest<commit_after>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use hyper;
use serde;
use serde_json;

use Version;
use Projects;
use Groups;
use GroupListing;

pub const API_VERSION: u16 = 3;

#[derive(Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    private_token: String,
    client: hyper::Client,
    pagination: Pagination,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                client: {:?}, pagination: {:?} }}",
               self.scheme,
               self.domain,
               self.port,
               self.client,
               self.pagination)
    }
}

impl GitLab {
    pub fn new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port: port,
            private_token: private_token.to_string(),
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
            pagination: Pagination {
                page: 1,
                per_page: 20,
            },
        }
    }

    pub fn new_http(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("http", &domain, 80, &private_token)
    }

    pub fn new_https(domain: &str, private_token: &str) -> GitLab {
        GitLab::new("https", &domain, 443, &private_token)
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com:\
    ///                     443/api/v3/projects?private_token=XXXXXXXXXXXXX&page=1&per_page=20";
    ///
    /// let gl = GitLab::new_https("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("projects"), expected_url);
    /// ```
    pub fn build_url(&self, command: &str) -> String {
        format!("{}://{}:{}/api/v{}/{}?private_token={}&page={}&per_page={}",
                self.scheme,
                self.domain,
                self.port,
                API_VERSION,
                command,
                self.private_token,
                self.pagination.page,
                self.pagination.per_page)
    }

    pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
        let url = self.build_url("version");
        // Close connections after each GET.
        let res = self.client.get(&url).header(hyper::header::Connection::close()).send();

        res
    }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = pagination;
    }

    pub fn get<T>(&self, command: &str) -> Result<T, serde_json::Error>
        where T: serde::Deserialize
    {

        let url = self.build_url(command);
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .unwrap();

        let mut body = String::new();
        res.read_to_string(&mut body).unwrap();

        serde_json::from_str(&body.as_str())
    }

    pub fn version(&self) -> Result<Version, serde_json::Error> {
        self.get("version")
    }

    pub fn groups(&self) -> Result<Groups, serde_json::Error> {
        self.get("groups")
    }

    pub fn projects(&self) -> Result<Projects, serde_json::Error> {
        self.get("projects")
    }

    // pub fn groups_listing(&mut self) -> GroupListing {
    pub fn groups_listing(&mut self) {
        // Default::default()
        // GroupListing { options: Default::default() }
    }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<|endoftext|>


<commit_before>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use url;
use hyper;
use serde;
use serde_json;

// use Groups;

use ::errors::*;

pub const API_VERSION: u16 = 3;

#[derive(Default, Clone, Copy, Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

#[derive(Default)]
pub struct GitLab {
    scheme: String,
    domain: String,
    port: u16,
    url: Option<url::Url>,
    private_token: String,
    pagination: Option<Pagination>,
    client: hyper::Client,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                pagination: {:?} }}",
               self.scheme,
               self.domain,
               self.port,
               self.pagination)
    }
}

impl GitLab {
    pub fn _new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        let mut url = url::Url::parse(&format!("{}://{}/api/v{}/", scheme, domain, API_VERSION))
            .unwrap();
        url.set_port(Some(port)).unwrap();
        GitLab {
            scheme: scheme.to_string(),
            domain: domain.to_string(),
            port: port,
            url: Some(url),
            private_token: private_token.to_string(),
            pagination: None,
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
        }
    }

    pub fn new_insecure(domain: &str, private_token: &str) -> GitLab {
        warn!("Using insecure http:// protocol: Token will be sent in clear!");
        GitLab::_new("http", domain, 80, private_token)
    }

    pub fn new(domain: &str, private_token: &str) -> GitLab {
        GitLab::_new("https", domain, 443, private_token)
    }

    pub fn port(mut self, port: u16) -> Self {
        self.port = port;
        self.url.as_mut().map(|url| url.set_port(Some(port)));
        self
    }

    pub fn scheme(mut self, scheme: &str) -> Self {
        self.scheme = scheme.to_string();
        self.url.as_mut().map(|url| url.set_scheme(scheme));
        self
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&private_token=XXXXXXXXXXXXX";
    ///
    /// let gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path"), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> Result<String> {
        let mut new_url =
            self.url.clone().chain_err(|| "failure to clone URL").join(query).chain_err(|| {
                format!("Failure to join query '{}' to url {}",
                        query,
                        self.url.as_str())
            });
        new_url.query_pairs_mut().append_pair("private_token", &self.private_token);
        self.pagination.as_ref().map(|pagination| {
            new_url.query_pairs_mut().append_pair("page", &pagination.page.to_string());
            new_url.query_pairs_mut().append_pair("per_page", &pagination.per_page.to_string());
        });

        Ok(new_url.into_string())
    }

    // pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
    //     let url = self.build_url("version");
    //     // Close connections after each GET.
    //     self.client.get(&url).header(hyper::header::Connection::close()).send()
    // }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T>
        where T: serde::Deserialize
    {
        let url = self.build_url(query);
        info!("url: {:?}", url);

        // Close connections after each GET.
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .chain_err(|| format!("cannot send request '{}' to {:?}", query, self))?;
        info!("res.status: {:?}", res.status);
        debug!("res.headers: {:?}", res.headers);
        debug!("res.url: {:?}", res.url);

        let mut body = String::new();
        res.read_to_string(&mut body).chain_err(|| "cannot read response body")?;
        debug!("body:\n{:?}", body);

        // assert_eq!(res.status, hyper::status::StatusCode::Ok);
        if res.status != hyper::status::StatusCode::Ok {
            bail!(format!("status code ({}) not Ok()", res.status));
        }

        serde_json::from_str(body.as_str())
            .chain_err(|| format!("cannot build Rust struct from JSON data: {}", body))
    }

    pub fn version(&self) -> Result<::Version> {
        self.get("version").chain_err(|| "cannot query 'version'")
    }

    pub fn groups(&self) -> ::groups::GroupsLister {
        ::groups::GroupsLister::new(self)
    }

    pub fn projects(&self) -> ::projects::ProjectsLister {
        ::projects::ProjectsLister::new(self)
    }

    pub fn issues(&self) -> ::issues::IssuesLister {
        ::issues::IssuesLister::new(self)
    }

    pub fn merge_requests(&self, project_id: i64) -> ::merge_requests::MergeRequestsLister {
        ::merge_requests::MergeRequestsLister::new(self, project_id)
    }

    // pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
    //     let query = listing.build_query();
    //     // self.get(&query)
    //     unimplemented!();
    // }

    // pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
    //     let query = ::groups::owned_groups::Listing::new().build_query();
    //     info!("query: {:?}", query);
    //     self.get(&query)
    // }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<commit_msg>Get rid of `GitLab`'s `scheme`, `hostname` and `port`, use `url` instead.<commit_after>
use std::fmt;
use std::io::Read;  // Trait providing read_to_string()
use std::env;

use url;
use hyper;
use serde;
use serde_json;

// use Groups;

use ::errors::*;

pub const API_VERSION: u16 = 3;

#[derive(Default, Clone, Copy, Debug)]
pub struct Pagination {
    pub page: u16,
    pub per_page: u16,
}

#[derive(Default)]
pub struct GitLab {
    url: Option<url::Url>,
    private_token: String,
    pagination: Option<Pagination>,
    client: hyper::Client,
}

// Explicitly implement Debug trait for GitLab so we can hide the token.
impl fmt::Debug for GitLab {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "GitLab {{ scheme: {}, domain: {}, port: {}, private_token: XXXXXXXXXXXXXXXXXXXX, \
                pagination: {:?} }}",
               self.url.scheme(),
               self.url.domain(),
               self.url.port(),
               self.pagination)
    }
}

impl GitLab {
    pub fn _new(scheme: &str, domain: &str, port: u16, private_token: &str) -> GitLab {
        let mut url = url::Url::parse(&format!("{}://{}/api/v{}/", scheme, domain, API_VERSION))
            .unwrap();
        url.set_port(Some(port)).unwrap();
        GitLab {
            url: Some(url),
            private_token: private_token.to_string(),
            pagination: None,
            client: match env::var("HTTP_PROXY") {
                Ok(proxy) => {
                    let proxy: Vec<&str> = proxy.trim_left_matches("http://").split(':').collect();
                    let hostname = proxy[0].to_string();
                    let port = proxy[1];

                    hyper::Client::with_http_proxy(hostname, port.parse().unwrap())
                }
                Err(_) => hyper::Client::new(),
            },
        }
    }

    pub fn new_insecure(domain: &str, private_token: &str) -> GitLab {
        warn!("Using insecure http:// protocol: Token will be sent in clear!");
        GitLab::_new("http", domain, 80, private_token)
    }

    pub fn new(domain: &str, private_token: &str) -> GitLab {
        GitLab::_new("https", domain, 443, private_token)
    }

    pub fn port(mut self, port: u16) -> Self {
        self.url.as_mut().map(|url| url.set_port(Some(port)));
        self
    }

    pub fn scheme(mut self, scheme: &str) -> Self {
        self.url.as_mut().map(|url| url.set_scheme(scheme));
        self
    }

    /// Build a URL used to access GitLab instance, including some parameters.
    ///
    /// # Examples
    ///
    /// Example from GitLab: https://docs.gitlab.com/ce/api/#basic-usage
    ///
    /// ```
    /// use gitlab_api::GitLab;
    ///
    /// let expected_url = "https://gitlab.example.com\
    ///                     /api/v3/groups?order_by=path&private_token=XXXXXXXXXXXXX";
    ///
    /// let gl = GitLab::new("gitlab.example.com", "XXXXXXXXXXXXX");
    ///
    /// assert_eq!(gl.build_url("groups?order_by=path"), expected_url);
    /// ```
    pub fn build_url(&self, query: &str) -> Result<String> {
        let mut new_url =
            self.url.clone().chain_err(|| "failure to clone URL").join(query).chain_err(|| {
                format!("Failure to join query '{}' to url {}",
                        query,
                        self.url.as_str())
            });
        new_url.query_pairs_mut().append_pair("private_token", &self.private_token);
        self.pagination.as_ref().map(|pagination| {
            new_url.query_pairs_mut().append_pair("page", &pagination.page.to_string());
            new_url.query_pairs_mut().append_pair("per_page", &pagination.per_page.to_string());
        });

        Ok(new_url.into_string())
    }

    // pub fn attempt_connection(&self) -> Result<hyper::client::Response, hyper::Error> {
    //     let url = self.build_url("version");
    //     // Close connections after each GET.
    //     self.client.get(&url).header(hyper::header::Connection::close()).send()
    // }

    pub fn set_pagination(&mut self, pagination: Pagination) {
        self.pagination = Some(pagination);
    }

    pub fn get<T>(&self, query: &str) -> Result<T>
        where T: serde::Deserialize
    {
        let url = self.build_url(query);
        info!("url: {:?}", url);

        // Close connections after each GET.
        let mut res: hyper::client::Response = self.client
            .get(&url)
            .header(hyper::header::Connection::close())
            .send()
            .chain_err(|| format!("cannot send request '{}' to {:?}", query, self))?;
        info!("res.status: {:?}", res.status);
        debug!("res.headers: {:?}", res.headers);
        debug!("res.url: {:?}", res.url);

        let mut body = String::new();
        res.read_to_string(&mut body).chain_err(|| "cannot read response body")?;
        debug!("body:\n{:?}", body);

        // assert_eq!(res.status, hyper::status::StatusCode::Ok);
        if res.status != hyper::status::StatusCode::Ok {
            bail!(format!("status code ({}) not Ok()", res.status));
        }

        serde_json::from_str(body.as_str())
            .chain_err(|| format!("cannot build Rust struct from JSON data: {}", body))
    }

    pub fn version(&self) -> Result<::Version> {
        self.get("version").chain_err(|| "cannot query 'version'")
    }

    pub fn groups(&self) -> ::groups::GroupsLister {
        ::groups::GroupsLister::new(self)
    }

    pub fn projects(&self) -> ::projects::ProjectsLister {
        ::projects::ProjectsLister::new(self)
    }

    pub fn issues(&self) -> ::issues::IssuesLister {
        ::issues::IssuesLister::new(self)
    }

    pub fn merge_requests(&self, project_id: i64) -> ::merge_requests::MergeRequestsLister {
        ::merge_requests::MergeRequestsLister::new(self, project_id)
    }

    // pub fn groups(&mut self, listing: ::groups::Listing) -> Result<Groups, serde_json::Error> {
    //     let query = listing.build_query();
    //     // self.get(&query)
    //     unimplemented!();
    // }

    // pub fn owned_groups(&mut self) -> Result<Groups, serde_json::Error> {
    //     let query = ::groups::owned_groups::Listing::new().build_query();
    //     info!("query: {:?}", query);
    //     self.get(&query)
    // }
}

// #[cfg(test)]
// mod tests {
//
// #[test]
// fn it_works() {
// }
// }
//
<|endoftext|>