diff --git a/CHANGELOG.md b/CHANGELOG.md index bceb642a..3892f26d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ This project adheres to [Semantic Versioning](http://semver.org/). - FixedValue* -> FixVal* ## [Unreleased][unreleased] +- Nothing yet. + +## 0.4.0 - 2015-07-17 +### Added +- Low level `write_str` function allows to serialize the UTF-8 encoded strings the most efficient way. +- Low level `write_bin` function allows to serialize the binary array the most efficient way. - Implemented `std::error::Error` trait for error types. ## 0.3.2 - 2015-07-05 diff --git a/benches/bench.rs b/benches/bench.rs index ff45d650..9fd00a01 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -39,6 +39,21 @@ fn from_string_read_value(b: &mut Bencher) { }); } +#[bench] +fn from_string_read_value_ref(b: &mut Bencher) { + // Lorem ipsum dolor sit amet. + let buf = [ + 0xbb, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, + 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, + 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2e + ]; + + b.iter(|| { + let res = read_value_ref(&mut &buf[..]).unwrap(); + test::black_box(res); + }); +} + #[bench] fn from_complex_read_value(b: &mut Bencher) { let buf = [ @@ -61,6 +76,49 @@ fn from_complex_read_value(b: &mut Bencher) { }); } +#[bench] +fn from_complex_read_value_ref(b: &mut Bencher) { + let buf = [ + 0x95, // Fixed array with 5 len. + 0xc0, // Nil. + 0x2a, // 42. + 0xcb, 0x40, 0x9, 0x21, 0xca, 0xc0, 0x83, 0x12, 0x6f, // 3.1415 + // Fixed string with "Lorem ipsum dolor sit amet." content. + 0xbb, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, + 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, + 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2e, + 0x81, // Fixed map with 1 len. + 0xa3, 0x6b, 0x65, 0x79, // Key "key". + 0xa5, 0x76, 0x61, 0x6c, 0x75, 0x65 // Value: "value". + ]; + + b.iter(|| { + let res = read_value_ref(&mut &buf[..]).unwrap(); + test::black_box(res); + }); +} + +#[bench] +fn from_complex_read_value_ref_to_owned(b: &mut Bencher) { + let buf = [ + 0x95, // Fixed array with 5 len. + 0xc0, // Nil. + 0x2a, // 42. + 0xcb, 0x40, 0x9, 0x21, 0xca, 0xc0, 0x83, 0x12, 0x6f, // 3.1415 + // Fixed string with "Lorem ipsum dolor sit amet." content. + 0xbb, 0x4c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x69, 0x70, 0x73, 0x75, + 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, + 0x20, 0x61, 0x6d, 0x65, 0x74, 0x2e, + 0x81, // Fixed map with 1 len. + 0xa3, 0x6b, 0x65, 0x79, // Key "key". + 0xa5, 0x76, 0x61, 0x6c, 0x75, 0x65 // Value: "value". + ]; + + b.iter(|| { + let res = read_value_ref(&mut &buf[..]).unwrap().to_owned(); + test::black_box(res); + }); +} #[bench] fn from_i64_read_i64_loosely(b: &mut Bencher) { diff --git a/src/decode.rs b/src/decode.rs index b6846db5..bc959db8 100644 --- a/src/decode.rs +++ b/src/decode.rs @@ -24,6 +24,10 @@ use byteorder::ReadBytesExt; use super::Marker; +#[path = "decode/value_ref.rs"] +pub mod value_ref; +pub use self::value_ref::read_value_ref; + /// Represents an error that can occur when attempting to read bytes from the reader. /// /// This is a thin wrapper over the standard `io::Error` type. Namely, it adds one additional error @@ -40,6 +44,7 @@ impl Error for ReadError { fn description(&self) -> &str { match *self { ReadError::UnexpectedEOF => "unexpected end of file while reading MessagePack value", + // TODO: Probably we should give here a short description that I/O error occurs. ReadError::Io(ref err) => err.description(), } } @@ -1038,6 +1043,7 @@ fn read_str_data<'r, R>(rd: &mut R, len: u32, buf: &'r mut[u8]) -> Result<&'r st /// // TODO: it is better to return &str; may panic on len mismatch; extend documentation. // TODO: Also it's possible to implement all borrowing functions for all `BufRead` implementors. +// TODO: It's not necessary to use cursor, use slices instead. pub fn read_str_ref(rd: &[u8]) -> Result<&[u8], DecodeStringError> { let mut cur = io::Cursor::new(rd); let len = try!(read_str_len(&mut cur)); diff --git a/src/decode/value_ref.rs b/src/decode/value_ref.rs new file mode 100644 index 00000000..0068c109 --- /dev/null +++ b/src/decode/value_ref.rs @@ -0,0 +1,459 @@ +//! This module is UNSTABLE, the reason is - recently added. + +use std::convert::From; +use std::error; +use std::fmt; +use std::io::{Cursor, Read}; +use std::str::{from_utf8, Utf8Error}; + +use super::{read_marker}; +use super::{ + ReadError, + MarkerReadError, +}; +use super::{BigEndianRead}; + +use super::super::init::Marker; +use super::super::value::{Float, Integer, ValueRef}; + +trait ToUnsigned { + fn from(v: Self) -> Option where Self: Sized; +} + +impl ToUnsigned for u8 { + fn from(v: u8) -> Option { + // Impossible to panic, since u8 always fits in usize. + Some(v as usize) + } +} + +impl ToUnsigned for u16 { + fn from(v: u16) -> Option { + // TODO: This can overflow on 8-bit systems. + Some(v as usize) + } +} + +impl ToUnsigned for u32 { + fn from(v: u32) -> Option { + // TODO: This can overflow on 8- and 16-bit systems. + Some(v as usize) + } +} + +#[derive(Debug)] +pub enum Error<'r> { + /// Failed to read the type marker value. + InvalidMarkerRead(ReadError), + /// Failed to read string/array/map size. + InvalidLengthRead(ReadError), + /// Failed to read packed non-marker data. + InvalidDataRead(ReadError), + /// Failed to cast the length read to machine size. + InvalidLengthSize, + /// Failed to interpret a byte slice as a UTF-8 string. + /// + /// Contains untouched bytearray with the underlying decoding error. + InvalidUtf8(&'r [u8], Utf8Error), + /// Failed to read ext type. + InvalidExtTypeRead(ReadError), + /// Using Reserved type found. + TypeMismatch, +} + +impl<'r> error::Error for Error<'r> { + fn description(&self) -> &str { + match self { + &Error::InvalidMarkerRead(..) => "failed to read the type marker value", + &Error::InvalidLengthRead(..) => "failed to read string/array/map size", + &Error::InvalidDataRead(..) => "failed to read packed non-marker data", + &Error::InvalidLengthSize => "failed to cast the length read to machine size", + &Error::InvalidUtf8(..) => "failed to interpret a byte slice as a UTF-8 string", + &Error::InvalidExtTypeRead(..) => "failed to read ext type", + &Error::TypeMismatch => "using Reserved type found", + } + } + + fn cause(&self) -> Option<&error::Error> { + match self { + &Error::InvalidMarkerRead(ref err) => Some(err), + &Error::InvalidLengthRead(ref err) => Some(err), + &Error::InvalidDataRead(ref err) => Some(err), + &Error::InvalidLengthSize => None, + &Error::InvalidUtf8(_, ref err) => Some(err), + &Error::InvalidExtTypeRead(ref err) => Some(err), + &Error::TypeMismatch => None, + } + } +} + +impl<'r> fmt::Display for Error<'r> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use std::error::Error; + self.description().fmt(f) + } +} + +impl<'r> From for Error<'r> { + fn from(err: MarkerReadError) -> Error<'r> { + Error::InvalidMarkerRead(From::from(err)) + } +} + +fn read_len(rd: &mut R) -> Result + where R: Read, + D: BigEndianRead +{ + D::read(rd).map_err(From::from) +} + +fn read_num<'a, R, D>(mut rd: &mut R) -> Result> + where R: BorrowRead<'a>, + D: BigEndianRead +{ + D::read(&mut rd).map_err(|err| Error::InvalidDataRead(From::from(err))) +} + +fn read_str<'a, R>(rd: &mut R, len: usize) -> Result<&'a str, Error<'a>> + where R: BorrowRead<'a> +{ + let buf = try!(read_bin(rd, len)); + + // Try to decode sliced buffer as UTF-8. + let res = try!(from_utf8(buf).map_err(|err| Error::InvalidUtf8(buf, err))); + + Ok(res) +} + +fn read_bin<'a, R>(rd: &mut R, len: usize) -> Result<&'a [u8], Error<'a>> + where R: BorrowRead<'a> +{ + let buf = rd.fill_buf(); + + if len > buf.len() { + return Err(Error::InvalidDataRead(ReadError::UnexpectedEOF)); + } + + // Take a slice. + let buf = &buf[..len]; + rd.consume(len); + + Ok(buf) +} + +// Helper function that reads a single byte from the given `Read` and interpret it as an Ext type. +fn read_ext_type(rd: &mut R) -> Result + where R: Read +{ + i8::read(rd).map_err(From::from) +} + +fn read_ext<'a, R>(mut rd: &mut R, len: usize) -> Result<(i8, &'a [u8]), Error<'a>> + where R: BorrowRead<'a> +{ + let ty = try!(read_ext_type(&mut rd).map_err(|err| Error::InvalidExtTypeRead(err))); + let buf = try!(read_bin(rd, len)); + + Ok((ty, buf)) +} + +#[inline] +fn read_str_value<'a, R, U>(rd: &mut R, len: U) -> Result, Error<'a>> + where R: BorrowRead<'a>, + U: ToUnsigned +{ + let len = try!(U::from(len).ok_or(Error::InvalidLengthSize)); + let res = try!(read_str(rd, len)); + + Ok(ValueRef::String(res)) +} + +#[inline] +fn read_bin_value<'a, R, U>(rd: &mut R, len: U) -> Result, Error<'a>> + where R: BorrowRead<'a>, + U: ToUnsigned +{ + let len = try!(U::from(len).ok_or(Error::InvalidLengthSize)); + let res = try!(read_bin(rd, len)); + + Ok(ValueRef::Binary(res)) +} + +#[inline] +fn read_ext_value<'a, R, U>(mut rd: &mut R, len: U) -> Result, Error<'a>> + where R: BorrowRead<'a>, + U: ToUnsigned +{ + let len = try!(U::from(len).ok_or(Error::InvalidLengthSize)); + let (ty, buf) = try!(read_ext(rd, len)); + + Ok(ValueRef::Ext(ty, buf)) +} + +#[inline] +fn read_array_value<'a, R, U>(rd: &mut R, len: U) -> Result, Error<'a>> + where R: BorrowRead<'a>, + U: ToUnsigned +{ + let len = try!(U::from(len).ok_or(Error::InvalidLengthSize)); + let vec = try!(read_array(rd, len)); + + Ok(ValueRef::Array(vec)) +} + +#[inline] +fn read_map_value<'a, R, U>(rd: &mut R, len: U) -> Result, Error<'a>> + where R: BorrowRead<'a>, + U: ToUnsigned +{ + let len = try!(U::from(len).ok_or(Error::InvalidLengthSize)); + let map = try!(read_map(rd, len)); + + Ok(ValueRef::Map(map)) +} + +fn read_array<'a, R>(rd: &mut R, len: usize) -> Result>, Error<'a>> + where R: BorrowRead<'a> +{ + let mut vec = Vec::with_capacity(len); + + for _ in 0..len { + let val = try!(read_value_ref(rd)); + + vec.push(val); + } + + Ok(vec) +} + +fn read_map<'a, R>(rd: &mut R, len: usize) -> Result, ValueRef<'a>)>, Error<'a>> + where R: BorrowRead<'a> +{ + let mut vec = Vec::with_capacity(len); + + for _ in 0..len { + let key = try!(read_value_ref(rd)); + let val = try!(read_value_ref(rd)); + + vec.push((key, val)); + } + + Ok(vec) +} + +/// A BorrowRead is a type of Reader which has an internal buffer. +/// +/// This magic trait acts like a standard BufRead but unlike the standard this has an explicit +/// internal buffer lifetime, which allows to borrow from underlying buffer while consuming bytes. +pub trait BorrowRead<'a>: Read { + /// Returns the buffer contents. + /// + /// This function is a lower-level call. It needs to be paired with the consume method to + /// function properly. When calling this method, none of the contents will be "read" in the + /// sense that later calling read may return the same contents. As such, consume must be called + /// with the number of bytes that are consumed from this buffer to ensure that the bytes are + /// never returned twice. + /// + /// An empty buffer returned indicates that the stream has reached EOF. + fn fill_buf(&self) -> &'a [u8]; + + /// Tells this buffer that len bytes have been consumed from the buffer, so they should no + /// longer be returned in calls to read. + fn consume(&mut self, len: usize); +} + +impl<'a> BorrowRead<'a> for &'a [u8] { + fn fill_buf(&self) -> &'a [u8] { + self + } + + fn consume(&mut self, len: usize) { + *self = &(*self)[len..]; + } +} + +/// Useful when you want to know how much bytes has been consumed during ValueRef decoding. +impl<'a> BorrowRead<'a> for Cursor<&'a [u8]> { + fn fill_buf(&self) -> &'a [u8] { + use std::cmp; + + let len = cmp::min(self.position(), self.get_ref().len() as u64); + &self.get_ref()[len as usize..] + } + + fn consume(&mut self, len: usize) { + let pos = self.position(); + self.set_position(pos + len as u64); + } +} + +/// Attempts to read the data from the given reader until either a complete MessagePack value +/// decoded or an error detected. +/// +/// Returns either a non-owning `ValueRef`, which borrows the buffer from the given reader or an +/// error. +/// +/// The reader should meet the requirement of a special `BorrowRead` trait, which allows to mutate +/// itself but permits to mutate the buffer it contains. It allows to perform a completely +/// zero-copy reading without a data loss fear in case of an error. +/// +/// Currently only two types fit in this requirement: `&[u8]` and `Cursor<&[u8]>`. Using Cursor is +/// helpful, when you need to know how exactly many bytes the decoded ValueRef consumes. A `Vec` +/// type doesn't fit in the `BorrowRead` requirement, because its mut reference can mutate the +/// underlying buffer - use `Vec::as_slice()` if you need to decode a value from the vector. +/// +/// # Errors +/// +/// Returns an `Error` value if unable to continue the decoding operation either because of read +/// failure or any other circumstances. See `Error` documentation for more information. +/// +/// # Examples +/// ``` +/// use rmp::ValueRef; +/// use rmp::decode::value_ref::read_value_ref; +/// +/// let buf = [0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65]; +/// let mut rd = &buf[..]; +/// +/// assert_eq!(ValueRef::String("le message"), read_value_ref(&mut rd).unwrap()); +/// ``` +pub fn read_value_ref<'a, R>(rd: &mut R) -> Result, Error<'a>> + where R: BorrowRead<'a> +{ + let mut rd = rd; + + // Reading the marker involves either 1 byte read or nothing. On success consumes strictly + // 1 byte from the `rd`. + let marker = try!(read_marker(rd)); + + let val = match marker { + Marker::Null => ValueRef::Nil, + Marker::True => ValueRef::Boolean(true), + Marker::False => ValueRef::Boolean(false), + Marker::PositiveFixnum(val) => { + ValueRef::Integer(Integer::U64(val as u64)) + } + Marker::U8 => { + let val: u8 = try!(read_num(rd)); + ValueRef::Integer(Integer::U64(val as u64)) + } + Marker::U16 => { + let val: u16 = try!(read_num(rd)); + ValueRef::Integer(Integer::U64(val as u64)) + } + Marker::U32 => { + let val: u32 = try!(read_num(rd)); + ValueRef::Integer(Integer::U64(val as u64)) + } + Marker::U64 => { + let val: u64 = try!(read_num(rd)); + ValueRef::Integer(Integer::U64(val)) + } + Marker::NegativeFixnum(val) => { + ValueRef::Integer(Integer::I64(val as i64)) + } + Marker::I8 => { + let val: i8 = try!(read_num(rd)); + ValueRef::Integer(Integer::I64(val as i64)) + } + Marker::I16 => { + let val: i16 = try!(read_num(rd)); + ValueRef::Integer(Integer::I64(val as i64)) + } + Marker::I32 => { + let val: i32 = try!(read_num(rd)); + ValueRef::Integer(Integer::I64(val as i64)) + } + Marker::I64 => { + let val: i64 = try!(read_num(rd)); + ValueRef::Integer(Integer::I64(val)) + } + Marker::F32 => { + let val: f32 = try!(read_num(rd)); + ValueRef::Float(Float::F32(val)) + } + Marker::F64 => { + let val: f64 = try!(read_num(rd)); + ValueRef::Float(Float::F64(val)) + } + Marker::FixedString(len) => { + try!(read_str_value(rd, len)) + } + Marker::Str8 => { + let len: u8 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_str_value(rd, len)) + } + Marker::Str16 => { + let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_str_value(rd, len)) + } + Marker::Str32 => { + let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_str_value(rd, len)) + } + Marker::Bin8 => { + let len: u8 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_bin_value(rd, len)) + } + Marker::Bin16 => { + let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_bin_value(rd, len)) + } + Marker::Bin32 => { + let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_bin_value(rd, len)) + } + Marker::FixedArray(len) => { + try!(read_array_value(rd, len)) + } + Marker::Array16 => { + let len: u16 = try!(read_len(&mut rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_array_value(rd, len)) + } + Marker::Array32 => { + let len: u32 = try!(read_len(&mut rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_array_value(rd, len)) + } + Marker::FixedMap(len) => { + try!(read_map_value(rd, len)) + } + Marker::Map16 => { + let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_map_value(rd, len)) + } + Marker::Map32 => { + let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_map_value(rd, len)) + } + Marker::FixExt1 => { + try!(read_ext_value(rd, 1u8)) + } + Marker::FixExt2 => { + try!(read_ext_value(rd, 2u8)) + } + Marker::FixExt4 => { + try!(read_ext_value(rd, 4u8)) + } + Marker::FixExt8 => { + try!(read_ext_value(rd, 8u8)) + } + Marker::FixExt16 => { + try!(read_ext_value(rd, 16u8)) + } + Marker::Ext8 => { + let len: u8 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_ext_value(rd, len)) + } + Marker::Ext16 => { + let len: u16 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_ext_value(rd, len)) + } + Marker::Ext32 => { + let len: u32 = try!(read_len(rd).map_err(|err| Error::InvalidLengthRead(err))); + try!(read_ext_value(rd, len)) + } + Marker::Reserved => return Err(Error::TypeMismatch), + }; + + Ok(val) +} diff --git a/src/encode.rs b/src/encode.rs index d6d870a0..94b1ffda 100644 --- a/src/encode.rs +++ b/src/encode.rs @@ -10,6 +10,9 @@ use byteorder::WriteBytesExt; use super::Marker; +#[path = "encode/value_ref.rs"] +pub mod value_ref; + /// Represents an error that can occur when attempting to write MessagePack'ed value into the write. #[derive(Debug)] pub struct WriteError(io::Error); @@ -807,12 +810,14 @@ pub fn write_value(wr: &mut W, val: &Value) -> Result<(), Error> match val { &Value::Nil => try!(write_nil(wr)), &Value::Boolean(val) => try!(write_bool(wr, val)), + // TODO: Replace with generic write_int(...). &Value::Integer(Integer::U64(val)) => { try!(write_uint(wr, val)); } &Value::Integer(Integer::I64(val)) => { try!(write_sint(wr, val)); } + // TODO: Replace with generic write_float(...). &Value::Float(Float::F32(val)) => try!(write_f32(wr, val)), &Value::Float(Float::F64(val)) => try!(write_f64(wr, val)), &Value::String(ref val) => { @@ -843,26 +848,7 @@ pub fn write_value(wr: &mut W, val: &Value) -> Result<(), Error> Ok(()) } -// TODO: Move tests outside. -#[cfg(test)] -mod tests { - -use super::*; - -#[test] -fn pack_nil() { - let mut buf = [0x00]; - - let val = Value::Nil; - - write_value(&mut &mut buf[..], &val).unwrap(); - - assert_eq!([0xc0], buf); -} - -} - -} +} // mod value pub mod serialize { diff --git a/src/encode/value_ref.rs b/src/encode/value_ref.rs new file mode 100644 index 00000000..33a71abb --- /dev/null +++ b/src/encode/value_ref.rs @@ -0,0 +1,161 @@ +//! This module is UNSTABLE, the reason is - recently added. + +use std::convert::From; +use std::error; +use std::fmt; +use std::io::Write; + +// TODO: Includes cleanup is required. +use super::super::value::{Float, Integer, ValueRef}; +use super::FixedValueWriteError; +use super::ValueWriteError; +use super::WriteError; +use super::write_array_len; +use super::write_bin; +use super::write_bool; +use super::write_ext_meta; +use super::write_f32; +use super::write_f64; +use super::write_map_len; +use super::write_nil; +use super::write_sint; +use super::write_str; +use super::write_uint; + +#[derive(Debug)] +pub struct Error(WriteError); + +impl fmt::Display for Error { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + &Error(ref err) => err.fmt(fmt), + } + } +} + +impl error::Error for Error { + fn description(&self) -> &str { + match self { + &Error(ref err) => err.description(), + } + } + + fn cause(&self) -> Option<&error::Error> { + match self { + &Error(ref err) => err.cause(), + } + } +} + +impl From for Error { + fn from(err: FixedValueWriteError) -> Error { + match err { + FixedValueWriteError(err) => Error(err) + } + } +} + +impl From for Error { + fn from(err: ValueWriteError) -> Error { + match err { + ValueWriteError::InvalidMarkerWrite(err) => Error(err), + ValueWriteError::InvalidDataWrite(err) => Error(err) + } + } +} + +/// Encodes and attempts to write the given non-owning ValueRef into the Write. +/// +/// # Errors +/// +/// This function returns Error with an underlying I/O error if unable to properly write entire +/// value. Interruption errors are handled internally by silent operation restarting. +/// +/// # Examples +/// ``` +/// use rmp::ValueRef; +/// use rmp::encode::value_ref::write_value_ref; +/// +/// let mut buf = Vec::new(); +/// let val = ValueRef::String("le message"); +/// +/// write_value_ref(&mut buf, &val).unwrap(); +/// assert_eq!(vec![0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65], buf); +/// ``` +pub fn write_value_ref(wr: &mut W, val: &ValueRef) -> Result<(), Error> + where W: Write +{ + match val { + &ValueRef::Nil => try!(write_nil(wr)), + &ValueRef::Boolean(val) => try!(write_bool(wr, val)), + &ValueRef::Integer(Integer::U64(val)) => { + try!(write_uint(wr, val)); + } + &ValueRef::Integer(Integer::I64(val)) => { + try!(write_sint(wr, val)); + } + // TODO: Replace with generic write_float(...). + &ValueRef::Float(Float::F32(val)) => try!(write_f32(wr, val)), + &ValueRef::Float(Float::F64(val)) => try!(write_f64(wr, val)), + &ValueRef::String(val) => { + try!(write_str(wr, val)); + } + &ValueRef::Binary(val) => { + try!(write_bin(wr, val)); + } + &ValueRef::Array(ref val) => { + let len = val.len() as u32; + + try!(write_array_len(wr, len)); + + for item in val { + try!(write_value_ref(wr, item)); + } + } + &ValueRef::Map(ref val) => { + let len = val.len() as u32; + + try!(write_map_len(wr, len)); + + for &(ref key, ref val) in val { + try!(write_value_ref(wr, key)); + try!(write_value_ref(wr, val)); + } + } + &ValueRef::Ext(ty, data) => { + try!(write_ext_meta(wr, data.len() as u32, ty)); + try!(wr.write_all(data).map_err(|err| ValueWriteError::InvalidDataWrite(WriteError(err)))); + } + } + + Ok(()) +} + +#[cfg(test)] +mod test { + use std::error; + use std::io; + use super::super::WriteError; + use super::Error; + + #[test] + fn display_trait() { + let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF"))); + + assert_eq!("err: error while writing MessagePack'ed value", format!("err: {}", err)); + } + + #[test] + fn error_trait() { + // Delegates to I/O Error. + let err = Error(WriteError(io::Error::new(io::ErrorKind::Other, "unexpected EOF"))); + + fn test(err: E) { + assert_eq!("error while writing MessagePack'ed value", err.description()); + assert!(err.cause().is_some()); + assert_eq!("unexpected EOF", err.cause().unwrap().description()); + } + + test(err); + } +} diff --git a/src/init.rs b/src/init.rs index 71077071..6ad150e4 100644 --- a/src/init.rs +++ b/src/init.rs @@ -4,7 +4,9 @@ const FIXMAP_SIZE : u8 = 0x0f; #[derive(Clone, Copy, PartialEq, Debug)] pub enum Marker { + // TODO: Possibly will be renamed to `FixPos`. PositiveFixnum(u8), + // TODO: Possibly will be renamed to `FixNeg`. NegativeFixnum(i8), Null, True, @@ -19,6 +21,7 @@ pub enum Marker { I64, F32, F64, + // TODO: Possibly will be renamed to `FixStr`. FixedString(u8), Str8, Str16, @@ -26,9 +29,11 @@ pub enum Marker { Bin8, Bin16, Bin32, + // TODO: Possibly will be renamed to `FixArray`. FixedArray(u8), Array16, Array32, + // TODO: Possibly will be renamed to `FixMap`. FixedMap(u8), Map16, Map32, diff --git a/src/lib.rs b/src/lib.rs index 5f02ab7e..0f4ae6c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,27 +124,18 @@ extern crate byteorder; extern crate rustc_serialize as serialize; -pub const MSGPACK_VERSION : u32 = 5; - -pub use init::Marker; +pub mod encode; +pub mod decode; -pub use decode::serialize::{ - Decoder, -}; +mod init; -pub use encode::serialize::{ - Encoder, -}; +pub mod value; -mod init; -pub mod encode; -pub mod decode; +pub const MSGPACK_VERSION : u32 = 5; -// TODO: Not ready yet. -mod value; +pub use decode::serialize::Decoder; +pub use encode::serialize::Encoder; -pub use value::Value; +pub use init::Marker; -// Suppressed due to instability. -// #[cfg(test)] -// mod bench; +pub use value::{Value, ValueRef}; diff --git a/src/value.rs b/src/value.rs index 7590373c..aa095f58 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum Integer { /// Every non-negative integer is treated as u64, even if it fits in i64. U64(u64), @@ -6,7 +6,7 @@ pub enum Integer { I64(i64), } -#[derive(Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq)] pub enum Float { F32(f32), F64(f64), @@ -35,25 +35,92 @@ pub enum Value { Ext(i8, Vec), } -//#[derive(Clone, Debug, PartialEq)] -//enum ValueRef<'a> { -// /// Nil represents nil. -// Nil, -// /// Boolean represents true or false. -// Boolean(bool), -// /// Integer represents an integer. -// Integer(Integer), -// /// Float represents a floating point number. -// Float(Float), -// /// String extending Raw type represents a UTF-8 string. -// String(&'a str), -// /// Binary extending Raw type represents a byte array. -// Binary(&'a [u8]), -// /// Array represents a sequence of objects. -// Array(&'a [ValueRef<'a>]), -// /// Map represents key-value pairs of objects. -// Map(&'a [(ValueRef<'a>, ValueRef<'a>)]), -// /// Extended implements Extension interface: represents a tuple of type information and a byte -// /// array where type information is an integer whose meaning is defined by applications. -// Ext(i8, &'a [u8]), -//} +#[derive(Clone, Debug, PartialEq)] +pub enum ValueRef<'a> { + /// Nil represents nil. + Nil, + /// Boolean represents true or false. + Boolean(bool), + /// Integer represents an integer. + Integer(Integer), + /// Float represents a floating point number. + Float(Float), + /// String extending Raw type represents a UTF-8 string. + String(&'a str), + /// Binary extending Raw type represents a byte array. + Binary(&'a [u8]), + /// Array represents a sequence of objects. + Array(Vec>), + /// Map represents key-value pairs of objects. + Map(Vec<(ValueRef<'a>, ValueRef<'a>)>), + /// Extended implements Extension interface: represents a tuple of type information and a byte + /// array where type information is an integer whose meaning is defined by applications. + Ext(i8, &'a [u8]), +} + +impl<'a> ValueRef<'a> { + /// Converts the current non-owning value to an owned Value. + /// + /// This is achieved by deep copying all underlying structures and borrowed buffers. + /// + /// # Panics + /// + /// Panics in unable to allocate memory to keep all internal structures and buffers. + /// + /// # Examples + /// ``` + /// use rmp::{Value, ValueRef}; + /// use rmp::value::Integer; + /// + /// let val = ValueRef::Array(vec![ + /// ValueRef::Nil, + /// ValueRef::Integer(Integer::U64(42)), + /// ValueRef::Array(vec![ + /// ValueRef::String("le message"), + /// ]) + /// ]); + /// + /// let expected = Value::Array(vec![ + /// Value::Nil, + /// Value::Integer(Integer::U64(42)), + /// Value::Array(vec![ + /// Value::String("le message".to_string()) + /// ]) + /// ]); + /// + /// assert_eq!(expected, val.to_owned()); + /// ``` + pub fn to_owned(&self) -> Value { + match self { + &ValueRef::Nil => Value::Nil, + &ValueRef::Boolean(val) => Value::Boolean(val), + &ValueRef::Integer(val) => Value::Integer(val), + &ValueRef::Float(val) => Value::Float(val), + &ValueRef::String(val) => Value::String(val.to_string()), + &ValueRef::Binary(val) => Value::Binary(val.to_vec()), + &ValueRef::Array(ref val) => { + let mut vec = Vec::new(); + for item in val { + vec.push(item.to_owned()); + } + + Value::Array(vec) + } + &ValueRef::Map(ref val) => { + let mut vec = Vec::new(); + for &(ref key, ref val) in val { + vec.push((key.to_owned(), val.to_owned())); + } + + Value::Map(vec) + } + &ValueRef::Ext(ty, buf) => Value::Ext(ty, buf.to_vec()), + } + } +} + +// For some weird reasons I can't implement it manually. +// It gives: conflicting implementations for trait `collections::borrow::ToOwned` +// impl<'a> ToOwned for ValueRef<'a> { +// type Owned = Value; +// } diff --git a/tests/func/decode/mod.rs b/tests/func/decode/mod.rs index 5dd0888d..f3af248e 100644 --- a/tests/func/decode/mod.rs +++ b/tests/func/decode/mod.rs @@ -12,3 +12,4 @@ mod ext; // High-level deserialize interface. mod decoder; mod value; +mod value_ref; diff --git a/tests/func/decode/value_ref.rs b/tests/func/decode/value_ref.rs new file mode 100644 index 00000000..04008bfa --- /dev/null +++ b/tests/func/decode/value_ref.rs @@ -0,0 +1,691 @@ +use msgpack::ValueRef; +use msgpack::decode::read_value_ref; +use msgpack::decode::value_ref::Error; +use msgpack::value::{Float, Integer}; + +#[test] +fn from_strfix() { + let buf = [0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65]; + let mut rd = &buf[..]; + + assert_eq!(ValueRef::String("le message"), read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_str8() { + let buf = [ + 0xd9, // Type. + 0x20, // Size + 0x42, // B + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x45 // E + ]; + + let mut slice = &buf[..]; + + assert_eq!(ValueRef::String("B123456789012345678901234567890E"), + read_value_ref(&mut slice).ok().unwrap()); +} + +#[test] +fn from_str16() { + let buf = [ + 0xda, // Type. + 0x00, 0x20, // Size + 0x42, // B + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x45 // E + ]; + + let mut slice = &buf[..]; + + assert_eq!(ValueRef::String("B123456789012345678901234567890E"), + read_value_ref(&mut slice).ok().unwrap()); +} + +#[test] +fn from_str32() { + let buf = [ + 0xdb, // Type. + 0x00, 0x00, 0x00, 0x20, // Size + 0x42, // B + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x45 // E + ]; + + let mut slice = &buf[..]; + + assert_eq!(ValueRef::String("B123456789012345678901234567890E"), + read_value_ref(&mut slice).ok().unwrap()); +} + +#[test] +fn from_empty_buffer_invalid_marker_read() { + let buf = []; + + let mut slice = &buf[..]; + + match read_value_ref(&mut slice).err().unwrap() { + Error::InvalidMarkerRead(..) => (), + _ => panic!(), + } +} + +#[test] +fn from_empty_buffer_invalid_buffer_fill() { + use std::io::{self, Read}; + use msgpack::decode::value_ref::BorrowRead; + + struct ErrorRead; + + impl Read for ErrorRead { + fn read(&mut self, _buf: &mut [u8]) -> io::Result { + Err(io::Error::new(io::ErrorKind::Other, "mock error")) + } + } + + impl<'a> BorrowRead<'a> for ErrorRead { + fn fill_buf(&self) -> &'a [u8] { &[] } + fn consume(&mut self, _: usize) {} + } + + let mut rd = ErrorRead; + + match read_value_ref(&mut rd).err().unwrap() { + Error::InvalidMarkerRead(..) => (), + _ => panic!(), + } +} + +#[test] +fn from_string_insufficient_bytes_while_reading_length() { + let buf = [0xd9]; + let mut rd = &buf[..]; + + match read_value_ref(&mut rd).err().unwrap() { + Error::InvalidLengthRead(..) => (), + _ => panic!(), + } +} + +#[test] +fn from_string_insufficient_bytes_while_reading_data() { + let buf = [ + 0xd9, // Type. + 0x20, // Size == 32 + 0x42, // B + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30, + 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30 + ]; + + let mut rd = &buf[..]; + + match read_value_ref(&mut rd).err().unwrap() { + Error::InvalidDataRead(..) => (), + _ => panic!(), + } +} + +#[test] +fn from_string_invalid_utf8() { + // Invalid 2 Octet Sequence. + let buf = [0xd9, 0x02, 0xc3, 0x28]; + + let mut rd = &buf[..]; + + match read_value_ref(&mut rd).err().unwrap() { + Error::InvalidUtf8(act, _) => { assert_eq!(&[0xc3, 0x28], act); }, + _ => panic!(), + } +} + +#[test] +fn from_bin8() { + let buf = [0xc4, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Binary(&[0, 1, 2, 3, 4]), + read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_bin16() { + let buf = [0xc5, 0x00, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Binary(&[0, 1, 2, 3, 4]), + read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_bin32() { + let buf = [0xc6, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Binary(&[0, 1, 2, 3, 4]), + read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_bin8_eof_while_reading_data() { + let buf = [0xc4, 0x05, 0x00, 0x01, 0x02, 0x03]; + + let mut rd = &buf[..]; + + match read_value_ref(&mut rd).err().unwrap() { + Error::InvalidDataRead(..) => (), + _ => panic!(), + } +} + +#[test] +fn from_fixext1() { + let buf = [0xd4, 0x2a, 0xff]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Ext(42, &[255]), + read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_ext1_eof_while_reading_type() { + let buf = [0xd4]; + + let mut rd = &buf[..]; + + match read_value_ref(&mut rd).err().unwrap() { + Error::InvalidExtTypeRead(..) => (), + _ => panic!(), + } +} + +#[test] +fn from_fixext2() { + let buf = [0xd5, 0x2a, 0xff, 0xee]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Ext(42, &[255, 238]), + read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_fixext4() { + let buf = [0xd6, 0x2a, 0xff, 0xee, 0xdd, 0xcc]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Ext(42, &[255, 238, 221, 204]), + read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_fixext8() { + let buf = [0xd7, 0x2a, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Ext(42, &[255, 238, 221, 204, 187, 170, 153, 136]), + read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_fixext16() { + let buf = [ + 0xd8, 0x2a, + 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, + 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00 + ]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Ext(42, &[255, 238, 221, 204, 187, 170, 153, 136, 119, 102, 85, 68, 51, 34, 17, 0]), + read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_ext8() { + let buf = [0xc7, 0x04, 0x2a, 0xff, 0xee, 0xdd, 0xcc]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Ext(42, &[255, 238, 221, 204]), + read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_ext16() { + let buf = [0xc8, 0x00, 0x04, 0x2a, 0xff, 0xee, 0xdd, 0xcc]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Ext(42, &[255, 238, 221, 204]), + read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_ext32() { + let buf = [0xc9, 0x00, 0x00, 0x00, 0x04, 0x2a, 0xff, 0xee, 0xdd, 0xcc]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Ext(42, &[255, 238, 221, 204]), + read_value_ref(&mut rd).ok().unwrap()); +} + +#[test] +fn from_fixmap() { + let buf = [ + 0x82, // size: 2 + 0x2a, // 42 + 0xce, 0x0, 0x1, 0x88, 0x94, // 100500 + 0xa3, 0x6b, 0x65, 0x79, // 'key' + 0xa5, 0x76, 0x61, 0x6c, 0x75, 0x65 // 'value' + ]; + let mut rd = &buf[..]; + + let map = vec![ + (ValueRef::Integer(Integer::U64(42)), ValueRef::Integer(Integer::U64(100500))), + (ValueRef::String("key"), ValueRef::String("value")), + ]; + let expected = ValueRef::Map(map); + + assert_eq!(expected, read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_map16() { + let buf = [ + 0xde, + 0x00, 0x01, + 0xa3, 0x6b, 0x65, 0x79, // 'key' + 0xa5, 0x76, 0x61, 0x6c, 0x75, 0x65 // 'value' + ]; + let mut rd = &buf[..]; + + let map = vec![(ValueRef::String("key"), ValueRef::String("value"))]; + let expected = ValueRef::Map(map); + + assert_eq!(expected, read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_map32() { + let buf = [ + 0xdf, + 0x00, 0x00, 0x00, 0x01, + 0xa3, 0x6b, 0x65, 0x79, // 'key' + 0xa5, 0x76, 0x61, 0x6c, 0x75, 0x65 // 'value' + ]; + let mut rd = &buf[..]; + + let map = vec![(ValueRef::String("key"), ValueRef::String("value"))]; + let expected = ValueRef::Map(map); + + assert_eq!(expected, read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_fixarray() { + let buf = [ + 0x92, + 0xa2, 0x76, 0x31, + 0xa2, 0x76, 0x32, + ]; + let mut rd = &buf[..]; + + let vec = vec![ValueRef::String("v1"), ValueRef::String("v2")]; + + assert_eq!(ValueRef::Array(vec), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_array16() { + let buf = [ + 0xdc, + 0x00, 0x02, + 0xa2, 0x76, 0x31, + 0xa2, 0x76, 0x32, + ]; + let mut rd = &buf[..]; + + let vec = vec![ValueRef::String("v1"), ValueRef::String("v2")]; + + assert_eq!(ValueRef::Array(vec), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_array32() { + let buf = [ + 0xdd, + 0x00, 0x00, 0x00, 0x02, + 0xa2, 0x76, 0x31, + 0xa2, 0x76, 0x32, + ]; + let mut rd = &buf[..]; + + let vec = vec![ValueRef::String("v1"), ValueRef::String("v2")]; + + assert_eq!(ValueRef::Array(vec), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_pfix() { + let buf = [0x1f]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Integer(Integer::U64(31)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_nfix() { + let buf = [0xe0]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Integer(Integer::I64(-32)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_u8() { + let buf = [0xcc, 0xff]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Integer(Integer::U64(255)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_u16() { + let buf = [0xcd, 0xff, 0xff]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Integer(Integer::U64(65535)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_u32() { + let buf = [0xce, 0xff, 0xff, 0xff, 0xff]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Integer(Integer::U64(4294967295)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_u64() { + let buf = [0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Integer(Integer::U64(18446744073709551615u64)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_i8() { + let buf = [0xd0, 0x7f]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Integer(Integer::I64(127)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_i16() { + let buf = [0xd1, 0x7f, 0xff]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Integer(Integer::I64(32767)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_i32() { + let buf = [0xd2, 0x7f, 0xff, 0xff, 0xff]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Integer(Integer::I64(2147483647)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_i64() { + let buf = [0xd3, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Integer(Integer::I64(9223372036854775807)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_f32() { + let buf = [0xca, 0x7f, 0x7f, 0xff, 0xff]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Float(Float::F32(3.4028234e38_f32)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_f64() { + use std::f64; + + let buf = [0xcb, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Float(Float::F64(f64::INFINITY)), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_nil() { + let buf = [0xc0]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Nil, read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_bool_false() { + let buf = [0xc2]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Boolean(false), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_bool_true() { + let buf = [0xc3]; + + let mut rd = &buf[..]; + + assert_eq!(ValueRef::Boolean(true), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_null_read_twice() { + use std::io::Cursor; + + let buf = [0xc0, 0xc0]; + + let mut cur1 = Cursor::new(&buf[..]); + let v1 = read_value_ref(&mut cur1).unwrap(); + + let mut cur2 = Cursor::new(&buf[cur1.position() as usize..]); + let v2 = read_value_ref(&mut cur2).unwrap(); + + assert_eq!(ValueRef::Nil, v1); + assert_eq!(ValueRef::Nil, v2); +} + +#[test] +fn from_fixmap_using_cursor() { + use std::io::Cursor; + + let buf = [ + 0x82, // size: 2 + 0x2a, // 42 + 0xce, 0x0, 0x1, 0x88, 0x94, // 100500 + 0xa3, 0x6b, 0x65, 0x79, // 'key' + 0xa5, 0x76, 0x61, 0x6c, 0x75, 0x65 // 'value' + ]; + let mut rd = Cursor::new(&buf[..]); + + let map = vec![ + (ValueRef::Integer(Integer::U64(42)), ValueRef::Integer(Integer::U64(100500))), + (ValueRef::String("key"), ValueRef::String("value")), + ]; + let expected = ValueRef::Map(map); + + assert_eq!(expected, read_value_ref(&mut rd).unwrap()); + assert_eq!(17, rd.position()); +} + +// [None, 42, ['le message'], {'map': [True, {42: 100500}], 'key': 'value'}, [1, 2, 3], {'key': {'k1': 'v1'}}] +const COMPLEX_MSGPACK: [u8; 55] = [ + 0x96, 0xc0, 0x2a, 0x91, 0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x82, 0xa3, 0x6d, 0x61, 0x70, 0x92, 0xc3, 0x81, 0x2a, 0xce, 0x0, 0x1, 0x88, 0x94, 0xa3, + 0x6b, 0x65, 0x79, 0xa5, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x93, 0x1, 0x2, 0x3, 0x81, 0xa3, 0x6b, + 0x65, 0x79, 0x81, 0xa2, 0x6b, 0x31, 0xa2, 0x76, 0x31 +]; + +fn get_complex_msgpack_value<'a>() -> ValueRef<'a> { + ValueRef::Array(vec![ + ValueRef::Nil, + ValueRef::Integer(Integer::U64(42)), + ValueRef::Array(vec![ + ValueRef::String("le message"), + ]), + ValueRef::Map(vec![ + ( + ValueRef::String("map"), + ValueRef::Array(vec![ + ValueRef::Boolean(true), + ValueRef::Map(vec![ + ( + ValueRef::Integer(Integer::U64(42)), + ValueRef::Integer(Integer::U64(100500)) + ) + ]) + ]) + ), + ( + ValueRef::String("key"), + ValueRef::String("value") + ) + ]), + ValueRef::Array(vec![ + ValueRef::Integer(Integer::U64(1)), + ValueRef::Integer(Integer::U64(2)), + ValueRef::Integer(Integer::U64(3)), + ]), + ValueRef::Map(vec![ + ( + ValueRef::String("key"), + ValueRef::Map(vec![ + ( + ValueRef::String("k1"), + ValueRef::String("v1") + ) + ]) + ) + ]) + ]) +} + +#[test] +fn from_complex_value_using_slice() { + let buf = COMPLEX_MSGPACK; + let mut rd = &buf[..]; + + assert_eq!(get_complex_msgpack_value(), read_value_ref(&mut rd).unwrap()); +} + +#[test] +fn from_complex_value_using_cursor() { + use std::io::Cursor; + + let buf = COMPLEX_MSGPACK; + let mut rd = Cursor::new(&buf[..]); + + assert_eq!(get_complex_msgpack_value(), read_value_ref(&mut rd).unwrap()); + assert_eq!(buf.len() as u64, rd.position()); +} + +#[test] +fn from_reserved() { + let buf = [0xc1]; + + let mut rd = &buf[..]; + + match read_value_ref(&mut rd).err().unwrap() { + Error::TypeMismatch => (), + _ => panic!(), + } +} + +#[test] +fn into_owned() { + use msgpack::Value; + + let val = get_complex_msgpack_value(); + + let expected = Value::Array(vec![ + Value::Nil, + Value::Integer(Integer::U64(42)), + Value::Array(vec![ + Value::String("le message".to_string()), + ]), + Value::Map(vec![ + ( + Value::String("map".to_string()), + Value::Array(vec![ + Value::Boolean(true), + Value::Map(vec![ + ( + Value::Integer(Integer::U64(42)), + Value::Integer(Integer::U64(100500)) + ) + ]) + ]) + ), + ( + Value::String("key".to_string()), + Value::String("value".to_string()) + ) + ]), + Value::Array(vec![ + Value::Integer(Integer::U64(1)), + Value::Integer(Integer::U64(2)), + Value::Integer(Integer::U64(3)), + ]), + Value::Map(vec![ + ( + Value::String("key".to_string()), + Value::Map(vec![ + ( + Value::String("k1".to_string()), + Value::String("v1".to_string()) + ) + ]) + ) + ]) + ]); + + assert_eq!(expected, val.to_owned()); +} diff --git a/tests/func/encode/encoder.rs b/tests/func/encode/encoder.rs index e3d35db1..34cc9f79 100644 --- a/tests/func/encode/encoder.rs +++ b/tests/func/encode/encoder.rs @@ -308,3 +308,19 @@ fn pass_encodong_struct_into_vec() { assert_eq!(vec![0x92, 0x2a, 0xaa, 0x74, 0x68, 0x65, 0x20, 0x41, 0x6e, 0x73, 0x77, 0x65, 0x72], buf); } + +#[test] +fn encode_struct_with_string_using_vec() { + #[derive(Debug, PartialEq, RustcEncodable)] + struct Custom { + data: String, + } + + let mut buf = Vec::new(); + + let val = Custom { data: "le message".to_string() }; + val.encode(&mut Encoder::new(&mut buf)).ok().unwrap(); + + let out = vec![0x91, 0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65]; + assert_eq!(out, buf); +} diff --git a/tests/func/encode/mod.rs b/tests/func/encode/mod.rs index b199f321..c5f2c33f 100644 --- a/tests/func/encode/mod.rs +++ b/tests/func/encode/mod.rs @@ -11,3 +11,5 @@ mod ext; // High-level serialize interface. mod encoder; +mod value; +mod value_ref; diff --git a/tests/func/encode/value.rs b/tests/func/encode/value.rs new file mode 100644 index 00000000..59f2b350 --- /dev/null +++ b/tests/func/encode/value.rs @@ -0,0 +1,13 @@ +use msgpack::Value; +use msgpack::encode::value::write_value; + +#[test] +fn pack_nil() { + let mut buf = [0x00]; + + let val = Value::Nil; + + write_value(&mut &mut buf[..], &val).unwrap(); + + assert_eq!([0xc0], buf); +} diff --git a/tests/func/encode/value_ref.rs b/tests/func/encode/value_ref.rs new file mode 100644 index 00000000..c56d847f --- /dev/null +++ b/tests/func/encode/value_ref.rs @@ -0,0 +1,132 @@ +use msgpack::ValueRef; +use msgpack::value::{Float, Integer}; +use msgpack::encode::value_ref::write_value_ref; + +#[test] +fn pack_nil() { + let mut buf = [0x00]; + + let val = ValueRef::Nil; + + write_value_ref(&mut &mut buf[..], &val).unwrap(); + + assert_eq!([0xc0], buf); +} + +#[test] +fn pack_nil_when_buffer_is_tool_small() { + let mut buf = []; + + let val = ValueRef::Nil; + + match write_value_ref(&mut &mut buf[..], &val) { + Err(..) => (), + other => panic!("unexpected result: {:?}", other) + } +} + +#[test] +fn pass_pack_true() { + let mut buf = [0x00]; + + let val = ValueRef::Boolean(true); + + write_value_ref(&mut &mut buf[..], &val).unwrap(); + + assert_eq!([0xc3], buf); +} + +#[test] +fn pass_pack_uint_u16() { + let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + + let val = ValueRef::Integer(Integer::U64(65535)); + + write_value_ref(&mut &mut buf[..], &val).unwrap(); + + assert_eq!([0xcd, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], buf); +} + +#[test] +fn pass_pack_i64() { + let mut buf = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + + let val = ValueRef::Integer(Integer::I64(-9223372036854775808)); + + write_value_ref(&mut &mut buf[..], &val).unwrap(); + + assert_eq!([0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], buf); +} + +fn check_packed_eq(expected: &Vec, actual: &ValueRef) { + let mut buf = Vec::new(); + + write_value_ref(&mut buf, actual).unwrap(); + + assert_eq!(*expected, buf); +} + +#[test] +fn pass_pack_f32() { + check_packed_eq( + &vec![0xca, 0x7f, 0x7f, 0xff, 0xff], + &ValueRef::Float(Float::F32(3.4028234e38_f32)) + ); +} + +#[test] +fn pass_pack_f64() { + use std::f64; + check_packed_eq( + &vec![0xcb, 0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], + &ValueRef::Float(Float::F64(f64::INFINITY)) + ); +} + +#[test] +fn pass_pack_string() { + check_packed_eq( + &vec![0xaa, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65], + &ValueRef::String("le message") + ); +} + +#[test] +fn pass_pack_bin() { + check_packed_eq( + &vec![0xc4, 0x0a, 0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65], + &ValueRef::Binary(&[0x6c, 0x65, 0x20, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65]) + ); +} + +#[test] +fn pass_pack_array() { + check_packed_eq( + &vec![0x93, 0x01, 0x02, 0x03], + &ValueRef::Array( + vec![ + ValueRef::Integer(Integer::U64(1)), + ValueRef::Integer(Integer::U64(2)), + ValueRef::Integer(Integer::U64(3)) + ] + ) + ); +} + +#[test] +fn pass_pack_map() { + check_packed_eq( + &vec![0x81, 0x01, 0x02], + &ValueRef::Map( + vec![(ValueRef::Integer(Integer::U64(1)), ValueRef::Integer(Integer::U64(2)))] + ) + ); +} + +#[test] +fn pass_pack_ext() { + check_packed_eq( + &vec![0xc7, 0x03, 0x10, 0x01, 0x02, 0x03], + &ValueRef::Ext(16, &[0x01, 0x02, 0x03]) + ); +}