<commit_before>use std::string;
use std::{char, f64, fmt, io, num, str, mem};
use std::ffi::CString;
use rustc_serialize as serialize;
use std::marker::PhantomData;
use proton_sys;

/*
Marshal encodes a Rust value as AMQP data in buffer based on its type.
If buffer is nil, or is not large enough, a new buffer  is created.

Returns the buffer used for encoding with len() adjusted to the actual size of data.

Rust types are encoded as follows

 +-------------------------------------+--------------------------------------------+
 |Rust type                            |AMQP type                                   |
 +-------------------------------------+--------------------------------------------+
 |bool                                 |bool                                        |
 +-------------------------------------+--------------------------------------------+
 |int8, int16, int32, int64 (int)      |byte, short, int, long (int or long)        |
 +-------------------------------------+--------------------------------------------+
 |uint8, uint16, uint32, uint64 (uint) |ubyte, ushort, uint, ulong (uint or ulong)  |
 +-------------------------------------+--------------------------------------------+
 |float32, float64                     |float, double.                              |
 +-------------------------------------+--------------------------------------------+
 |string                               |string                                      |
 +-------------------------------------+--------------------------------------------+
 |[]byte, Binary                       |binary                                      |
 +-------------------------------------+--------------------------------------------+
 |Symbol                               |symbol                                      |
 +-------------------------------------+--------------------------------------------+
 |interface{}                          |the contained type                          |
 +-------------------------------------+--------------------------------------------+
 |nil                                  |null                                        |
 +-------------------------------------+--------------------------------------------+
 |map[K]T                              |map with K and T converted as above         |
 +-------------------------------------+--------------------------------------------+
 |Map                                  |map, may have mixed types for keys, values  |
 +-------------------------------------+--------------------------------------------+
 |[]T                                  |list with T converted as above              |
 +-------------------------------------+--------------------------------------------+
 |List                                 |list, may have mixed types  values          |
 +-------------------------------------+--------------------------------------------+

TODO: Update the table to match rust types
TODO Rust types: array, slice, struct

Rust types that cannot be marshaled: complex64/128, uintptr, function, interface, channel
*/

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ErrorCode {
// ADD SOMETHING HERE
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ParserError {
    /// msg, line, col
    SyntaxError(ErrorCode, usize, usize),
    IoError(io::ErrorKind, &'static str),
}

// Builder and Parser have the same errors.
pub type BuilderError = ParserError;

#[derive(Clone, PartialEq, Debug)]
pub enum DecoderError {
    ParseError(ParserError),
    ExpectedError(string::String, string::String),
    MissingFieldError(string::String),
    UnknownVariantError(string::String),
    ApplicationError(string::String)
}

#[derive(Clone, Copy, Debug)]
pub enum EncoderError {
    FmtError(fmt::Error),
    BadHashmapKey,
}

pub type EncodeResult = Result<(), EncoderError>;
pub type DecodeResult<T> = Result<T, DecoderError>;

pub struct Encoder<'e> {
    data: *mut proton_sys::pn_data_t,
    __phantom: PhantomData<&'e ()>
}

impl<'e> Encoder<'e> {
    fn new() -> Encoder<'e> {
        Encoder::with_capacity(16)
    }

    fn with_capacity(capacity: ::libc::size_t) -> Encoder<'e> {
        let data = unsafe{proton_sys::pn_data(capacity)};
        Encoder {data: data, __phantom: PhantomData}
    }
}

impl<'a> serialize::Encoder for Encoder<'a> {
    type Error = EncoderError;

    fn emit_nil(&mut self) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_null(&mut *self.data);})
    }

    fn emit_usize(&mut self, v: usize) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_uint(&mut *self.data, v as u32);})
    }
    fn emit_u64(&mut self, v: u64) -> EncodeResult {
        // NOTE(flaper87): check word size
        Ok(unsafe{proton_sys::pn_data_put_ulong(&mut *self.data, v);})
    }
    fn emit_u32(&mut self, v: u32) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_uint(&mut *self.data, v);})
    }
    fn emit_u16(&mut self, v: u16) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_ushort(&mut *self.data, v);})
    }
    fn emit_u8(&mut self, v: u8) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_ubyte(&mut *self.data, v);})
    }

    fn emit_isize(&mut self, v: isize) -> EncodeResult {
        // NOTE(flaper87): check word size
        Ok(unsafe{proton_sys::pn_data_put_int(&mut *self.data, v as i32);})
    }
    fn emit_i64(&mut self, v: i64) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_long(&mut *self.data, v);})
    }
    fn emit_i32(&mut self, v: i32) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_int(&mut *self.data, v);})
    }
    fn emit_i16(&mut self, v: i16) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_short(&mut *self.data, v);})
    }
    fn emit_i8(&mut self, v: i8) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_byte(&mut *self.data, v);})
    }

    fn emit_bool(&mut self, v: bool) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_bool(&mut *self.data, v as u8);})
    }

    fn emit_f64(&mut self, v: f64) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_float(&mut *self.data, v as f32);})
    }
    fn emit_f32(&mut self, v: f32) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_float(&mut *self.data, v);})
    }

    fn emit_char(&mut self, v: char) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_char(&mut *self.data, v as u32);})
    }

    fn emit_str(&mut self, slice: &str) -> EncodeResult {
        Ok(unsafe{
            let s = CString::new(slice).unwrap();
            let bytes = proton_sys::pn_bytes(slice.len() as proton_sys::size_t, s.as_ptr());
            proton_sys::pn_data_put_string(&mut *self.data, bytes);})
    }

    fn emit_enum<F>(&mut self, _name: &str, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }

    fn emit_enum_variant<F>(&mut self,
                            name: &str,
                            _id: usize,
                            cnt: usize,
                            f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        Ok(())
    }

    fn emit_enum_variant_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }

    fn emit_enum_struct_variant<F>(&mut self,
                                   name: &str,
                                   id: usize,
                                   cnt: usize,
                                   f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_enum_variant(name, id, cnt, f)
    }

    fn emit_enum_struct_variant_field<F>(&mut self,
                                         _: &str,
                                         idx: usize,
                                         f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_enum_variant_arg(idx, f)
    }

    fn emit_struct<F>(&mut self, _: &str, _: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        Ok(())
    }

    fn emit_struct_field<F>(&mut self, name: &str, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }

    fn emit_tuple<F>(&mut self, len: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_seq(len, f)
    }
    fn emit_tuple_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_seq_elt(idx, f)
    }

    fn emit_tuple_struct<F>(&mut self, _name: &str, len: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_seq(len, f)
    }
    fn emit_tuple_struct_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_seq_elt(idx, f)
    }

    fn emit_option<F>(&mut self, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }
    fn emit_option_none(&mut self) -> EncodeResult {
        self.emit_nil()
    }
    fn emit_option_some<F>(&mut self, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }

    fn emit_seq<F>(&mut self, _len: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        Ok(())
    }

    fn emit_seq_elt<F>(&mut self, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }

    fn emit_map<F>(&mut self, _len: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        Ok(())
    }

    fn emit_map_elt_key<F>(&mut self, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        Ok(())
    }

    fn emit_map_elt_val<F>(&mut self, _idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }
}

/// Shortcut function to encode a `T` into a JSON `String`
pub fn encode<T: serialize::Encodable>(object: &T) -> Result<Vec<i8>, EncoderError> {
    let mut encoder = Encoder::new();
    try!(object.encode(&mut encoder));
    let mut size = 1024;
    let mut bytes = Vec::with_capacity(size);
    let mut result = proton_sys::PN_OVERFLOW;

    while result == proton_sys::PN_OVERFLOW {
        result = unsafe{proton_sys::pn_data_encode(encoder.data,
                                                   bytes.as_mut_ptr(),
                                                   size as u64) as i8};

        if result > 0 {
            unsafe {bytes.set_len(result as usize);}
            break;
        } else if result <= 0 {
            // not ok
            println!("ERROR");
            break;
        }

        size *= 2;
        bytes = Vec::with_capacity(size);
    }

    bytes.shrink_to_fit();
    Ok(bytes)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ffi::CStr;
    use std::slice;
    use std::str;
    use proton_sys;

    macro_rules! create_test {
        ($func:ident, $get_data:ident, $value:expr) => (
            #[test]
            fn $func() {
                let value = $value;
                let encoded = encode(&value).unwrap();
                let data;
                unsafe {
                    data = proton_sys::pn_data(1024);
                    let err = proton_sys::pn_data_decode(data,
                                                         encoded.as_ptr(),
                                                         encoded.len() as u64);
                }

                assert_eq!(1, unsafe{proton_sys::pn_data_size(data)});
                assert_eq!(format!("pn_{:}", stringify!($func)),
                           get_type_name(data).to_lowercase());

                let data_content = unsafe {
                    //concat_idents!(proton_sys::pn_data_get_, $func)(data)
                    proton_sys::$get_data(data)
                };
                assert_eq!(value, data_content);
            }
        )
    }

    fn get_type_name<'a>(data: *mut proton_sys::pn_data_t) -> &'a str {
        unsafe{
            let t = proton_sys::pn_data_type(data);
            let name = proton_sys::pn_type_name(t);
            CStr::from_ptr(name).to_str().unwrap()
        }
    }

    #[test]
    fn test_string_encoding() {
        let value = "testing";
        let encoded = encode(&value).unwrap();
        let data;
        unsafe {
            data = proton_sys::pn_data(1024);
            let err = proton_sys::pn_data_decode(data,
                                                 encoded.as_ptr(),
                                                 encoded.len() as u64);
        }

        assert_eq!(1, unsafe{proton_sys::pn_data_size(data)});
        assert_eq!("PN_STRING", get_type_name(data));

        let data_content = unsafe {
            let pn_bytes = proton_sys::pn_data_get_string(data);
            let data: &[u8] = slice::from_raw_parts(pn_bytes.start as *const u8,
                                                    pn_bytes.size as usize);
            str::from_utf8(data).unwrap()
        };
        assert_eq!(value, data_content);

    }

    create_test!(ubyte, pn_data_get_ubyte, 1u8);
    create_test!(ushort, pn_data_get_ushort, 1u16);
    create_test!(uint, pn_data_get_uint, 1u32);
    create_test!(ulong, pn_data_get_ulong, 1u64);

    create_test!(byte, pn_data_get_byte, 1i8);
    create_test!(short, pn_data_get_short, 1i16);
    create_test!(int, pn_data_get_int, 1i32);
    create_test!(long, pn_data_get_long, 1i64);
}
<commit_msg>add float<commit_after>use std::string;
use std::{char, f64, fmt, io, num, str, mem};
use std::ffi::CString;
use rustc_serialize as serialize;
use std::marker::PhantomData;
use proton_sys;

/*
Marshal encodes a Rust value as AMQP data in buffer based on its type.
If buffer is nil, or is not large enough, a new buffer  is created.

Returns the buffer used for encoding with len() adjusted to the actual size of data.

Rust types are encoded as follows

 +-------------------------------------+--------------------------------------------+
 |Rust type                            |AMQP type                                   |
 +-------------------------------------+--------------------------------------------+
 |bool                                 |bool                                        |
 +-------------------------------------+--------------------------------------------+
 |int8, int16, int32, int64 (int)      |byte, short, int, long (int or long)        |
 +-------------------------------------+--------------------------------------------+
 |uint8, uint16, uint32, uint64 (uint) |ubyte, ushort, uint, ulong (uint or ulong)  |
 +-------------------------------------+--------------------------------------------+
 |float32, float64                     |float, double.                              |
 +-------------------------------------+--------------------------------------------+
 |string                               |string                                      |
 +-------------------------------------+--------------------------------------------+
 |[]byte, Binary                       |binary                                      |
 +-------------------------------------+--------------------------------------------+
 |Symbol                               |symbol                                      |
 +-------------------------------------+--------------------------------------------+
 |interface{}                          |the contained type                          |
 +-------------------------------------+--------------------------------------------+
 |nil                                  |null                                        |
 +-------------------------------------+--------------------------------------------+
 |map[K]T                              |map with K and T converted as above         |
 +-------------------------------------+--------------------------------------------+
 |Map                                  |map, may have mixed types for keys, values  |
 +-------------------------------------+--------------------------------------------+
 |[]T                                  |list with T converted as above              |
 +-------------------------------------+--------------------------------------------+
 |List                                 |list, may have mixed types  values          |
 +-------------------------------------+--------------------------------------------+

TODO: Update the table to match rust types
TODO Rust types: array, slice, struct

Rust types that cannot be marshaled: complex64/128, uintptr, function, interface, channel
*/

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ErrorCode {
// ADD SOMETHING HERE
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum ParserError {
    /// msg, line, col
    SyntaxError(ErrorCode, usize, usize),
    IoError(io::ErrorKind, &'static str),
}

// Builder and Parser have the same errors.
pub type BuilderError = ParserError;

#[derive(Clone, PartialEq, Debug)]
pub enum DecoderError {
    ParseError(ParserError),
    ExpectedError(string::String, string::String),
    MissingFieldError(string::String),
    UnknownVariantError(string::String),
    ApplicationError(string::String)
}

#[derive(Clone, Copy, Debug)]
pub enum EncoderError {
    FmtError(fmt::Error),
    BadHashmapKey,
}

pub type EncodeResult = Result<(), EncoderError>;
pub type DecodeResult<T> = Result<T, DecoderError>;

pub struct Encoder<'e> {
    data: *mut proton_sys::pn_data_t,
    __phantom: PhantomData<&'e ()>
}

impl<'e> Encoder<'e> {
    fn new() -> Encoder<'e> {
        Encoder::with_capacity(16)
    }

    fn with_capacity(capacity: ::libc::size_t) -> Encoder<'e> {
        let data = unsafe{proton_sys::pn_data(capacity)};
        Encoder {data: data, __phantom: PhantomData}
    }
}

impl<'a> serialize::Encoder for Encoder<'a> {
    type Error = EncoderError;

    fn emit_nil(&mut self) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_null(&mut *self.data);})
    }

    fn emit_usize(&mut self, v: usize) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_uint(&mut *self.data, v as u32);})
    }
    fn emit_u64(&mut self, v: u64) -> EncodeResult {
        // NOTE(flaper87): check word size
        Ok(unsafe{proton_sys::pn_data_put_ulong(&mut *self.data, v);})
    }
    fn emit_u32(&mut self, v: u32) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_uint(&mut *self.data, v);})
    }
    fn emit_u16(&mut self, v: u16) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_ushort(&mut *self.data, v);})
    }
    fn emit_u8(&mut self, v: u8) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_ubyte(&mut *self.data, v);})
    }

    fn emit_isize(&mut self, v: isize) -> EncodeResult {
        // NOTE(flaper87): check word size
        Ok(unsafe{proton_sys::pn_data_put_int(&mut *self.data, v as i32);})
    }
    fn emit_i64(&mut self, v: i64) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_long(&mut *self.data, v);})
    }
    fn emit_i32(&mut self, v: i32) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_int(&mut *self.data, v);})
    }
    fn emit_i16(&mut self, v: i16) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_short(&mut *self.data, v);})
    }
    fn emit_i8(&mut self, v: i8) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_byte(&mut *self.data, v);})
    }

    fn emit_bool(&mut self, v: bool) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_bool(&mut *self.data, v as u8);})
    }

    fn emit_f64(&mut self, v: f64) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_float(&mut *self.data, v as f32);})
    }
    fn emit_f32(&mut self, v: f32) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_float(&mut *self.data, v);})
    }

    fn emit_char(&mut self, v: char) -> EncodeResult {
        Ok(unsafe{proton_sys::pn_data_put_char(&mut *self.data, v as u32);})
    }

    fn emit_str(&mut self, slice: &str) -> EncodeResult {
        Ok(unsafe{
            let s = CString::new(slice).unwrap();
            let bytes = proton_sys::pn_bytes(slice.len() as proton_sys::size_t, s.as_ptr());
            proton_sys::pn_data_put_string(&mut *self.data, bytes);})
    }

    fn emit_enum<F>(&mut self, _name: &str, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }

    fn emit_enum_variant<F>(&mut self,
                            name: &str,
                            _id: usize,
                            cnt: usize,
                            f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        Ok(())
    }

    fn emit_enum_variant_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }

    fn emit_enum_struct_variant<F>(&mut self,
                                   name: &str,
                                   id: usize,
                                   cnt: usize,
                                   f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_enum_variant(name, id, cnt, f)
    }

    fn emit_enum_struct_variant_field<F>(&mut self,
                                         _: &str,
                                         idx: usize,
                                         f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_enum_variant_arg(idx, f)
    }

    fn emit_struct<F>(&mut self, _: &str, _: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        Ok(())
    }

    fn emit_struct_field<F>(&mut self, name: &str, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }

    fn emit_tuple<F>(&mut self, len: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_seq(len, f)
    }
    fn emit_tuple_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_seq_elt(idx, f)
    }

    fn emit_tuple_struct<F>(&mut self, _name: &str, len: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_seq(len, f)
    }
    fn emit_tuple_struct_arg<F>(&mut self, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        self.emit_seq_elt(idx, f)
    }

    fn emit_option<F>(&mut self, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }
    fn emit_option_none(&mut self) -> EncodeResult {
        self.emit_nil()
    }
    fn emit_option_some<F>(&mut self, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }

    fn emit_seq<F>(&mut self, _len: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        Ok(())
    }

    fn emit_seq_elt<F>(&mut self, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }

    fn emit_map<F>(&mut self, _len: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        Ok(())
    }

    fn emit_map_elt_key<F>(&mut self, idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        Ok(())
    }

    fn emit_map_elt_val<F>(&mut self, _idx: usize, f: F) -> EncodeResult where
        F: FnOnce(&mut Encoder<'a>) -> EncodeResult,
    {
        f(self)
    }
}

/// Shortcut function to encode a `T` into a JSON `String`
pub fn encode<T: serialize::Encodable>(object: &T) -> Result<Vec<i8>, EncoderError> {
    let mut encoder = Encoder::new();
    try!(object.encode(&mut encoder));
    let mut size = 1024;
    let mut bytes = Vec::with_capacity(size);
    let mut result = proton_sys::PN_OVERFLOW;

    while result == proton_sys::PN_OVERFLOW {
        result = unsafe{proton_sys::pn_data_encode(encoder.data,
                                                   bytes.as_mut_ptr(),
                                                   size as u64) as i8};

        if result > 0 {
            unsafe {bytes.set_len(result as usize);}
            break;
        } else if result <= 0 {
            // not ok
            println!("ERROR");
            break;
        }

        size *= 2;
        bytes = Vec::with_capacity(size);
    }

    bytes.shrink_to_fit();
    Ok(bytes)
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ffi::CStr;
    use std::slice;
    use std::str;
    use proton_sys;

    macro_rules! create_test {
        ($func:ident, $get_data:ident, $value:expr) => (
            #[test]
            fn $func() {
                let value = $value;
                let encoded = encode(&value).unwrap();
                let data;
                unsafe {
                    data = proton_sys::pn_data(1024);
                    let err = proton_sys::pn_data_decode(data,
                                                         encoded.as_ptr(),
                                                         encoded.len() as u64);
                }

                assert_eq!(1, unsafe{proton_sys::pn_data_size(data)});
                assert_eq!(format!("pn_{:}", stringify!($func)),
                           get_type_name(data).to_lowercase());

                let data_content = unsafe {
                    //concat_idents!(proton_sys::pn_data_get_, $func)(data)
                    proton_sys::$get_data(data)
                };
                assert_eq!(value, data_content);
            }
        )
    }

    fn get_type_name<'a>(data: *mut proton_sys::pn_data_t) -> &'a str {
        unsafe{
            let t = proton_sys::pn_data_type(data);
            let name = proton_sys::pn_type_name(t);
            CStr::from_ptr(name).to_str().unwrap()
        }
    }

    #[test]
    fn test_string_encoding() {
        let value = "testing";
        let encoded = encode(&value).unwrap();
        let data;
        unsafe {
            data = proton_sys::pn_data(1024);
            let err = proton_sys::pn_data_decode(data,
                                                 encoded.as_ptr(),
                                                 encoded.len() as u64);
        }

        assert_eq!(1, unsafe{proton_sys::pn_data_size(data)});
        assert_eq!("PN_STRING", get_type_name(data));

        let data_content = unsafe {
            let pn_bytes = proton_sys::pn_data_get_string(data);
            let data: &[u8] = slice::from_raw_parts(pn_bytes.start as *const u8,
                                                    pn_bytes.size as usize);
            str::from_utf8(data).unwrap()
        };
        assert_eq!(value, data_content);

    }

    create_test!(ubyte, pn_data_get_ubyte, 1u8);
    create_test!(ushort, pn_data_get_ushort, 1u16);
    create_test!(uint, pn_data_get_uint, 1u32);
    create_test!(ulong, pn_data_get_ulong, 1u64);

    create_test!(byte, pn_data_get_byte, 1i8);
    create_test!(short, pn_data_get_short, 1i16);
    create_test!(int, pn_data_get_int, 1i32);
    create_test!(long, pn_data_get_long, 1i64);

    create_test!(float, pn_data_get_float, 1f32);
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
// Copyright 2012-2014 Dustin Hiatt. See the COPYRIGHT
// file at the top-level directory.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
//! A statically-sized ring buffer that acts as an MPMC queue.  Differs
//! from the ring buffer-backed VecDeque in that this structure
//! is lockless and threadsafe.  Threadsafety is achieved using only
//! CAS operations, making this queue ideal for high throughput.  This
//! queue is not ideal for situations where a long wait is expected
//! as any getter is blocked in a spinlock until an item is put.  Locks
//! due to resource starvation are avoided by yielding the spin lock to
//! the scheduler every few thousand iterations.  This queue is cache-aware
//! and performs best with cache line sizes of 64 bytes.
//!
//! Because this queue is threadsafe, it is safe to pass an Arc around
//! to spawned threads and call put, without wrapping the struct itself
//! in a mutex.

//! Benchmarks:
//! test ringbuffer::rbtest::bench_rb_batch     ... bench:    443876 ns/iter (+/- 19276)
//! ringbuffer::rbtest::bench_rb_get       ... bench:        42 ns/iter (+/- 2)
//! ringbuffer::rbtest::bench_rb_lifecycle ... bench:        24 ns/iter (+/- 0)
//! ringbuffer::rbtest::bench_rb_put       ... bench:        36 ns/iter (+/- 5)
//! ringbuffer::rbtest::bench_vecdeque     ... bench:      2934 ns/iter (+/- 394)
//!
//! The final benchmark is comparable to the bench_rb_lifecycle in terms
//! of functionality.

/// #Examples
///
/// ```
/// use marble::ringbuffer::RingBuffer;
///
/// use std::sync::Arc;
/// use std::thread;
///
/// let rb = Arc::new(RingBuffer::new(1));
///
/// let rbc = rb.clone();
/// let join = thread::spawn(move || {
/// 	loop {
/// 		let result = rbc.get();
/// 		match result {
/// 			Err(_) => break,
/// 			_ => ()
///			}
///		}
/// });
///
/// rb.put(1);
/// rb.dispose();
/// join.join();
/// ```

use std::cell::UnsafeCell;
use std::default::Default;
use std::marker;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
use std::thread::yield_now;
use std::vec::Vec;

// Node will wrap items in the buffer and keep track of enough
// meta data to know if queue and dequere are referencing the
// correct item.  This is achieved through the atomic position
// field.
struct Node<T> {
	item: 	  Option<T>,
	position: AtomicUsize,
}

impl<T> Node<T> {
	// new creates a new node with its atomic position set to
	// position.  No item by default.
	fn new(position: usize) -> Node<T> {
		Node {
			position: AtomicUsize::new(position),
			item: None,
		}
	}
}

/// RingBuffer is a threadsafe MPMC queue that's statically-sized.
/// Puts are blocked on a full queue and gets are blocked on an empty
/// queue.  In either case, calling dispose will free any blocked threads
/// and return an error.
pub struct RingBuffer<T> {
	queue: 	   AtomicUsize,
	// padding is here to ensure that queue and dequeue end on different
	// cache lines to prevent false sharing.
	_padding0: [u64;8],
	dequeue:   AtomicUsize,
	_padding1: [u64;8],
	disposed:  AtomicUsize,
	_padding2: [u64;8],
	mask:      usize,
	// positions is unsafecell so we can grab a node mutably in the
	// put and get functions, which themselves can be called on an
	// immutable ring buffer.
	positions: UnsafeCell<Vec<Node<T>>>,
}

// these implementations are required to access a ringbuffer in a separate
// lifetime and thread using only a wrapping Arc.
unsafe impl<T> marker::Send for RingBuffer<T> {}
unsafe impl<T> marker::Sync for RingBuffer<T> {}

impl<T> Default for RingBuffer<T> {
	// this is convenient so we don't have to create 0 for atomics
	// and empty arrays with every constructor call.
	fn default() -> RingBuffer<T> {
		RingBuffer{
			queue:     ATOMIC_USIZE_INIT,
			_padding0: [0;8],
			dequeue:   ATOMIC_USIZE_INIT,
			_padding1: [0;8],
			disposed:  ATOMIC_USIZE_INIT,
			_padding2: [0;8],
			mask: 	   0,
			positions: UnsafeCell::new(vec![]),
		}
	}
}

/// RingBufferError is returned when a thread gets or puts on a disposed
/// RingBuffer.  Any blocked threads will be freed and will received this
/// error.
pub enum RingBufferError { Disposed }

impl<T> RingBuffer<T> {
	/// new creates a new RingBuffer with the specified capacity.  It
	/// is important to note that capacity will be rounded up to the next
	/// power of two.  This is done to improve performance by avoiding a
	/// costly modulo call.  To get the actual capacity of the ring buffer
	/// call cap().
	pub fn new(cap: usize) -> RingBuffer<T> {
		let calculated_capacity = cap.next_power_of_two();
		let mut positions = Vec::<Node<T>>::with_capacity(calculated_capacity);

		for i in 0..calculated_capacity {
			positions.push(Node::new(i as usize));
		}

		RingBuffer{
			mask: calculated_capacity-1,
			positions: UnsafeCell::new(positions),
			..Default::default()
		}
	}

	/// cap returns the actual capacity of the ring buffer.  This
	/// different than length, which returns the actual number of
	/// items in the ring buffer.  If cap == len, then the ring
	/// buffer is full.
	pub fn cap(&self) -> usize {
		unsafe {
			let positions = self.positions.get();
			(*positions).len()
		}
	}

	/// len returns the number of items in the ring buffer at this
	/// moment.
	pub fn len(&self) -> usize {
		self.queue.load(Ordering::Relaxed) - self.dequeue.load(Ordering::Relaxed)
	}

	/// dispose frees any resources consumed by this ring buffer and
	/// releases any threads that are currently in spin locks.  Any subsequent
	/// calls to put or get will return an error.
	pub fn dispose(&self) {
		self.disposed.store(1, Ordering::Relaxed);
	}

	/// put will add item to the ring buffer.  If the ring buffer is full
	/// this operation blocks until it can be put.  Returns an error if
	/// this ring buffer is disposed.
	pub fn put(&self, item: T) -> Result<(), RingBufferError> {
		unsafe {
			let mut position = self.queue.load(Ordering::Relaxed);
			let mut i = 0;

			loop {
				if self.disposed.load(Ordering::Relaxed) == 1 {
					return Err(RingBufferError::Disposed);
				}

				let positions = self.positions.get();
				let n = (*positions).get_unchecked_mut(position&self.mask);
				let diff = n.position.load(Ordering::Acquire) - position;
				if diff == 0 {
					if self.queue.compare_and_swap(position, position+1, Ordering::Relaxed) == position {
						n.item = Some(item);
						n.position.store(position+1, Ordering::Release);
						return Ok(());
					}
				} else {
					position = self.queue.load(Ordering::Relaxed);
				}

				if i == 10000 {
					yield_now();
				} else {
					i += 1;
				}
			}
		}
	}

	/// get retrieves the next item from the ring buffer.  This method blocks
	/// if the ring buffer is empty until an item is placed.  Returns an error
	/// if the ring buffer is disposed.
	pub fn get(&self) -> Result<T, RingBufferError> {
		unsafe {
			let mut position = self.dequeue.load(Ordering::Relaxed);
			let mut i = 0;
			loop {
				if self.disposed.load(Ordering::SeqCst) == 1 {
					return Err(RingBufferError::Disposed);
				}
				let positions = self.positions.get();
				let n = (*positions).get_unchecked_mut(position&self.mask);
				let diff = n.position.load(Ordering::Acquire) - (position + 1);
				if diff == 0 {
					if self.dequeue.compare_and_swap(position, position+1, Ordering::Relaxed) == position {
						let data = n.item.take().unwrap();
						n.position.store(position+self.mask+1, Ordering::Release);
						return Ok(data);
					}
				} else {
					position = self.dequeue.load(Ordering::Relaxed);
				}

				if i == 10000 {
					yield_now();
				} else {
					i += 1;
				}
			}
		}
	}
}

#[cfg(test)]
#[allow(unused_must_use)]
mod rbtest {
	extern crate test;

	use self::test::Bencher;

	use std::collections::vec_deque::VecDeque;
	use std::sync::{Arc, Mutex};
	use std::sync::atomic::{AtomicUsize, Ordering};
	use std::sync::mpsc::channel;
	use std::thread;

    use super::*;

   	#[test]
	fn test_simple_put_get() {
		let rb = RingBuffer::new(10);
		let number = 5;
		rb.put(number);
		assert_eq!(1, rb.len());
		let result = rb.get();
		match result {
			Ok(x) => assert_eq!(x, 5),
			Err(x) => panic!(x)
		}
	}

	#[test]
	fn test_fill_and_empty() {
		let rb = RingBuffer::new(8);
		for i in 0..rb.cap() {
			rb.put(i);
		}

		for i in 0..rb.cap() {
			let result = rb.get();
			match result {
				Ok(x) => assert_eq!(x, i),
				Err(x) => panic!(x)
			}
		}

		assert_eq!(0, rb.len());
	}

	#[test]
	fn test_fill_and_dispose() {
		let rb = RingBuffer::new(8);
		let arb = Arc::new(rb);
		let mut vec = vec![];

		for i in 0..arb.cap()+1 {
			let trb = arb.clone();
			let join = thread::spawn(move || {
				trb.put(i);
			});

			vec.push(join);
		}

		arb.dispose();
		for j in vec {
			j.join();
		}
	}

	#[test]
	fn test_get_put_on_dispose() {
		let rb = RingBuffer::new(2);
		rb.dispose();

		let result = rb.get();
		match result {
			Ok(_) => panic!("Should return error."),
			_ => ()
		}

		let result = rb.put(3);
		match result {
			Ok(_) => panic!("Should return error."),
			_ => ()
		}
	}

	#[bench]
	fn bench_rb_put(b: &mut Bencher) {
		b.iter(|| {
			let rb = RingBuffer::new(2);
			rb.put(1);
		});
	}

	#[bench]
	fn bench_rb_get(b: &mut Bencher) {
		b.iter(|| {
			let rb = RingBuffer::new(2);
			rb.put(1);
			rb.get();
		});
	}

	#[bench]
	fn bench_rb_batch(b: &mut Bencher) {
		b.iter(|| {
			const NUM_ITEMS:usize = 1000;
			let rb = Arc::new(RingBuffer::new(NUM_ITEMS));
			let num_done = Arc::new(AtomicUsize::new(0));
			let num_sent = Arc::new(AtomicUsize::new(0));
			let (tx, rx) = channel();

			for _ in 0..8 {
				let rb = rb.clone();
				let tx = tx.clone();
				let num_done = num_done.clone();
				thread::spawn(move || {
					loop {
						let result = rb.get();

						match result {
							Ok(x) => {
								num_done.fetch_add(1, Ordering::SeqCst);
								if x == NUM_ITEMS-2 {
									tx.send(()).unwrap();
								}
							},
							_ => break
						}
					}
				});
			}

			for _ in 0..8 {
				let rb = rb.clone();
				let num_sent = num_sent.clone();
				thread::spawn(move || {
					loop {
						let previous = num_sent.fetch_add(1, Ordering::SeqCst);
						if previous >= NUM_ITEMS {
							break
						}
						rb.put(previous);
					}
				});
			}

			rx.recv().unwrap();
			rb.dispose();
		});
	}

	#[bench]
	fn bench_rb_lifecycle(b: &mut Bencher) {
		let rb = Arc::new(RingBuffer::new(1));

		let rbc = rb.clone();
		let join = thread::spawn(move || {
			loop {
				let result = rbc.get();
				match result {
					Err(_) => break,
					_ => ()
				}
			}
		});

		b.iter(|| {
			let rb = rb.clone();
			rb.put(1);
		});

		rb.dispose();
		join.join();
	}

	#[bench]
	fn bench_vecdeque(b: &mut Bencher) {
		let rb = VecDeque::new();
		let arc = Arc::new(Mutex::new(rb));

		let clone = arc.clone();
		thread::spawn(move || {
			loop {
				let mut rb = clone.lock().unwrap();
				let result = rb.pop_front();
				match result {
					Some(x) => {if x == 2 { break }},
					None => ()
				}
			}
		});

		b.iter(|| {
			let mut rb = arc.lock().unwrap();
			rb.push_back(1);
		});

		let mut rb = arc.lock().unwrap();
		rb.push_back(2);
	}
}<commit_msg>Fixed reset to zero bug.<commit_after>#![allow(dead_code)]
// Copyright 2012-2014 Dustin Hiatt. See the COPYRIGHT
// file at the top-level directory.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
//! A statically-sized ring buffer that acts as an MPMC queue.  Differs
//! from the ring buffer-backed VecDeque in that this structure
//! is lockless and threadsafe.  Threadsafety is achieved using only
//! CAS operations, making this queue ideal for high throughput.  This
//! queue is not ideal for situations where a long wait is expected
//! as any getter is blocked in a spinlock until an item is put.  Locks
//! due to resource starvation are avoided by yielding the spin lock to
//! the scheduler every few thousand iterations.  This queue is cache-aware
//! and performs best with cache line sizes of 64 bytes.
//!
//! Because this queue is threadsafe, it is safe to pass an Arc around
//! to spawned threads and call put, without wrapping the struct itself
//! in a mutex.

//! Benchmarks:
//! test ringbuffer::rbtest::bench_rb_batch     ... bench:    443876 ns/iter (+/- 19276)
//! ringbuffer::rbtest::bench_rb_get       ... bench:        42 ns/iter (+/- 2)
//! ringbuffer::rbtest::bench_rb_lifecycle ... bench:        24 ns/iter (+/- 0)
//! ringbuffer::rbtest::bench_rb_put       ... bench:        36 ns/iter (+/- 5)
//! ringbuffer::rbtest::bench_vecdeque     ... bench:      2934 ns/iter (+/- 394)
//!
//! The final benchmark is comparable to the bench_rb_lifecycle in terms
//! of functionality.

/// #Examples
///
/// ```
/// use marble::ringbuffer::RingBuffer;
///
/// use std::sync::Arc;
/// use std::thread;
///
/// let rb = Arc::new(RingBuffer::new(1));
///
/// let rbc = rb.clone();
/// let join = thread::spawn(move || {
/// 	loop {
/// 		let result = rbc.get();
/// 		match result {
/// 			Err(_) => break,
/// 			_ => ()
///			}
///		}
/// });
///
/// rb.put(1);
/// rb.dispose();
/// join.join();
/// ```

use std::cell::UnsafeCell;
use std::default::Default;
use std::marker;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
use std::thread::yield_now;
use std::vec::Vec;

// Node will wrap items in the buffer and keep track of enough
// meta data to know if queue and dequere are referencing the
// correct item.  This is achieved through the atomic position
// field.
struct Node<T> {
	item: 	  Option<T>,
	position: AtomicUsize,
}

impl<T> Node<T> {
	// new creates a new node with its atomic position set to
	// position.  No item by default.
	fn new(position: usize) -> Node<T> {
		Node {
			position: AtomicUsize::new(position),
			item: None,
		}
	}
}

/// RingBuffer is a threadsafe MPMC queue that's statically-sized.
/// Puts are blocked on a full queue and gets are blocked on an empty
/// queue.  In either case, calling dispose will free any blocked threads
/// and return an error.
pub struct RingBuffer<T> {
	queue: 	   AtomicUsize,
	// padding is here to ensure that queue and dequeue end on different
	// cache lines to prevent false sharing.
	_padding0: [u64;8],
	dequeue:   AtomicUsize,
	_padding1: [u64;8],
	disposed:  AtomicUsize,
	_padding2: [u64;8],
	mask:      usize,
	// positions is unsafecell so we can grab a node mutably in the
	// put and get functions, which themselves can be called on an
	// immutable ring buffer.
	positions: UnsafeCell<Vec<Node<T>>>,
}

// these implementations are required to access a ringbuffer in a separate
// lifetime and thread using only a wrapping Arc.
unsafe impl<T> marker::Send for RingBuffer<T> {}
unsafe impl<T> marker::Sync for RingBuffer<T> {}

impl<T> Default for RingBuffer<T> {
	// this is convenient so we don't have to create 0 for atomics
	// and empty arrays with every constructor call.
	fn default() -> RingBuffer<T> {
		RingBuffer{
			queue:     ATOMIC_USIZE_INIT,
			_padding0: [0;8],
			dequeue:   ATOMIC_USIZE_INIT,
			_padding1: [0;8],
			disposed:  ATOMIC_USIZE_INIT,
			_padding2: [0;8],
			mask: 	   0,
			positions: UnsafeCell::new(vec![]),
		}
	}
}

/// RingBufferError is returned when a thread gets or puts on a disposed
/// RingBuffer.  Any blocked threads will be freed and will received this
/// error.
pub enum RingBufferError { Disposed }

impl<T> RingBuffer<T> {
	/// new creates a new RingBuffer with the specified capacity.  It
	/// is important to note that capacity will be rounded up to the next
	/// power of two.  This is done to improve performance by avoiding a
	/// costly modulo call.  To get the actual capacity of the ring buffer
	/// call cap().
	pub fn new(cap: usize) -> RingBuffer<T> {
		let calculated_capacity = cap.next_power_of_two();
		let mut positions = Vec::<Node<T>>::with_capacity(calculated_capacity);

		for i in 0..calculated_capacity {
			positions.push(Node::new(i as usize));
		}

		RingBuffer{
			mask: calculated_capacity-1,
			positions: UnsafeCell::new(positions),
			..Default::default()
		}
	}

	/// cap returns the actual capacity of the ring buffer.  This
	/// different than length, which returns the actual number of
	/// items in the ring buffer.  If cap == len, then the ring
	/// buffer is full.
	pub fn cap(&self) -> usize {
		unsafe {
			let positions = self.positions.get();
			(*positions).len()
		}
	}

	/// len returns the number of items in the ring buffer at this
	/// moment.
	pub fn len(&self) -> usize {
		self.queue.load(Ordering::Relaxed) - self.dequeue.load(Ordering::Relaxed)
	}

	/// dispose frees any resources consumed by this ring buffer and
	/// releases any threads that are currently in spin locks.  Any subsequent
	/// calls to put or get will return an error.
	pub fn dispose(&self) {
		self.disposed.store(1, Ordering::Relaxed);
	}

	/// put will add item to the ring buffer.  If the ring buffer is full
	/// this operation blocks until it can be put.  Returns an error if
	/// this ring buffer is disposed.
	pub fn put(&self, item: T) -> Result<(), RingBufferError> {
		unsafe {
			let mut position = self.queue.load(Ordering::Relaxed);
			let mut i = 0;

			loop {
				if self.disposed.load(Ordering::Relaxed) == 1 {
					return Err(RingBufferError::Disposed);
				}

				let positions = self.positions.get();
				let n = (*positions).get_unchecked_mut(position&self.mask);
				let diff = n.position.load(Ordering::Acquire) - position;
				if diff == 0 {
					if self.queue.compare_and_swap(position, position+1, Ordering::Relaxed) == position {
						n.item = Some(item);
						n.position.store(position+1, Ordering::Release);
						return Ok(());
					}
				} else {
					position = self.queue.load(Ordering::Relaxed);
				}

				if i == 10000 {
					i = 0;
					yield_now();
				} else {
					i += 1;
				}
			}
		}
	}

	/// get retrieves the next item from the ring buffer.  This method blocks
	/// if the ring buffer is empty until an item is placed.  Returns an error
	/// if the ring buffer is disposed.
	pub fn get(&self) -> Result<T, RingBufferError> {
		unsafe {
			let mut position = self.dequeue.load(Ordering::Relaxed);
			let mut i = 0;
			loop {
				if self.disposed.load(Ordering::SeqCst) == 1 {
					return Err(RingBufferError::Disposed);
				}
				let positions = self.positions.get();
				let n = (*positions).get_unchecked_mut(position&self.mask);
				let diff = n.position.load(Ordering::Acquire) - (position + 1);
				if diff == 0 {
					if self.dequeue.compare_and_swap(position, position+1, Ordering::Relaxed) == position {
						let data = n.item.take().unwrap();
						n.position.store(position+self.mask+1, Ordering::Release);
						return Ok(data);
					}
				} else {
					position = self.dequeue.load(Ordering::Relaxed);
				}

				if i == 10000 {
					i = 0;
					yield_now();
				} else {
					i += 1;
				}
			}
		}
	}
}

#[cfg(test)]
#[allow(unused_must_use)]
mod rbtest {
	extern crate test;

	use self::test::Bencher;

	use std::collections::vec_deque::VecDeque;
	use std::sync::{Arc, Mutex};
	use std::sync::atomic::{AtomicUsize, Ordering};
	use std::sync::mpsc::channel;
	use std::thread;

    use super::*;

   	#[test]
	fn test_simple_put_get() {
		let rb = RingBuffer::new(10);
		let number = 5;
		rb.put(number);
		assert_eq!(1, rb.len());
		let result = rb.get();
		match result {
			Ok(x) => assert_eq!(x, 5),
			Err(x) => panic!(x)
		}
	}

	#[test]
	fn test_fill_and_empty() {
		let rb = RingBuffer::new(8);
		for i in 0..rb.cap() {
			rb.put(i);
		}

		for i in 0..rb.cap() {
			let result = rb.get();
			match result {
				Ok(x) => assert_eq!(x, i),
				Err(x) => panic!(x)
			}
		}

		assert_eq!(0, rb.len());
	}

	#[test]
	fn test_fill_and_dispose() {
		let rb = RingBuffer::new(8);
		let arb = Arc::new(rb);
		let mut vec = vec![];

		for i in 0..arb.cap()+1 {
			let trb = arb.clone();
			let join = thread::spawn(move || {
				trb.put(i);
			});

			vec.push(join);
		}

		arb.dispose();
		for j in vec {
			j.join();
		}
	}

	#[test]
	fn test_get_put_on_dispose() {
		let rb = RingBuffer::new(2);
		rb.dispose();

		let result = rb.get();
		match result {
			Ok(_) => panic!("Should return error."),
			_ => ()
		}

		let result = rb.put(3);
		match result {
			Ok(_) => panic!("Should return error."),
			_ => ()
		}
	}

	#[bench]
	fn bench_rb_put(b: &mut Bencher) {
		b.iter(|| {
			let rb = RingBuffer::new(2);
			rb.put(1);
		});
	}

	#[bench]
	fn bench_rb_get(b: &mut Bencher) {
		b.iter(|| {
			let rb = RingBuffer::new(2);
			rb.put(1);
			rb.get();
		});
	}

	#[bench]
	fn bench_rb_batch(b: &mut Bencher) {
		b.iter(|| {
			const NUM_ITEMS:usize = 1000;
			let rb = Arc::new(RingBuffer::new(NUM_ITEMS));
			let num_done = Arc::new(AtomicUsize::new(0));
			let num_sent = Arc::new(AtomicUsize::new(0));
			let (tx, rx) = channel();

			for _ in 0..8 {
				let rb = rb.clone();
				let tx = tx.clone();
				let num_done = num_done.clone();
				thread::spawn(move || {
					loop {
						let result = rb.get();

						match result {
							Ok(x) => {
								num_done.fetch_add(1, Ordering::SeqCst);
								if x == NUM_ITEMS-2 {
									tx.send(()).unwrap();
								}
							},
							_ => break
						}
					}
				});
			}

			for _ in 0..8 {
				let rb = rb.clone();
				let num_sent = num_sent.clone();
				thread::spawn(move || {
					loop {
						let previous = num_sent.fetch_add(1, Ordering::SeqCst);
						if previous >= NUM_ITEMS {
							break
						}
						rb.put(previous);
					}
				});
			}

			rx.recv().unwrap();
			rb.dispose();
		});
	}

	#[bench]
	fn bench_rb_lifecycle(b: &mut Bencher) {
		let rb = Arc::new(RingBuffer::new(1));

		let rbc = rb.clone();
		let join = thread::spawn(move || {
			loop {
				let result = rbc.get();
				match result {
					Err(_) => break,
					_ => ()
				}
			}
		});

		b.iter(|| {
			let rb = rb.clone();
			rb.put(1);
		});

		rb.dispose();
		join.join();
	}

	#[bench]
	fn bench_vecdeque(b: &mut Bencher) {
		let rb = VecDeque::new();
		let arc = Arc::new(Mutex::new(rb));

		let clone = arc.clone();
		thread::spawn(move || {
			loop {
				let mut rb = clone.lock().unwrap();
				let result = rb.pop_front();
				match result {
					Some(x) => {if x == 2 { break }},
					None => ()
				}
			}
		});

		b.iter(|| {
			let mut rb = arc.lock().unwrap();
			rb.push_back(1);
		});

		let mut rb = arc.lock().unwrap();
		rb.push_back(2);
	}
}<|endoftext|>


<commit_before>use getopts::Matches;

/// Abstraction for getopts
pub trait CommandLineOpts {
    /// returns all commandline parameters which do not belong to an option.
    fn inputs(&self) -> Vec<String>;
    /// tests if any of the specified options is present.
    fn opts_present(&self, &[&str]) -> bool;
}

/// Implementation for `getopts`
impl CommandLineOpts for Matches {
    fn inputs(&self) -> Vec<String> {
        self.free.clone()
    }
    fn opts_present(&self, opts: &[&str]) -> bool {
        self.opts_present(&opts.iter().map(|s| (*s).to_string()).collect::<Vec<_>>())
    }
}

/// Contains the Input filename(s) with an optional offset.
///
/// `FileNames` is used for one or more file inputs ("-" = stdin)
/// `FileAndOffset` is used for a single file input, with an offset
/// and an optional label. Offset and label are specified in bytes.
/// `FileAndOffset` will be only used if an offset is specified,
/// but it might be 0.
#[derive(PartialEq, Debug)]
pub enum CommandLineInputs {
    FileNames(Vec<String>),
    FileAndOffset((String, usize, Option<usize>)),
}

/// Interprets the commandline inputs of od.
///
/// Returns either an unspecified number of filenames.
/// Or it will return a single filename, with an offset and optional label.
/// Offset and label are specified in bytes.
/// '-' is used as filename if stdin is meant. This is also returned if
/// there is no input, as stdin is the default input.
pub fn parse_inputs(matches: &dyn CommandLineOpts) -> Result<CommandLineInputs, String> {
    let mut input_strings: Vec<String> = matches.inputs();

    if matches.opts_present(&["traditional"]) {
        return parse_inputs_traditional(input_strings);
    }

    // test if commandline contains: [file] <offset>
    // fall-through if no (valid) offset is found
    if input_strings.len() == 1 || input_strings.len() == 2 {
        // if any of the options -A, -j, -N, -t, -v or -w are present there is no offset
        if !matches.opts_present(&["A", "j", "N", "t", "v", "w"]) {
            // test if the last input can be parsed as an offset.
            let offset = parse_offset_operand(&input_strings[input_strings.len() - 1]);
            if let Ok(n) = offset {
                // if there is just 1 input (stdin), an offset must start with '+'
                if input_strings.len() == 1 && input_strings[0].starts_with('+') {
                    return Ok(CommandLineInputs::FileAndOffset(("-".to_string(), n, None)));
                }
                if input_strings.len() == 2 {
                    return Ok(CommandLineInputs::FileAndOffset((
                        input_strings[0].clone(),
                        n,
                        None,
                    )));
                }
            }
        }
    }

    if input_strings.is_empty() {
        input_strings.push("-".to_string());
    }
    Ok(CommandLineInputs::FileNames(input_strings))
}

/// interprets inputs when --traditional is on the commandline
///
/// normally returns CommandLineInputs::FileAndOffset, but if no offset is found,
/// it returns CommandLineInputs::FileNames (also to differentiate from the offset == 0)
pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLineInputs, String> {
    match input_strings.len() {
        0 => Ok(CommandLineInputs::FileNames(vec!["-".to_string()])),
        1 => {
            let offset0 = parse_offset_operand(&input_strings[0]);
            Ok(match offset0 {
                Ok(n) => CommandLineInputs::FileAndOffset(("-".to_string(), n, None)),
                _ => CommandLineInputs::FileNames(input_strings),
            })
        }
        2 => {
            let offset0 = parse_offset_operand(&input_strings[0]);
            let offset1 = parse_offset_operand(&input_strings[1]);
            match (offset0, offset1) {
                (Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
                    "-".to_string(),
                    n,
                    Some(m),
                ))),
                (_, Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
                    input_strings[0].clone(),
                    m,
                    None,
                ))),
                _ => Err(format!("invalid offset: {}", input_strings[1])),
            }
        }
        3 => {
            let offset = parse_offset_operand(&input_strings[1]);
            let label = parse_offset_operand(&input_strings[2]);
            match (offset, label) {
                (Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
                    input_strings[0].clone(),
                    n,
                    Some(m),
                ))),
                (Err(_), _) => Err(format!("invalid offset: {}", input_strings[1])),
                (_, Err(_)) => Err(format!("invalid label: {}", input_strings[2])),
            }
        }
        _ => Err(format!(
            "too many inputs after --traditional: {}",
            input_strings[3]
        )),
    }
}

/// parses format used by offset and label on the commandline
pub fn parse_offset_operand(s: &str) -> Result<usize, &'static str> {
    let mut start = 0;
    let mut len = s.len();
    let mut radix = 8;
    let mut multiply = 1;

    if s.starts_with('+') {
        start += 1;
    }

    if s[start..len].starts_with("0x") || s[start..len].starts_with("0X") {
        start += 2;
        radix = 16;
    } else {
        if s[start..len].ends_with('b') {
            len -= 1;
            multiply = 512;
        }
        if s[start..len].ends_with('.') {
            len -= 1;
            radix = 10;
        }
    }
    match usize::from_str_radix(&s[start..len], radix) {
        Ok(i) => Ok(i * multiply),
        Err(_) => Err("parse failed"),
    }
}

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

    /// A mock for the commandline options type
    ///
    /// `inputs` are all commandline parameters which do not belong to an option.
    /// `option_names` are the names of the options on the commandline.
    struct MockOptions<'a> {
        inputs: Vec<String>,
        option_names: Vec<&'a str>,
    }

    impl<'a> MockOptions<'a> {
        fn new(inputs: Vec<&'a str>, option_names: Vec<&'a str>) -> MockOptions<'a> {
            MockOptions {
                inputs: inputs.iter().map(|s| s.to_string()).collect::<Vec<_>>(),
                option_names,
            }
        }
    }

    impl<'a> CommandLineOpts for MockOptions<'a> {
        fn inputs(&self) -> Vec<String> {
            self.inputs.clone()
        }
        fn opts_present(&self, opts: &[&str]) -> bool {
            for expected in opts.iter() {
                for actual in self.option_names.iter() {
                    if *expected == *actual {
                        return true;
                    }
                }
            }
            false
        }
    }

    #[test]
    fn test_parse_inputs_normal() {
        assert_eq!(
            CommandLineInputs::FileNames(vec!["-".to_string()]),
            parse_inputs(&MockOptions::new(vec![], vec![])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["-".to_string()]),
            parse_inputs(&MockOptions::new(vec!["-"], vec![])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["file1".to_string()]),
            parse_inputs(&MockOptions::new(vec!["file1"], vec![])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["file1".to_string(), "file2".to_string()]),
            parse_inputs(&MockOptions::new(vec!["file1", "file2"], vec![])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec![
                "-".to_string(),
                "file1".to_string(),
                "file2".to_string(),
            ]),
            parse_inputs(&MockOptions::new(vec!["-", "file1", "file2"], vec![])).unwrap()
        );
    }

    #[test]
    fn test_parse_inputs_with_offset() {
        // offset is found without filename, so stdin will be used.
        assert_eq!(
            CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
            parse_inputs(&MockOptions::new(vec!["+10"], vec![])).unwrap()
        );

        // offset must start with "+" if no input is specified.
        assert_eq!(
            CommandLineInputs::FileNames(vec!["10".to_string()]),
            parse_inputs(&MockOptions::new(vec!["10"], vec![""])).unwrap()
        );

        // offset is not valid, so it is considered a filename.
        assert_eq!(
            CommandLineInputs::FileNames(vec!["+10a".to_string()]),
            parse_inputs(&MockOptions::new(vec!["+10a"], vec![""])).unwrap()
        );

        // if -j is included in the commandline, there cannot be an offset.
        assert_eq!(
            CommandLineInputs::FileNames(vec!["+10".to_string()]),
            parse_inputs(&MockOptions::new(vec!["+10"], vec!["j"])).unwrap()
        );

        // if -v is included in the commandline, there cannot be an offset.
        assert_eq!(
            CommandLineInputs::FileNames(vec!["+10".to_string()]),
            parse_inputs(&MockOptions::new(vec!["+10"], vec!["o", "v"])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
            parse_inputs(&MockOptions::new(vec!["file1", "+10"], vec![])).unwrap()
        );

        // offset does not need to start with "+" if a filename is included.
        assert_eq!(
            CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
            parse_inputs(&MockOptions::new(vec!["file1", "10"], vec![])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["file1".to_string(), "+10a".to_string()]),
            parse_inputs(&MockOptions::new(vec!["file1", "+10a"], vec![""])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["file1".to_string(), "+10".to_string()]),
            parse_inputs(&MockOptions::new(vec!["file1", "+10"], vec!["j"])).unwrap()
        );

        // offset must be last on the commandline
        assert_eq!(
            CommandLineInputs::FileNames(vec!["+10".to_string(), "file1".to_string()]),
            parse_inputs(&MockOptions::new(vec!["+10", "file1"], vec![""])).unwrap()
        );
    }

    #[test]
    fn test_parse_inputs_traditional() {
        // it should not return FileAndOffset to signal no offset was entered on the commandline.
        assert_eq!(
            CommandLineInputs::FileNames(vec!["-".to_string()]),
            parse_inputs(&MockOptions::new(vec![], vec!["traditional"])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["file1".to_string()]),
            parse_inputs(&MockOptions::new(vec!["file1"], vec!["traditional"])).unwrap()
        );

        // offset does not need to start with a +
        assert_eq!(
            CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
            parse_inputs(&MockOptions::new(vec!["10"], vec!["traditional"])).unwrap()
        );

        // valid offset and valid label
        assert_eq!(
            CommandLineInputs::FileAndOffset(("-".to_string(), 8, Some(8))),
            parse_inputs(&MockOptions::new(vec!["10", "10"], vec!["traditional"])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
            parse_inputs(&MockOptions::new(vec!["file1", "10"], vec!["traditional"])).unwrap()
        );

        // only one file is allowed, it must be the first
        parse_inputs(&MockOptions::new(vec!["10", "file1"], vec!["traditional"])).unwrap_err();

        assert_eq!(
            CommandLineInputs::FileAndOffset(("file1".to_string(), 8, Some(8))),
            parse_inputs(&MockOptions::new(
                vec!["file1", "10", "10"],
                vec!["traditional"]
            ))
            .unwrap()
        );

        parse_inputs(&MockOptions::new(
            vec!["10", "file1", "10"],
            vec!["traditional"],
        ))
        .unwrap_err();

        parse_inputs(&MockOptions::new(
            vec!["10", "10", "file1"],
            vec!["traditional"],
        ))
        .unwrap_err();

        parse_inputs(&MockOptions::new(
            vec!["10", "10", "10", "10"],
            vec!["traditional"],
        ))
        .unwrap_err();
    }

    fn parse_offset_operand_str(s: &str) -> Result<usize, &'static str> {
        parse_offset_operand(&String::from(s))
    }

    #[test]
    fn test_parse_offset_operand_invalid() {
        parse_offset_operand_str("").unwrap_err();
        parse_offset_operand_str("a").unwrap_err();
        parse_offset_operand_str("+").unwrap_err();
        parse_offset_operand_str("+b").unwrap_err();
        parse_offset_operand_str("0x1.").unwrap_err();
        parse_offset_operand_str("0x1.b").unwrap_err();
        parse_offset_operand_str("-").unwrap_err();
        parse_offset_operand_str("-1").unwrap_err();
        parse_offset_operand_str("1e10").unwrap_err();
    }

    #[test]
    fn test_parse_offset_operand() {
        assert_eq!(8, parse_offset_operand_str("10").unwrap()); // default octal
        assert_eq!(0, parse_offset_operand_str("0").unwrap());
        assert_eq!(8, parse_offset_operand_str("+10").unwrap()); // optional leading '+'
        assert_eq!(16, parse_offset_operand_str("0x10").unwrap()); // hex
        assert_eq!(16, parse_offset_operand_str("0X10").unwrap()); // hex
        assert_eq!(16, parse_offset_operand_str("+0X10").unwrap()); // hex
        assert_eq!(10, parse_offset_operand_str("10.").unwrap()); // decimal
        assert_eq!(10, parse_offset_operand_str("+10.").unwrap()); // decimal
        assert_eq!(4096, parse_offset_operand_str("10b").unwrap()); // b suffix = *512
        assert_eq!(4096, parse_offset_operand_str("+10b").unwrap()); // b suffix = *512
        assert_eq!(5120, parse_offset_operand_str("10.b").unwrap()); // b suffix = *512
        assert_eq!(5120, parse_offset_operand_str("+10.b").unwrap()); // b suffix = *512
        assert_eq!(267, parse_offset_operand_str("0x10b").unwrap()); // hex
    }
}
<commit_msg>fix/od ~ fix 'edition=2018' trait specification error<commit_after>use getopts::Matches;

/// Abstraction for getopts
pub trait CommandLineOpts {
    /// returns all commandline parameters which do not belong to an option.
    fn inputs(&self) -> Vec<String>;
    /// tests if any of the specified options is present.
    fn opts_present(&self, _: &[&str]) -> bool;
}

/// Implementation for `getopts`
impl CommandLineOpts for Matches {
    fn inputs(&self) -> Vec<String> {
        self.free.clone()
    }
    fn opts_present(&self, opts: &[&str]) -> bool {
        self.opts_present(&opts.iter().map(|s| (*s).to_string()).collect::<Vec<_>>())
    }
}

/// Contains the Input filename(s) with an optional offset.
///
/// `FileNames` is used for one or more file inputs ("-" = stdin)
/// `FileAndOffset` is used for a single file input, with an offset
/// and an optional label. Offset and label are specified in bytes.
/// `FileAndOffset` will be only used if an offset is specified,
/// but it might be 0.
#[derive(PartialEq, Debug)]
pub enum CommandLineInputs {
    FileNames(Vec<String>),
    FileAndOffset((String, usize, Option<usize>)),
}

/// Interprets the commandline inputs of od.
///
/// Returns either an unspecified number of filenames.
/// Or it will return a single filename, with an offset and optional label.
/// Offset and label are specified in bytes.
/// '-' is used as filename if stdin is meant. This is also returned if
/// there is no input, as stdin is the default input.
pub fn parse_inputs(matches: &dyn CommandLineOpts) -> Result<CommandLineInputs, String> {
    let mut input_strings: Vec<String> = matches.inputs();

    if matches.opts_present(&["traditional"]) {
        return parse_inputs_traditional(input_strings);
    }

    // test if commandline contains: [file] <offset>
    // fall-through if no (valid) offset is found
    if input_strings.len() == 1 || input_strings.len() == 2 {
        // if any of the options -A, -j, -N, -t, -v or -w are present there is no offset
        if !matches.opts_present(&["A", "j", "N", "t", "v", "w"]) {
            // test if the last input can be parsed as an offset.
            let offset = parse_offset_operand(&input_strings[input_strings.len() - 1]);
            if let Ok(n) = offset {
                // if there is just 1 input (stdin), an offset must start with '+'
                if input_strings.len() == 1 && input_strings[0].starts_with('+') {
                    return Ok(CommandLineInputs::FileAndOffset(("-".to_string(), n, None)));
                }
                if input_strings.len() == 2 {
                    return Ok(CommandLineInputs::FileAndOffset((
                        input_strings[0].clone(),
                        n,
                        None,
                    )));
                }
            }
        }
    }

    if input_strings.is_empty() {
        input_strings.push("-".to_string());
    }
    Ok(CommandLineInputs::FileNames(input_strings))
}

/// interprets inputs when --traditional is on the commandline
///
/// normally returns CommandLineInputs::FileAndOffset, but if no offset is found,
/// it returns CommandLineInputs::FileNames (also to differentiate from the offset == 0)
pub fn parse_inputs_traditional(input_strings: Vec<String>) -> Result<CommandLineInputs, String> {
    match input_strings.len() {
        0 => Ok(CommandLineInputs::FileNames(vec!["-".to_string()])),
        1 => {
            let offset0 = parse_offset_operand(&input_strings[0]);
            Ok(match offset0 {
                Ok(n) => CommandLineInputs::FileAndOffset(("-".to_string(), n, None)),
                _ => CommandLineInputs::FileNames(input_strings),
            })
        }
        2 => {
            let offset0 = parse_offset_operand(&input_strings[0]);
            let offset1 = parse_offset_operand(&input_strings[1]);
            match (offset0, offset1) {
                (Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
                    "-".to_string(),
                    n,
                    Some(m),
                ))),
                (_, Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
                    input_strings[0].clone(),
                    m,
                    None,
                ))),
                _ => Err(format!("invalid offset: {}", input_strings[1])),
            }
        }
        3 => {
            let offset = parse_offset_operand(&input_strings[1]);
            let label = parse_offset_operand(&input_strings[2]);
            match (offset, label) {
                (Ok(n), Ok(m)) => Ok(CommandLineInputs::FileAndOffset((
                    input_strings[0].clone(),
                    n,
                    Some(m),
                ))),
                (Err(_), _) => Err(format!("invalid offset: {}", input_strings[1])),
                (_, Err(_)) => Err(format!("invalid label: {}", input_strings[2])),
            }
        }
        _ => Err(format!(
            "too many inputs after --traditional: {}",
            input_strings[3]
        )),
    }
}

/// parses format used by offset and label on the commandline
pub fn parse_offset_operand(s: &str) -> Result<usize, &'static str> {
    let mut start = 0;
    let mut len = s.len();
    let mut radix = 8;
    let mut multiply = 1;

    if s.starts_with('+') {
        start += 1;
    }

    if s[start..len].starts_with("0x") || s[start..len].starts_with("0X") {
        start += 2;
        radix = 16;
    } else {
        if s[start..len].ends_with('b') {
            len -= 1;
            multiply = 512;
        }
        if s[start..len].ends_with('.') {
            len -= 1;
            radix = 10;
        }
    }
    match usize::from_str_radix(&s[start..len], radix) {
        Ok(i) => Ok(i * multiply),
        Err(_) => Err("parse failed"),
    }
}

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

    /// A mock for the commandline options type
    ///
    /// `inputs` are all commandline parameters which do not belong to an option.
    /// `option_names` are the names of the options on the commandline.
    struct MockOptions<'a> {
        inputs: Vec<String>,
        option_names: Vec<&'a str>,
    }

    impl<'a> MockOptions<'a> {
        fn new(inputs: Vec<&'a str>, option_names: Vec<&'a str>) -> MockOptions<'a> {
            MockOptions {
                inputs: inputs.iter().map(|s| s.to_string()).collect::<Vec<_>>(),
                option_names,
            }
        }
    }

    impl<'a> CommandLineOpts for MockOptions<'a> {
        fn inputs(&self) -> Vec<String> {
            self.inputs.clone()
        }
        fn opts_present(&self, opts: &[&str]) -> bool {
            for expected in opts.iter() {
                for actual in self.option_names.iter() {
                    if *expected == *actual {
                        return true;
                    }
                }
            }
            false
        }
    }

    #[test]
    fn test_parse_inputs_normal() {
        assert_eq!(
            CommandLineInputs::FileNames(vec!["-".to_string()]),
            parse_inputs(&MockOptions::new(vec![], vec![])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["-".to_string()]),
            parse_inputs(&MockOptions::new(vec!["-"], vec![])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["file1".to_string()]),
            parse_inputs(&MockOptions::new(vec!["file1"], vec![])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["file1".to_string(), "file2".to_string()]),
            parse_inputs(&MockOptions::new(vec!["file1", "file2"], vec![])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec![
                "-".to_string(),
                "file1".to_string(),
                "file2".to_string(),
            ]),
            parse_inputs(&MockOptions::new(vec!["-", "file1", "file2"], vec![])).unwrap()
        );
    }

    #[test]
    fn test_parse_inputs_with_offset() {
        // offset is found without filename, so stdin will be used.
        assert_eq!(
            CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
            parse_inputs(&MockOptions::new(vec!["+10"], vec![])).unwrap()
        );

        // offset must start with "+" if no input is specified.
        assert_eq!(
            CommandLineInputs::FileNames(vec!["10".to_string()]),
            parse_inputs(&MockOptions::new(vec!["10"], vec![""])).unwrap()
        );

        // offset is not valid, so it is considered a filename.
        assert_eq!(
            CommandLineInputs::FileNames(vec!["+10a".to_string()]),
            parse_inputs(&MockOptions::new(vec!["+10a"], vec![""])).unwrap()
        );

        // if -j is included in the commandline, there cannot be an offset.
        assert_eq!(
            CommandLineInputs::FileNames(vec!["+10".to_string()]),
            parse_inputs(&MockOptions::new(vec!["+10"], vec!["j"])).unwrap()
        );

        // if -v is included in the commandline, there cannot be an offset.
        assert_eq!(
            CommandLineInputs::FileNames(vec!["+10".to_string()]),
            parse_inputs(&MockOptions::new(vec!["+10"], vec!["o", "v"])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
            parse_inputs(&MockOptions::new(vec!["file1", "+10"], vec![])).unwrap()
        );

        // offset does not need to start with "+" if a filename is included.
        assert_eq!(
            CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
            parse_inputs(&MockOptions::new(vec!["file1", "10"], vec![])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["file1".to_string(), "+10a".to_string()]),
            parse_inputs(&MockOptions::new(vec!["file1", "+10a"], vec![""])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["file1".to_string(), "+10".to_string()]),
            parse_inputs(&MockOptions::new(vec!["file1", "+10"], vec!["j"])).unwrap()
        );

        // offset must be last on the commandline
        assert_eq!(
            CommandLineInputs::FileNames(vec!["+10".to_string(), "file1".to_string()]),
            parse_inputs(&MockOptions::new(vec!["+10", "file1"], vec![""])).unwrap()
        );
    }

    #[test]
    fn test_parse_inputs_traditional() {
        // it should not return FileAndOffset to signal no offset was entered on the commandline.
        assert_eq!(
            CommandLineInputs::FileNames(vec!["-".to_string()]),
            parse_inputs(&MockOptions::new(vec![], vec!["traditional"])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileNames(vec!["file1".to_string()]),
            parse_inputs(&MockOptions::new(vec!["file1"], vec!["traditional"])).unwrap()
        );

        // offset does not need to start with a +
        assert_eq!(
            CommandLineInputs::FileAndOffset(("-".to_string(), 8, None)),
            parse_inputs(&MockOptions::new(vec!["10"], vec!["traditional"])).unwrap()
        );

        // valid offset and valid label
        assert_eq!(
            CommandLineInputs::FileAndOffset(("-".to_string(), 8, Some(8))),
            parse_inputs(&MockOptions::new(vec!["10", "10"], vec!["traditional"])).unwrap()
        );

        assert_eq!(
            CommandLineInputs::FileAndOffset(("file1".to_string(), 8, None)),
            parse_inputs(&MockOptions::new(vec!["file1", "10"], vec!["traditional"])).unwrap()
        );

        // only one file is allowed, it must be the first
        parse_inputs(&MockOptions::new(vec!["10", "file1"], vec!["traditional"])).unwrap_err();

        assert_eq!(
            CommandLineInputs::FileAndOffset(("file1".to_string(), 8, Some(8))),
            parse_inputs(&MockOptions::new(
                vec!["file1", "10", "10"],
                vec!["traditional"]
            ))
            .unwrap()
        );

        parse_inputs(&MockOptions::new(
            vec!["10", "file1", "10"],
            vec!["traditional"],
        ))
        .unwrap_err();

        parse_inputs(&MockOptions::new(
            vec!["10", "10", "file1"],
            vec!["traditional"],
        ))
        .unwrap_err();

        parse_inputs(&MockOptions::new(
            vec!["10", "10", "10", "10"],
            vec!["traditional"],
        ))
        .unwrap_err();
    }

    fn parse_offset_operand_str(s: &str) -> Result<usize, &'static str> {
        parse_offset_operand(&String::from(s))
    }

    #[test]
    fn test_parse_offset_operand_invalid() {
        parse_offset_operand_str("").unwrap_err();
        parse_offset_operand_str("a").unwrap_err();
        parse_offset_operand_str("+").unwrap_err();
        parse_offset_operand_str("+b").unwrap_err();
        parse_offset_operand_str("0x1.").unwrap_err();
        parse_offset_operand_str("0x1.b").unwrap_err();
        parse_offset_operand_str("-").unwrap_err();
        parse_offset_operand_str("-1").unwrap_err();
        parse_offset_operand_str("1e10").unwrap_err();
    }

    #[test]
    fn test_parse_offset_operand() {
        assert_eq!(8, parse_offset_operand_str("10").unwrap()); // default octal
        assert_eq!(0, parse_offset_operand_str("0").unwrap());
        assert_eq!(8, parse_offset_operand_str("+10").unwrap()); // optional leading '+'
        assert_eq!(16, parse_offset_operand_str("0x10").unwrap()); // hex
        assert_eq!(16, parse_offset_operand_str("0X10").unwrap()); // hex
        assert_eq!(16, parse_offset_operand_str("+0X10").unwrap()); // hex
        assert_eq!(10, parse_offset_operand_str("10.").unwrap()); // decimal
        assert_eq!(10, parse_offset_operand_str("+10.").unwrap()); // decimal
        assert_eq!(4096, parse_offset_operand_str("10b").unwrap()); // b suffix = *512
        assert_eq!(4096, parse_offset_operand_str("+10b").unwrap()); // b suffix = *512
        assert_eq!(5120, parse_offset_operand_str("10.b").unwrap()); // b suffix = *512
        assert_eq!(5120, parse_offset_operand_str("+10.b").unwrap()); // b suffix = *512
        assert_eq!(267, parse_offset_operand_str("0x10b").unwrap()); // hex
    }
}
<|endoftext|>


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

#[allow(uppercase_variables)];

extern crate freetype;
extern crate fontconfig;

use fontconfig::fontconfig::{
    FcChar8, FcResultMatch, FcSetSystem, FcPattern,
    FcResultNoMatch, FcMatchPattern, FC_SLANT_ITALIC, FC_WEIGHT_BOLD, FC_SLANT_OBLIQUE
};
use fontconfig::fontconfig::{
    FcConfigGetCurrent, FcConfigGetFonts, FcPatternGetString,
    FcPatternDestroy, FcFontSetDestroy, FcConfigSubstitute,
    FcDefaultSubstitute, FcPatternCreate, FcPatternAddString, FcPatternAddInteger,
    FcFontMatch, FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy,
    FcObjectSetAdd, FcPatternGetInteger
};

use style::computed_values::font_style;

use font::{FontHandleMethods, UsedFontStyle};
use font_list::{FontEntry, FontFamily, FontFamilyMap};
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;

use collections::HashMap;
use libc;
use libc::{c_int, c_char};
use std::ptr;
use std::str;

pub struct FontListHandle {
    pub fctx: FontContextHandle,
}

impl FontListHandle {
    pub fn new(fctx: &FontContextHandle) -> FontListHandle {
        FontListHandle { fctx: fctx.clone() }
    }

    pub fn get_available_families(&self) -> FontFamilyMap {
        let mut family_map : FontFamilyMap = HashMap::new();
        unsafe {
            let config = FcConfigGetCurrent();
            let fontSet = FcConfigGetFonts(config, FcSetSystem);
            for i in range(0, (*fontSet).nfont as int) {
                let font = (*fontSet).fonts.offset(i);
                let family: *FcChar8 = ptr::null();
                let mut v: c_int = 0;
                "family".to_c_str().with_ref(|FC_FAMILY| {
                    while FcPatternGetString(*font, FC_FAMILY, v, &family) == FcResultMatch {
                        let family_name = str::raw::from_c_str(family as *c_char);
                        debug!("Creating new FontFamily for family: {:s}", family_name);
                        let new_family = FontFamily::new(family_name);
                        family_map.insert(family_name, new_family);
                        v += 1;
                    }
                });
            }
        }
        return family_map;
    }

    pub fn load_variations_for_family(&self, family: &mut FontFamily) {
        debug!("getting variations for {:?}", family);
        unsafe {
            let config = FcConfigGetCurrent();
            let font_set = FcConfigGetFonts(config, FcSetSystem);
            let font_set_array_ptr = &font_set;
            let pattern = FcPatternCreate();
            assert!(pattern.is_not_null());
            "family".to_c_str().with_ref(|FC_FAMILY| {
                family.family_name.to_c_str().with_ref(|family_name| {
                    let ok = FcPatternAddString(pattern, FC_FAMILY, family_name as *FcChar8);
                    assert!(ok != 0);
                });
            });

            let object_set = FcObjectSetCreate();
            assert!(object_set.is_not_null());

            "file".to_c_str().with_ref(|FC_FILE| {
                FcObjectSetAdd(object_set, FC_FILE);
            });
            "index".to_c_str().with_ref(|FC_INDEX| {
                FcObjectSetAdd(object_set, FC_INDEX);
            });

            let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

            debug!("found {} variations", (*matches).nfont);

            for i in range(0, (*matches).nfont as int) {
                let font = (*matches).fonts.offset(i);
                let file = "file".to_c_str().with_ref(|FC_FILE| {
                    let file: *FcChar8 = ptr::null();
                    if FcPatternGetString(*font, FC_FILE, 0, &file) == FcResultMatch {
                        str::raw::from_c_str(file as *libc::c_char)
                    } else {
                        fail!();
                    }
                });
                let index = "index".to_c_str().with_ref(|FC_INDEX| {
                    let index: libc::c_int = 0;
                    if FcPatternGetInteger(*font, FC_INDEX, 0, &index) == FcResultMatch {
                        index
                    } else {
                        fail!();
                    }
                });

                debug!("variation file: {}", file);
                debug!("variation index: {}", index);

                let font_handle = FontHandle::new_from_file_unstyled(&self.fctx,
                                                                     file);
                let font_handle = font_handle.unwrap();

                debug!("Creating new FontEntry for face: {:s}", font_handle.face_name());
                let entry = FontEntry::new(font_handle);
                family.entries.push(entry);
            }

            FcFontSetDestroy(matches);
            FcPatternDestroy(pattern);
            FcObjectSetDestroy(object_set);
        }
    }

    pub fn get_last_resort_font_families() -> ~[~str] {
        ~[~"Arial"]
    }
}

struct AutoPattern {
    pattern: *FcPattern
}

impl Drop for AutoPattern {
    fn drop(&mut self) {
        unsafe {
            FcPatternDestroy(self.pattern);
        }
    }
}

pub fn path_from_identifier(name: ~str, style: &UsedFontStyle) -> Result<~str, ()> {
    unsafe {
        let config = FcConfigGetCurrent();
        let wrapper = AutoPattern { pattern: FcPatternCreate() };
        let pattern = wrapper.pattern;
        let res = "family".to_c_str().with_ref(|FC_FAMILY| {
            name.to_c_str().with_ref(|family| {
                FcPatternAddString(pattern, FC_FAMILY, family as *FcChar8)
            })
        });
        if res != 1 {
            debug!("adding family to pattern failed");
            return Err(());
        }

        match style.style {
            font_style::normal => (),
            font_style::italic => {
                let res = "slant".to_c_str().with_ref(|FC_SLANT| {
                    FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC)
                });
                if res != 1 {
                    debug!("adding slant to pattern failed");
                    return Err(());
                }
            },
            font_style::oblique => {
                let res = "slant".to_c_str().with_ref(|FC_SLANT| {
                    FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_OBLIQUE)
                });
                if res != 1 {
                    debug!("adding slant(oblique) to pattern failed");
                    return Err(());
                }
            }
        }

        if style.weight.is_bold() {
            let res = "weight".to_c_str().with_ref(|FC_WEIGHT| {
                FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD)
            });
            if res != 1 {
                debug!("adding weight to pattern failed");
                return Err(());
            }
        }

        if FcConfigSubstitute(config, pattern, FcMatchPattern) != 1 {
            debug!("substitution failed");
            return Err(());
        }
        FcDefaultSubstitute(pattern);
        let result = FcResultNoMatch;
        let result_wrapper = AutoPattern { pattern: FcFontMatch(config, pattern, &result) };
        let result_pattern = result_wrapper.pattern;
        if result != FcResultMatch && result_pattern.is_null() {
            debug!("obtaining match to pattern failed");
            return Err(());
        }

        let file: *FcChar8 = ptr::null();
        let res = "file".to_c_str().with_ref(|FC_FILE| {
            FcPatternGetString(result_pattern, FC_FILE, 0, &file)
        });
        if res != FcResultMatch {
            debug!("getting filename for font failed");
            return Err(());
        }
        Ok(str::raw::from_c_str(file as *c_char))
    }
}
<commit_msg>auto merge of #2302 : Manishearth/servo/attr, r=Ms2ger<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![allow(uppercase_variables)]

extern crate freetype;
extern crate fontconfig;

use fontconfig::fontconfig::{
    FcChar8, FcResultMatch, FcSetSystem, FcPattern,
    FcResultNoMatch, FcMatchPattern, FC_SLANT_ITALIC, FC_WEIGHT_BOLD, FC_SLANT_OBLIQUE
};
use fontconfig::fontconfig::{
    FcConfigGetCurrent, FcConfigGetFonts, FcPatternGetString,
    FcPatternDestroy, FcFontSetDestroy, FcConfigSubstitute,
    FcDefaultSubstitute, FcPatternCreate, FcPatternAddString, FcPatternAddInteger,
    FcFontMatch, FcFontSetList, FcObjectSetCreate, FcObjectSetDestroy,
    FcObjectSetAdd, FcPatternGetInteger
};

use style::computed_values::font_style;

use font::{FontHandleMethods, UsedFontStyle};
use font_list::{FontEntry, FontFamily, FontFamilyMap};
use platform::font::FontHandle;
use platform::font_context::FontContextHandle;

use collections::HashMap;
use libc;
use libc::{c_int, c_char};
use std::ptr;
use std::str;

pub struct FontListHandle {
    pub fctx: FontContextHandle,
}

impl FontListHandle {
    pub fn new(fctx: &FontContextHandle) -> FontListHandle {
        FontListHandle { fctx: fctx.clone() }
    }

    pub fn get_available_families(&self) -> FontFamilyMap {
        let mut family_map : FontFamilyMap = HashMap::new();
        unsafe {
            let config = FcConfigGetCurrent();
            let fontSet = FcConfigGetFonts(config, FcSetSystem);
            for i in range(0, (*fontSet).nfont as int) {
                let font = (*fontSet).fonts.offset(i);
                let family: *FcChar8 = ptr::null();
                let mut v: c_int = 0;
                "family".to_c_str().with_ref(|FC_FAMILY| {
                    while FcPatternGetString(*font, FC_FAMILY, v, &family) == FcResultMatch {
                        let family_name = str::raw::from_c_str(family as *c_char);
                        debug!("Creating new FontFamily for family: {:s}", family_name);
                        let new_family = FontFamily::new(family_name);
                        family_map.insert(family_name, new_family);
                        v += 1;
                    }
                });
            }
        }
        return family_map;
    }

    pub fn load_variations_for_family(&self, family: &mut FontFamily) {
        debug!("getting variations for {:?}", family);
        unsafe {
            let config = FcConfigGetCurrent();
            let font_set = FcConfigGetFonts(config, FcSetSystem);
            let font_set_array_ptr = &font_set;
            let pattern = FcPatternCreate();
            assert!(pattern.is_not_null());
            "family".to_c_str().with_ref(|FC_FAMILY| {
                family.family_name.to_c_str().with_ref(|family_name| {
                    let ok = FcPatternAddString(pattern, FC_FAMILY, family_name as *FcChar8);
                    assert!(ok != 0);
                });
            });

            let object_set = FcObjectSetCreate();
            assert!(object_set.is_not_null());

            "file".to_c_str().with_ref(|FC_FILE| {
                FcObjectSetAdd(object_set, FC_FILE);
            });
            "index".to_c_str().with_ref(|FC_INDEX| {
                FcObjectSetAdd(object_set, FC_INDEX);
            });

            let matches = FcFontSetList(config, font_set_array_ptr, 1, pattern, object_set);

            debug!("found {} variations", (*matches).nfont);

            for i in range(0, (*matches).nfont as int) {
                let font = (*matches).fonts.offset(i);
                let file = "file".to_c_str().with_ref(|FC_FILE| {
                    let file: *FcChar8 = ptr::null();
                    if FcPatternGetString(*font, FC_FILE, 0, &file) == FcResultMatch {
                        str::raw::from_c_str(file as *libc::c_char)
                    } else {
                        fail!();
                    }
                });
                let index = "index".to_c_str().with_ref(|FC_INDEX| {
                    let index: libc::c_int = 0;
                    if FcPatternGetInteger(*font, FC_INDEX, 0, &index) == FcResultMatch {
                        index
                    } else {
                        fail!();
                    }
                });

                debug!("variation file: {}", file);
                debug!("variation index: {}", index);

                let font_handle = FontHandle::new_from_file_unstyled(&self.fctx,
                                                                     file);
                let font_handle = font_handle.unwrap();

                debug!("Creating new FontEntry for face: {:s}", font_handle.face_name());
                let entry = FontEntry::new(font_handle);
                family.entries.push(entry);
            }

            FcFontSetDestroy(matches);
            FcPatternDestroy(pattern);
            FcObjectSetDestroy(object_set);
        }
    }

    pub fn get_last_resort_font_families() -> ~[~str] {
        ~[~"Arial"]
    }
}

struct AutoPattern {
    pattern: *FcPattern
}

impl Drop for AutoPattern {
    fn drop(&mut self) {
        unsafe {
            FcPatternDestroy(self.pattern);
        }
    }
}

pub fn path_from_identifier(name: ~str, style: &UsedFontStyle) -> Result<~str, ()> {
    unsafe {
        let config = FcConfigGetCurrent();
        let wrapper = AutoPattern { pattern: FcPatternCreate() };
        let pattern = wrapper.pattern;
        let res = "family".to_c_str().with_ref(|FC_FAMILY| {
            name.to_c_str().with_ref(|family| {
                FcPatternAddString(pattern, FC_FAMILY, family as *FcChar8)
            })
        });
        if res != 1 {
            debug!("adding family to pattern failed");
            return Err(());
        }

        match style.style {
            font_style::normal => (),
            font_style::italic => {
                let res = "slant".to_c_str().with_ref(|FC_SLANT| {
                    FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_ITALIC)
                });
                if res != 1 {
                    debug!("adding slant to pattern failed");
                    return Err(());
                }
            },
            font_style::oblique => {
                let res = "slant".to_c_str().with_ref(|FC_SLANT| {
                    FcPatternAddInteger(pattern, FC_SLANT, FC_SLANT_OBLIQUE)
                });
                if res != 1 {
                    debug!("adding slant(oblique) to pattern failed");
                    return Err(());
                }
            }
        }

        if style.weight.is_bold() {
            let res = "weight".to_c_str().with_ref(|FC_WEIGHT| {
                FcPatternAddInteger(pattern, FC_WEIGHT, FC_WEIGHT_BOLD)
            });
            if res != 1 {
                debug!("adding weight to pattern failed");
                return Err(());
            }
        }

        if FcConfigSubstitute(config, pattern, FcMatchPattern) != 1 {
            debug!("substitution failed");
            return Err(());
        }
        FcDefaultSubstitute(pattern);
        let result = FcResultNoMatch;
        let result_wrapper = AutoPattern { pattern: FcFontMatch(config, pattern, &result) };
        let result_pattern = result_wrapper.pattern;
        if result != FcResultMatch && result_pattern.is_null() {
            debug!("obtaining match to pattern failed");
            return Err(());
        }

        let file: *FcChar8 = ptr::null();
        let res = "file".to_c_str().with_ref(|FC_FILE| {
            FcPatternGetString(result_pattern, FC_FILE, 0, &file)
        });
        if res != FcResultMatch {
            debug!("getting filename for font failed");
            return Err(());
        }
        Ok(str::raw::from_c_str(file as *c_char))
    }
}
<|endoftext|>