diff --git a/fuzz/.gitignore b/fuzz/.gitignore new file mode 100644 index 0000000..a092511 --- /dev/null +++ b/fuzz/.gitignore @@ -0,0 +1,3 @@ +target +corpus +artifacts diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml new file mode 100644 index 0000000..dece18a --- /dev/null +++ b/fuzz/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "msgpacker-fuzz" +version = "0.0.0" +authors = ["Automatically generated"] +publish = false +edition = "2018" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +arbitrary = { version = "1.1", features = ["derive"] } +libfuzzer-sys = "0.4" +msgpacker = { path = "../msgpacker" } + +[workspace] +members = ["."] + +[[bin]] +name = "messages" +path = "fuzz_targets/messages.rs" +test = false +doc = false diff --git a/fuzz/fuzz_targets/messages.rs b/fuzz/fuzz_targets/messages.rs new file mode 100644 index 0000000..8f80558 --- /dev/null +++ b/fuzz/fuzz_targets/messages.rs @@ -0,0 +1,44 @@ +#![no_main] +use libfuzzer_sys::fuzz_target; + +use arbitrary::Arbitrary; +use msgpacker::prelude::*; + +#[derive(Arbitrary, MsgPacker, Debug, Clone, PartialEq, Eq)] +pub struct AllMessages { + pub uint64: Option, + pub int64: Option, + pub b: Option, + pub f_32: Option, + pub f_64: Option, + pub string: Option, + pub bin: Option>, +} + +fuzz_target!(|m: AllMessages| { + let mut m = m; + + let buf: Vec = vec![]; + let mut packer = CursorPacker::new(buf); + + packer.pack(m.clone()).expect("failed to pack message"); + packer.set_position(0); + + let mut p: AllMessages = packer.unpack().expect("failed to unpack message"); + + // NaN equality doesn't hold + if m.f_32.filter(|f| f.is_nan()).is_some() { + m.f_32.take(); + } + if m.f_64.filter(|f| f.is_nan()).is_some() { + m.f_64.take(); + } + if p.f_32.filter(|f| f.is_nan()).is_some() { + p.f_32.take(); + } + if p.f_64.filter(|f| f.is_nan()).is_some() { + p.f_64.take(); + } + + assert_eq!(m, p); +}); diff --git a/msgpacker-derive/Cargo.toml b/msgpacker-derive/Cargo.toml index 6d0586b..13e85ca 100644 --- a/msgpacker-derive/Cargo.toml +++ b/msgpacker-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "msgpacker-derive" -version = "0.1.0" +version = "0.1.2" authors = ["Victor Lopez "] categories = ["compression", "encoding", "parser-implementations"] edition = "2021" diff --git a/msgpacker-derive/src/lib.rs b/msgpacker-derive/src/lib.rs index 336f06e..e81bf6d 100644 --- a/msgpacker-derive/src/lib.rs +++ b/msgpacker-derive/src/lib.rs @@ -16,7 +16,7 @@ pub fn msg_packer(input: TokenStream) -> TokenStream { let data = input.data; let mut values: Punctuated = Punctuated::new(); - let block: Block = match data { + let (block, block_size): (Block, Block) = match data { Data::Struct(syn::DataStruct { struct_token: _, fields: Fields::Named(f), @@ -25,17 +25,21 @@ pub fn msg_packer(input: TokenStream) -> TokenStream { .named .into_pairs() .map(|p| p.into_value()) - .fold(syn::parse_str("{}").unwrap(), |mut block, field| { + .fold((syn::parse_str("{}").unwrap(), syn::parse_str("{}").unwrap()), |(mut block, mut block_size), field| { let ident = field.ident.as_ref().cloned().unwrap(); let ty = field.ty; + block_size.stmts.push(parse_quote! { + n += <#ty as msgpacker::prelude::SizeableMessage>::packed_len(&self.#ident); + }); + block.stmts.push(parse_quote! { n += <#ty as msgpacker::prelude::Packable>::pack(&self.#ident, packer.by_ref())?; }); let fv = FieldValue { attrs: vec![], - member: Member::Named(ident.clone()), + member: Member::Named(ident), colon_token: Some(::default()), expr: parse_quote! { <#ty as msgpacker::prelude::Unpackable>::unpack(unpacker.by_ref())? @@ -43,12 +47,32 @@ pub fn msg_packer(input: TokenStream) -> TokenStream { }; values.push(fv); - block + (block, block_size) }), _ => todo!(), }; let expanded = quote! { + impl msgpacker::prelude::SizeableMessage for #name { + fn packed_len(&self) -> usize { + let mut n = 0; + + #block_size + + n + } + } + + impl<'a> msgpacker::prelude::SizeableMessage for &'a #name { + fn packed_len(&self) -> usize { + let mut n = 0; + + #block_size + + n + } + } + impl msgpacker::prelude::Packable for #name { fn pack(&self, mut packer: W) -> std::io::Result where @@ -62,6 +86,19 @@ pub fn msg_packer(input: TokenStream) -> TokenStream { } } + impl<'a> msgpacker::prelude::Packable for &'a #name { + fn pack(&self, mut packer: W) -> std::io::Result + where + W: std::io::Write + { + let mut n = 0; + + #block + + Ok(n) + } + } + impl msgpacker::prelude::Unpackable for #name { fn unpack(mut unpacker: R) -> std::io::Result where diff --git a/msgpacker/Cargo.toml b/msgpacker/Cargo.toml index 46f6e91..ae7872f 100644 --- a/msgpacker/Cargo.toml +++ b/msgpacker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "msgpacker" -version = "0.2.1" +version = "0.2.2" authors = ["Victor Lopez "] categories = ["compression", "encoding", "parser-implementations"] edition = "2021" diff --git a/msgpacker/src/buffer.rs b/msgpacker/src/buffer.rs index 5ad1580..c4d32e6 100644 --- a/msgpacker/src/buffer.rs +++ b/msgpacker/src/buffer.rs @@ -59,3 +59,18 @@ where pub const unsafe fn cast_fixed_array(array: [u8; M]) -> [u8; N] { *mem::transmute::<&[u8; M], &[u8; N]>(&array) } + +#[test] +fn take_buf_wont_panic_for_small_buf() { + use std::io::Read; + + const LEN: usize = 10; + + let mut cursor = io::Cursor::new([0u8; LEN]); + + let err = unsafe { take_buf(cursor.by_ref(), LEN + 1) } + .err() + .expect("buffer isn't big enough"); + + assert_eq!(io::ErrorKind::UnexpectedEof, err.kind()); +} diff --git a/msgpacker/src/consts.rs b/msgpacker/src/consts.rs new file mode 100644 index 0000000..0243874 --- /dev/null +++ b/msgpacker/src/consts.rs @@ -0,0 +1,11 @@ +/// Packable representation for variant `None` of [`Option`] +pub const OPTION_NONE: isize = 0x00; + +/// Packable representation for variant `Some` of [`Option`] +pub const OPTION_SOME: isize = 0x01; + +/// Packable representation for variant `Ok` of [`Result`] +pub const RESULT_OK: isize = 0x00; + +/// Packable representation for variant `Err` of [`Result`] +pub const RESULT_ERR: isize = 0x01; diff --git a/msgpacker/src/float.rs b/msgpacker/src/float.rs index bebb5d6..d6e2984 100644 --- a/msgpacker/src/float.rs +++ b/msgpacker/src/float.rs @@ -1,5 +1,6 @@ use crate::buffer; use crate::format::MessageFormat; +use crate::packer::SizeableMessage; use std::io; @@ -57,6 +58,18 @@ impl Float { } } + debug_assert_eq!(n, self.packed_len()); + Ok(n) } } + +impl SizeableMessage for Float { + fn packed_len(&self) -> usize { + match self { + Self::F32(_) => 5, + + Self::F64(_) => 9, + } + } +} diff --git a/msgpacker/src/format.rs b/msgpacker/src/format.rs index 1acd520..a5aa615 100644 --- a/msgpacker/src/format.rs +++ b/msgpacker/src/format.rs @@ -1,3 +1,5 @@ +use crate::packer::SizeableMessage; + /// [specs](https://github.com/msgpack/msgpack/blob/master/spec.md#formats) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum MessageFormat { @@ -77,6 +79,12 @@ pub enum MessageFormat { NegativeFixInt(i8), } +impl SizeableMessage for MessageFormat { + fn packed_len(&self) -> usize { + 1 + } +} + impl From for MessageFormat { fn from(b: u8) -> Self { use MessageFormat::*; diff --git a/msgpacker/src/integer.rs b/msgpacker/src/integer.rs index 4639d12..9f5e9c8 100644 --- a/msgpacker/src/integer.rs +++ b/msgpacker/src/integer.rs @@ -1,5 +1,6 @@ use crate::buffer; use crate::format::MessageFormat; +use crate::packer::SizeableMessage; use std::io; @@ -158,10 +159,48 @@ impl Integer { } } + debug_assert_eq!(n, self.packed_len()); + Ok(n) } } +impl SizeableMessage for Integer { + fn packed_len(&self) -> usize { + match self { + Self::Int64(i) if *i <= i32::MIN as i64 => 9, + + Self::Int64(i) if *i <= i16::MIN as i64 => 5, + + Self::Int64(i) if *i <= i8::MIN as i64 => 3, + + Self::Int64(i) if *i <= -33 => 2, + + Self::Int64(i) if *i <= -1 => 1, + + Self::Int64(i) if *i <= 127 => 1, + + Self::Uint64(i) if *i <= 127 => 1, + + Self::Int64(i) if *i <= u8::MAX as i64 => 2, + + Self::Uint64(i) if *i <= u8::MAX as u64 => 2, + + Self::Int64(i) if *i <= u16::MAX as i64 => 3, + + Self::Uint64(i) if *i <= u16::MAX as u64 => 3, + + Self::Int64(i) if *i <= u32::MAX as i64 => 5, + + Self::Uint64(i) if *i <= u32::MAX as u64 => 5, + + Self::Int64(_) => 9, + + Self::Uint64(_) => 9, + } + } +} + impl From for i64 { fn from(i: Integer) -> i64 { match i { diff --git a/msgpacker/src/lib.rs b/msgpacker/src/lib.rs index ef80e82..2d06cb5 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -11,6 +11,9 @@ mod message; mod message_ref; mod packer; +/// Preset constants +pub mod consts; + pub use message::Message; pub use message_ref::MessageRef; @@ -27,7 +30,10 @@ pub mod types { pub mod prelude { pub use crate::message::Message; pub use crate::message_ref::MessageRef; - pub use crate::packer::{CursorPacker, MessagePacker, MessageUnpacker, Packable, Unpackable}; + pub use crate::packer::{ + BufferedUnpacker, CursorPacker, MessagePacker, MessageUnpacker, Packable, SizeableMessage, + Unpackable, UnpackableIter, + }; pub use crate::types::*; #[cfg(feature = "derive")] diff --git a/msgpacker/src/message.rs b/msgpacker/src/message.rs index 01a1dd9..336a6d9 100644 --- a/msgpacker/src/message.rs +++ b/msgpacker/src/message.rs @@ -5,6 +5,7 @@ use crate::format::MessageFormat; use crate::integer::Integer; use crate::map::MapEntry; use crate::message_ref::MessageRef; +use crate::packer::SizeableMessage; use std::io; use std::ops::Index; @@ -112,10 +113,7 @@ impl Message { /// Return `true` if the message is nil pub const fn is_nil(&self) -> bool { - match self { - Self::Nil => true, - _ => false, - } + matches!(self, Self::Nil) } /// Create a new unsigned interger @@ -215,7 +213,7 @@ impl Message { MessageFormat::Int8 => { buffer::take(reader, &mut buf, 1)?; - let n = Integer::Int64(buf[0] as i64); + let n = Integer::Int64((buf[0] as i8) as i64); Ok(Self::Integer(n)) } @@ -759,6 +757,8 @@ impl Message { )), } + debug_assert_eq!(n, self.packed_len()); + Ok(n) } } @@ -770,11 +770,16 @@ impl> Index for Message { let i = i.into(); self.as_map() - .map(|m| { + .and_then(|m| { m.iter() .find_map(|m| if m.key() == &i { Some(m.val()) } else { None }) }) - .flatten() .unwrap_or(&Message::Nil) } } + +impl SizeableMessage for Message { + fn packed_len(&self) -> usize { + self.to_ref().packed_len() + } +} diff --git a/msgpacker/src/message_ref.rs b/msgpacker/src/message_ref.rs index 8741063..bbc6f5a 100644 --- a/msgpacker/src/message_ref.rs +++ b/msgpacker/src/message_ref.rs @@ -3,6 +3,7 @@ use crate::float::Float; use crate::format::MessageFormat; use crate::integer::Integer; use crate::map::MapEntryRef; +use crate::packer::SizeableMessage; use crate::{buffer, Message}; use std::ops::Index; @@ -103,10 +104,7 @@ impl<'a> MessageRef<'a> { /// Return `true` if the message is nil pub const fn is_nil(&self) -> bool { - match self { - Self::Nil => true, - _ => false, - } + matches!(self, Self::Nil) } /// Create a new unsigned interger @@ -173,7 +171,7 @@ impl<'a> MessageRef<'a> { MessageFormat::Int8 => { let buf = buffer::take_buf(reader, 1)?; - let n = Integer::Int64(buf[0] as i64); + let n = Integer::Int64((buf[0] as i8) as i64); Ok(Self::Integer(n)) } @@ -763,6 +761,8 @@ impl<'a> MessageRef<'a> { )), } + debug_assert_eq!(n, self.packed_len()); + Ok(n) } } @@ -777,11 +777,108 @@ impl<'a, 'b, M: Into>> Index for MessageRef<'a> { // Safety: self is bound to 'a let m: Option<&'a [MapEntryRef<'a>]> = unsafe { mem::transmute(m) }; - m.map(|m| { + m.and_then(|m| { m.iter() .find_map(|m| if m.key() == &i { Some(m.val()) } else { None }) }) - .flatten() .unwrap_or(&MessageRef::Nil) } } + +impl<'a> SizeableMessage for MessageRef<'a> { + fn packed_len(&self) -> usize { + match self { + Self::Nil => 1, + Self::Boolean(true) => 1, + Self::Boolean(false) => 1, + Self::Integer(i) => i.packed_len(), + Self::Float(f) => f.packed_len(), + + Self::String(s) if s.len() <= 31 => 1 + s.len(), + + Self::String(s) if s.len() <= u8::MAX as usize => 2 + s.len(), + + Self::String(s) if s.len() <= u16::MAX as usize => 3 + s.len(), + + Self::String(s) => 5 + s.len(), + + Self::Bin(b) if b.len() <= u8::MAX as usize => 2 + b.len(), + + Self::Bin(b) if b.len() <= u16::MAX as usize => 3 + b.len(), + + Self::Bin(b) => 5 + b.len(), + + Self::Array(a) if a.len() <= 15 => 1 + a.iter().map(Self::packed_len).sum::(), + + Self::Array(a) if a.len() <= u16::MAX as usize => { + 3 + a.iter().map(Self::packed_len).sum::() + } + + Self::Array(a) => 5 + a.iter().map(Self::packed_len).sum::(), + + Self::Map(m) if m.len() <= 15 => { + 1 + m + .iter() + .map(MapEntryRef::inner) + .map(|(k, v)| k.packed_len() + v.packed_len()) + .sum::() + } + + Self::Map(m) if m.len() <= u16::MAX as usize => { + 3 + m + .iter() + .map(MapEntryRef::inner) + .map(|(k, v)| k.packed_len() + v.packed_len()) + .sum::() + } + + Self::Map(m) => { + 4 + m + .iter() + .map(MapEntryRef::inner) + .map(|(k, v)| k.packed_len() + v.packed_len()) + .sum::() + } + + Self::Extension(ExtensionRef::FixExt1(_, _)) => 3, + + Self::Extension(ExtensionRef::Ext(_, e)) if e.len() == 1 => 3, + + Self::Extension(ExtensionRef::FixExt2(_, e)) => 2 + e.len(), + + Self::Extension(ExtensionRef::Ext(_, e)) if e.len() == 2 => 2 + e.len(), + + Self::Extension(ExtensionRef::FixExt4(_, e)) => 2 + e.len(), + + Self::Extension(ExtensionRef::Ext(_, e)) if e.len() == 4 => 2 + e.len(), + + Self::Extension(ExtensionRef::FixExt8(_, e)) => 2 + e.len(), + + Self::Extension(ExtensionRef::Ext(_, e)) if e.len() == 8 => 2 + e.len(), + + Self::Extension(ExtensionRef::FixExt16(_, e)) => 2 + e.len(), + + Self::Extension(ExtensionRef::Ext(_, e)) if e.len() == 16 => 2 + e.len(), + + Self::Extension(ExtensionRef::Ext(_, e)) if e.len() <= u8::MAX as usize => 3 + e.len(), + + Self::Extension(ExtensionRef::Ext(_, e)) if e.len() <= u16::MAX as usize => 4 + e.len(), + + Self::Extension(ExtensionRef::Ext(_, e)) => 6 + e.len(), + + Self::Extension(ExtensionRef::Timestamp(d)) + if d.as_secs() <= u32::MAX as u64 && d.subsec_nanos() == 0 => + { + 6 + } + + Self::Extension(ExtensionRef::Timestamp(d)) + if d.as_secs() < 1u64 << 34 && d.subsec_nanos() < 1u32 << 30 => + { + 10 + } + + Self::Extension(ExtensionRef::Timestamp(_)) => 15, + } + } +} diff --git a/msgpacker/src/packer.rs b/msgpacker/src/packer.rs index 6cb10aa..67be29e 100644 --- a/msgpacker/src/packer.rs +++ b/msgpacker/src/packer.rs @@ -1,9 +1,19 @@ +use std::fs; use std::io::{self, BufRead, Cursor, Read, Seek, SeekFrom, Write}; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; +use std::path::Path; mod impls; +/// Messages that have a defined packed length +pub trait SizeableMessage { + /// Packed length of the message + fn packed_len(&self) -> usize; +} + /// Define a type that can be packed into a writer. -pub trait Packable: Sized { +pub trait Packable: Sized + SizeableMessage { /// Pack the type into a writer fn pack(&self, packer: W) -> io::Result where @@ -24,6 +34,32 @@ pub trait MessagePacker { fn pack

(&mut self, package: P) -> io::Result where P: Packable; + + /// Use the packer by ref + fn by_ref(&mut self) -> &mut Self { + self + } + + /// Pack all items of the iterator + fn pack_all(&mut self, mut iter: I) -> io::Result + where + P: Packable, + I: Iterator, + { + iter.try_fold(0, |n, p| Ok(n + self.pack(p)?)) + } +} + +impl MessagePacker for &mut M +where + M: MessagePacker, +{ + fn pack

(&mut self, package: P) -> io::Result + where + P: Packable, + { + ::pack(self, package) + } } /// An unpacker implementation that can output messages @@ -32,6 +68,237 @@ pub trait MessageUnpacker { fn unpack

(&mut self) -> io::Result

where P: Unpackable; + + /// Use the unpacker by ref + fn by_ref(&mut self) -> &mut Self { + self + } +} + +impl MessageUnpacker for &mut M +where + M: MessageUnpacker, +{ + fn unpack

(&mut self) -> io::Result

+ where + P: Unpackable, + { + ::unpack(self) + } +} + +impl MessageUnpacker for io::BufReader +where + R: io::Read, +{ + fn unpack

(&mut self) -> io::Result

+ where + P: Unpackable, + { + P::unpack(self) + } +} + +/// A buffered packer implementation +#[derive(Debug)] +pub struct BufferedUnpacker { + buffer: io::BufReader, +} + +impl BufferedUnpacker +where + R: io::Read, +{ + /// Create a new instance from an implementation of [`io::Read`] + pub fn from_reader(reader: R) -> Self { + io::BufReader::new(reader).into() + } +} + +impl From> for BufferedUnpacker { + fn from(buffer: io::BufReader) -> Self { + Self { buffer } + } +} + +impl Deref for BufferedUnpacker { + type Target = io::BufReader; + + fn deref(&self) -> &Self::Target { + &self.buffer + } +} + +impl DerefMut for BufferedUnpacker { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.buffer + } +} + +impl Read for BufferedUnpacker +where + R: io::Read, +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.buffer.read(buf) + } +} + +impl io::Seek for BufferedUnpacker +where + R: io::Seek, +{ + fn seek(&mut self, pos: SeekFrom) -> io::Result { + self.buffer.seek(pos) + } +} + +impl io::BufRead for BufferedUnpacker +where + R: io::Read, +{ + fn fill_buf(&mut self) -> io::Result<&[u8]> { + self.buffer.fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.buffer.consume(amt) + } +} + +impl MessageUnpacker for BufferedUnpacker +where + R: io::Read, +{ + fn unpack

(&mut self) -> io::Result

+ where + P: Unpackable, + { + P::unpack(self) + } +} + +/// Iterator helper of unpackable items +/// +/// # Warning +/// +/// The iterator will provide messages until [`io::ErrorKind::UnexpectedEof`]. +/// +/// If EOF happens in the middle of a valid message, this case will be ignored and the iterator +/// will return `None`. This exception doesn't build a strong enough rule to compromise the +/// ergonomics of the struct. +/// +/// We might, in the future, create a `SizedMessageUnpacker` so we can trivially check if EOF +/// corresponds to the expected EOF. However, in most use-cases, the underlying buffer will be of +/// unknown size so this might not be used at all. +pub struct UnpackableIter +where + I: MessageUnpacker, + P: Unpackable, +{ + unpacker: I, + _package: PhantomData

, +} + +impl From for UnpackableIter +where + I: MessageUnpacker, + P: Unpackable, +{ + fn from(unpacker: I) -> Self { + Self { + unpacker, + _package: PhantomData, + } + } +} + +impl From for UnpackableIter, P> +where + R: Read, + P: Unpackable, +{ + fn from(reader: R) -> Self { + io::BufReader::new(reader).into() + } +} + +impl UnpackableIter +where + I: MessageUnpacker, + P: Unpackable, +{ + /// Unpack messages until EOF is hit + pub fn unpack_until_eof(&mut self) -> io::Result> + where + P: Unpackable, + { + self.try_fold(vec![], |mut v, p| { + v.push(p?); + + Ok(v) + }) + } +} + +impl

UnpackableIter, P> +where + P: Unpackable, +{ + /// Open a file as read-only to extract unpackable items + pub fn open_file(path: F) -> io::Result + where + F: AsRef, + { + fs::OpenOptions::new().read(true).open(path).map(Self::from) + } +} + +impl Deref for UnpackableIter +where + I: MessageUnpacker, + P: Unpackable, +{ + type Target = I; + + fn deref(&self) -> &Self::Target { + &self.unpacker + } +} + +impl DerefMut for UnpackableIter +where + I: MessageUnpacker, + P: Unpackable, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.unpacker + } +} + +impl Iterator for UnpackableIter +where + I: MessageUnpacker, + P: Unpackable, +{ + type Item = io::Result

; + + fn next(&mut self) -> Option { + match self.unpack::

() { + Ok(p) => Some(Ok(p)), + Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => None, + Err(e) => Some(Err(e)), + } + } +} + +impl MessagePacker for fs::File { + fn pack

(&mut self, package: P) -> io::Result + where + P: Packable, + { + package.pack(self) + } } /// A packer/unpacker implementation with an underlying [`Cursor`] @@ -40,6 +307,20 @@ pub struct CursorPacker { cursor: Cursor, } +impl Deref for CursorPacker { + type Target = Cursor; + + fn deref(&self) -> &Self::Target { + &self.cursor + } +} + +impl DerefMut for CursorPacker { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cursor + } +} + impl From> for CursorPacker { fn from(cursor: Cursor) -> Self { Self { cursor } @@ -59,6 +340,16 @@ impl CursorPacker { } } +impl CursorPacker> { + /// Read file into buffer + pub fn from_file

(path: P) -> io::Result + where + P: AsRef, + { + fs::read(path).map(Self::new) + } +} + impl Read for CursorPacker where B: AsRef<[u8]>, diff --git a/msgpacker/src/packer/impls.rs b/msgpacker/src/packer/impls.rs index bcc8351..1cd9ba9 100644 --- a/msgpacker/src/packer/impls.rs +++ b/msgpacker/src/packer/impls.rs @@ -1,11 +1,21 @@ +use crate::consts::*; use crate::packer::{Packable, Unpackable}; use crate::types::{Extension, ExtensionRef, Float, Integer, MapEntry, MapEntryRef}; use crate::{Message, MessageRef}; use std::io; +use std::marker::PhantomData; + +use super::SizeableMessage; macro_rules! packable { ($t:ty) => { + impl SizeableMessage for $t { + fn packed_len(&self) -> usize { + MessageRef::from(self).packed_len() + } + } + impl Packable for $t { fn pack(&self, mut packer: W) -> io::Result where @@ -28,6 +38,35 @@ macro_rules! packable { } macro_rules! packable_copy { + ($t:ty) => { + impl SizeableMessage for $t { + fn packed_len(&self) -> usize { + MessageRef::from(*self).packed_len() + } + } + + impl Packable for $t { + fn pack(&self, mut packer: W) -> io::Result + where + W: io::Write, + { + MessageRef::from(*self).pack(packer.by_ref()) + } + } + + impl Unpackable for $t { + fn unpack(mut unpacker: R) -> io::Result + where + R: io::BufRead, + { + // Safety: The temporary message reference lives long enough until deserialize + unsafe { MessageRef::unpack(unpacker.by_ref()) }.and_then(MessageRef::try_into) + } + } + }; +} + +macro_rules! packable_copy_without_sizeable { ($t:ty) => { impl Packable for $t { fn pack(&self, mut packer: W) -> io::Result @@ -50,15 +89,77 @@ macro_rules! packable_copy { }; } +macro_rules! packable_vec_copy { + ($t:ty) => { + impl SizeableMessage for Vec<$t> { + fn packed_len(&self) -> usize { + let arr = self + .iter() + .map(|i| MessageRef::from(*i)) + .collect::>>(); + + MessageRef::Array(arr).packed_len() + } + } + + impl Packable for Vec<$t> { + fn pack(&self, mut packer: W) -> io::Result + where + W: io::Write, + { + let arr = self + .iter() + .map(|i| MessageRef::from(*i)) + .collect::>>(); + + MessageRef::Array(arr).pack(packer.by_ref()) + } + } + + impl Unpackable for Vec<$t> { + fn unpack(mut unpacker: R) -> io::Result + where + R: io::BufRead, + { + // Safety: The temporary message reference lives long enough until deserialize + unsafe { MessageRef::unpack(unpacker.by_ref()) }? + .as_array() + .ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, "expected vector of messages") + })? + .into_iter() + .cloned() + .map(MessageRef::try_into) + .collect() + } + } + }; +} + macro_rules! packable_vec { ($t:ty) => { + impl SizeableMessage for Vec<$t> { + fn packed_len(&self) -> usize { + let arr = self + .iter() + .map(MessageRef::from) + .collect::>>(); + + MessageRef::Array(arr).packed_len() + } + } + impl Packable for Vec<$t> { fn pack(&self, mut packer: W) -> io::Result where W: io::Write, { - self.iter() - .try_fold(0, |s, m| Ok(s + m.pack(packer.by_ref())?)) + let arr = self + .iter() + .map(MessageRef::from) + .collect::>>(); + + MessageRef::Array(arr).pack(packer.by_ref()) } } @@ -82,7 +183,6 @@ macro_rules! packable_vec { }; } -packable_copy!(Integer); packable_copy!(u8); packable_copy!(u16); packable_copy!(u32); @@ -94,29 +194,30 @@ packable_copy!(i32); packable_copy!(i64); packable_copy!(isize); packable_copy!(bool); -packable_copy!(Float); packable_copy!(f32); packable_copy!(f64); packable_copy!(&str); packable_copy!(&[u8]); +packable_copy_without_sizeable!(Integer); +packable_copy_without_sizeable!(Float); packable!(String); packable!(Vec); packable!(Extension); -packable_vec!(Integer); -packable_vec!(u16); -packable_vec!(u32); -packable_vec!(u64); -packable_vec!(usize); -packable_vec!(i8); -packable_vec!(i16); -packable_vec!(i32); -packable_vec!(i64); -packable_vec!(isize); -packable_vec!(bool); -packable_vec!(Float); -packable_vec!(f32); -packable_vec!(f64); +packable_vec_copy!(Integer); +packable_vec_copy!(u16); +packable_vec_copy!(u32); +packable_vec_copy!(u64); +packable_vec_copy!(usize); +packable_vec_copy!(i8); +packable_vec_copy!(i16); +packable_vec_copy!(i32); +packable_vec_copy!(i64); +packable_vec_copy!(isize); +packable_vec_copy!(bool); +packable_vec_copy!(Float); +packable_vec_copy!(f32); +packable_vec_copy!(f64); packable_vec!(String); packable_vec!(Vec); packable_vec!(Extension); @@ -139,13 +240,28 @@ impl Unpackable for Message { } } +impl SizeableMessage for Vec { + fn packed_len(&self) -> usize { + let arr = self + .iter() + .map(Message::to_ref) + .collect::>>(); + + MessageRef::Array(arr).packed_len() + } +} + impl Packable for Vec { fn pack(&self, mut packer: W) -> io::Result where W: io::Write, { - self.iter() - .try_fold(0, |s, m| Ok(s + m.pack(packer.by_ref())?)) + let arr = self + .iter() + .map(Message::to_ref) + .collect::>>(); + + MessageRef::Array(arr).pack(packer.by_ref()) } } @@ -160,7 +276,7 @@ impl Unpackable for Vec { .ok_or_else(|| { io::Error::new(io::ErrorKind::InvalidData, "expected vector of messages") })? - .into_iter() + .iter() .cloned() .map(|m| m.into_owned()) .collect() @@ -458,152 +574,88 @@ impl<'a> From> for MessageRef<'a> { } } -macro_rules! packable_array { - ($n:expr) => { - impl From<[u8; $n]> for Message { - fn from(b: [u8; $n]) -> Self { - Self::from(b.to_vec()) - } - } +impl From<[u8; N]> for Message { + fn from(b: [u8; N]) -> Self { + Self::from(b.to_vec()) + } +} - impl<'a> From<&'a [u8; $n]> for MessageRef<'a> { - fn from(b: &'a [u8; $n]) -> Self { - MessageRef::from(&b[..]) - } - } +impl<'a, const N: usize> From<&'a [u8; N]> for MessageRef<'a> { + fn from(b: &'a [u8; N]) -> Self { + MessageRef::from(&b[..]) + } +} - impl TryFrom for [u8; $n] { - type Error = io::Error; +impl TryFrom for [u8; N] { + type Error = io::Error; - fn try_from(m: Message) -> Result<[u8; $n], Self::Error> { - m.as_bin() - .ok_or_else(|| { - io::Error::new(io::ErrorKind::InvalidData, "expected fixed array message") - }) - .and_then(|b| { - <[u8; $n]>::try_from(b).map_err(|_| { - io::Error::new( - io::ErrorKind::InvalidData, - "expected fixed array message - invalid len", - ) - }) - }) - } - } + fn try_from(m: Message) -> Result<[u8; N], Self::Error> { + m.as_bin() + .ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, "expected fixed array message") + }) + .and_then(|b| { + <[u8; N]>::try_from(b).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + "expected fixed array message - invalid len", + ) + }) + }) + } +} - impl<'a> TryFrom> for [u8; $n] { - type Error = io::Error; +impl<'a, const N: usize> TryFrom> for [u8; N] { + type Error = io::Error; - fn try_from(m: MessageRef<'a>) -> Result<[u8; $n], Self::Error> { - m.as_bin() - .ok_or_else(|| { - io::Error::new(io::ErrorKind::InvalidData, "expected fixed array message") - }) - .and_then(|b| { - <[u8; $n]>::try_from(b).map_err(|_| { - io::Error::new( - io::ErrorKind::InvalidData, - "expected fixed array message - invalid len", - ) - }) - }) - } - } + fn try_from(m: MessageRef<'a>) -> Result<[u8; N], Self::Error> { + m.as_bin() + .ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, "expected fixed array message") + }) + .and_then(|b| { + <[u8; N]>::try_from(b).map_err(|_| { + io::Error::new( + io::ErrorKind::InvalidData, + "expected fixed array message - invalid len", + ) + }) + }) + } +} - impl Packable for [u8; $n] { - fn pack(&self, mut packer: W) -> io::Result - where - W: io::Write, - { - MessageRef::from(&self[..]).pack(packer.by_ref()) - } - } +impl SizeableMessage for [u8; N] { + fn packed_len(&self) -> usize { + MessageRef::from(&self[..]).packed_len() + } +} - impl Unpackable for [u8; $n] { - fn unpack(mut unpacker: R) -> io::Result - where - R: io::BufRead, - { - // Safety: The temporary message reference lives long enough until deserialize - unsafe { MessageRef::unpack(unpacker.by_ref()) }.and_then(MessageRef::try_into) - } - } - }; +impl Packable for [u8; N] { + fn pack(&self, mut packer: W) -> io::Result + where + W: io::Write, + { + MessageRef::from(&self[..]).pack(packer.by_ref()) + } } -packable_array!(1); -packable_array!(2); -packable_array!(3); -packable_array!(4); -packable_array!(5); -packable_array!(6); -packable_array!(7); -packable_array!(8); -packable_array!(9); -packable_array!(10); -packable_array!(11); -packable_array!(12); -packable_array!(13); -packable_array!(14); -packable_array!(15); -packable_array!(16); -packable_array!(17); -packable_array!(18); -packable_array!(19); -packable_array!(20); -packable_array!(21); -packable_array!(22); -packable_array!(23); -packable_array!(24); -packable_array!(25); -packable_array!(26); -packable_array!(27); -packable_array!(28); -packable_array!(29); -packable_array!(30); -packable_array!(31); -packable_array!(32); -packable_array!(33); -packable_array!(34); -packable_array!(35); -packable_array!(36); -packable_array!(37); -packable_array!(38); -packable_array!(39); -packable_array!(40); -packable_array!(41); -packable_array!(42); -packable_array!(43); -packable_array!(44); -packable_array!(45); -packable_array!(46); -packable_array!(47); -packable_array!(48); -packable_array!(49); -packable_array!(50); -packable_array!(51); -packable_array!(52); -packable_array!(53); -packable_array!(54); -packable_array!(55); -packable_array!(56); -packable_array!(57); -packable_array!(58); -packable_array!(59); -packable_array!(60); -packable_array!(61); -packable_array!(62); -packable_array!(63); -packable_array!(64); +impl Unpackable for [u8; N] { + fn unpack(mut unpacker: R) -> io::Result + where + R: io::BufRead, + { + // Safety: The temporary message reference lives long enough until deserialize + unsafe { MessageRef::unpack(unpacker.by_ref()) }.and_then(MessageRef::try_into) + } +} impl TryFrom for Integer { type Error = io::Error; fn try_from(message: Message) -> Result { - message.as_integer().ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected integer message", - )) + message + .as_integer() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected integer message")) } } @@ -611,10 +663,9 @@ impl<'a> TryFrom> for Integer { type Error = io::Error; fn try_from(message: MessageRef<'a>) -> Result { - message.as_integer().ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected integer message", - )) + message + .as_integer() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected integer message")) } } @@ -625,10 +676,7 @@ impl TryFrom for u8 { message .as_integer() .map(|i| i.as_unsigned() as u8) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected byte message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected byte message")) } } @@ -639,10 +687,7 @@ impl<'a> TryFrom> for u8 { message .as_integer() .map(|i| i.as_unsigned() as u8) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected byte message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected byte message")) } } @@ -653,10 +698,7 @@ impl TryFrom for u16 { message .as_integer() .map(|i| i.as_unsigned() as u16) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected u16 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected u16 message")) } } @@ -667,10 +709,7 @@ impl<'a> TryFrom> for u16 { message .as_integer() .map(|i| i.as_unsigned() as u16) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected u16 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected u16 message")) } } @@ -681,10 +720,7 @@ impl TryFrom for u32 { message .as_integer() .map(|i| i.as_unsigned() as u32) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected u32 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected u32 message")) } } @@ -695,10 +731,7 @@ impl<'a> TryFrom> for u32 { message .as_integer() .map(|i| i.as_unsigned() as u32) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected u32 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected u32 message")) } } @@ -709,10 +742,7 @@ impl TryFrom for u64 { message .as_integer() .map(|i| i.as_unsigned() as u64) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected u64 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected u64 message")) } } @@ -723,10 +753,7 @@ impl<'a> TryFrom> for u64 { message .as_integer() .map(|i| i.as_unsigned() as u64) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected u64 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected u64 message")) } } @@ -737,10 +764,7 @@ impl TryFrom for usize { message .as_integer() .map(|i| i.as_unsigned() as usize) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected usize message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected usize message")) } } @@ -751,10 +775,7 @@ impl<'a> TryFrom> for usize { message .as_integer() .map(|i| i.as_unsigned() as usize) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected usize message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected usize message")) } } @@ -765,10 +786,9 @@ impl TryFrom for i8 { message .as_integer() .map(|i| i.as_signed() as i8) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected signed byte message", - )) + .ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, "expected signed byte message") + }) } } @@ -779,10 +799,9 @@ impl<'a> TryFrom> for i8 { message .as_integer() .map(|i| i.as_signed() as i8) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected signed byte message", - )) + .ok_or_else(|| { + io::Error::new(io::ErrorKind::InvalidData, "expected signed byte message") + }) } } @@ -793,10 +812,7 @@ impl TryFrom for i16 { message .as_integer() .map(|i| i.as_signed() as i16) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected i16 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected i16 message")) } } @@ -807,10 +823,7 @@ impl<'a> TryFrom> for i16 { message .as_integer() .map(|i| i.as_signed() as i16) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected i16 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected i16 message")) } } @@ -821,10 +834,7 @@ impl TryFrom for i32 { message .as_integer() .map(|i| i.as_signed() as i32) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected i32 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected i32 message")) } } @@ -835,10 +845,7 @@ impl<'a> TryFrom> for i32 { message .as_integer() .map(|i| i.as_signed() as i32) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected i32 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected i32 message")) } } @@ -849,10 +856,7 @@ impl TryFrom for i64 { message .as_integer() .map(|i| i.as_signed() as i64) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected i64 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected i64 message")) } } @@ -863,10 +867,7 @@ impl<'a> TryFrom> for i64 { message .as_integer() .map(|i| i.as_signed() as i64) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected i64 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected i64 message")) } } @@ -877,10 +878,7 @@ impl TryFrom for isize { message .as_integer() .map(|i| i.as_signed() as isize) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected isize message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected isize message")) } } @@ -891,10 +889,7 @@ impl<'a> TryFrom> for isize { message .as_integer() .map(|i| i.as_signed() as isize) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected isize message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected isize message")) } } @@ -902,10 +897,9 @@ impl TryFrom for bool { type Error = io::Error; fn try_from(message: Message) -> Result { - message.as_boolean().ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected bool message", - )) + message + .as_boolean() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected bool message")) } } @@ -913,10 +907,9 @@ impl<'a> TryFrom> for bool { type Error = io::Error; fn try_from(message: MessageRef<'a>) -> Result { - message.as_boolean().ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected bool message", - )) + message + .as_boolean() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected bool message")) } } @@ -924,10 +917,9 @@ impl TryFrom for Float { type Error = io::Error; fn try_from(message: Message) -> Result { - message.as_float().ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected float message", - )) + message + .as_float() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected float message")) } } @@ -935,10 +927,9 @@ impl<'a> TryFrom> for Float { type Error = io::Error; fn try_from(message: MessageRef<'a>) -> Result { - message.as_float().ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected float message", - )) + message + .as_float() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected float message")) } } @@ -949,10 +940,7 @@ impl TryFrom for f32 { message .as_float() .and_then(|f| f.as_f32()) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected f32 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected f32 message")) } } @@ -963,10 +951,7 @@ impl<'a> TryFrom> for f32 { message .as_float() .and_then(|f| f.as_f32()) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected f32 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected f32 message")) } } @@ -977,10 +962,7 @@ impl TryFrom for f64 { message .as_float() .and_then(|f| f.as_f64()) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected f64 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected f64 message")) } } @@ -991,10 +973,7 @@ impl<'a> TryFrom> for f64 { message .as_float() .and_then(|f| f.as_f64()) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected f64 message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected f64 message")) } } @@ -1005,10 +984,7 @@ impl TryFrom for String { message .as_string() .map(|s| s.to_owned()) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected string message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected string message")) } } @@ -1016,10 +992,9 @@ impl<'a> TryFrom> for &'a str { type Error = io::Error; fn try_from(message: MessageRef<'a>) -> Result { - message.as_string().ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected string message", - )) + message + .as_string() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected string message")) } } @@ -1030,10 +1005,7 @@ impl<'a> TryFrom> for String { message .as_string() .map(|s| s.to_owned()) - .ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected string message", - )) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected string message")) } } @@ -1041,10 +1013,10 @@ impl TryFrom for Vec { type Error = io::Error; fn try_from(message: Message) -> Result { - message.as_bin().map(|s| s.to_owned()).ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected binary message", - )) + message + .as_bin() + .map(|s| s.to_owned()) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected binary message")) } } @@ -1052,10 +1024,9 @@ impl<'a> TryFrom> for &'a [u8] { type Error = io::Error; fn try_from(message: MessageRef<'a>) -> Result { - message.as_bin().ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected binary message", - )) + message + .as_bin() + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected binary message")) } } @@ -1063,10 +1034,10 @@ impl<'a> TryFrom> for Vec { type Error = io::Error; fn try_from(message: MessageRef<'a>) -> Result { - message.as_bin().map(|s| s.to_owned()).ok_or(io::Error::new( - io::ErrorKind::InvalidData, - "expected binary message", - )) + message + .as_bin() + .map(|s| s.to_owned()) + .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "expected binary message")) } } @@ -1115,3 +1086,204 @@ impl<'a> FromIterator> for MessageRef<'a> { iter.into_iter().collect::>>().into() } } + +impl SizeableMessage for () { + fn packed_len(&self) -> usize { + 0 + } +} + +impl Packable for () { + fn pack(&self, _packer: W) -> io::Result + where + W: io::Write, + { + Ok(0) + } +} + +impl SizeableMessage for PhantomData { + fn packed_len(&self) -> usize { + 0 + } +} + +impl Packable for PhantomData { + fn pack(&self, _packer: W) -> io::Result + where + W: io::Write, + { + Ok(0) + } +} + +impl SizeableMessage for Option +where + T: SizeableMessage, +{ + fn packed_len(&self) -> usize { + match self { + Some(t) => OPTION_SOME.packed_len() + t.packed_len(), + None => OPTION_NONE.packed_len(), + } + } +} + +impl Packable for Option +where + T: Packable, +{ + fn pack(&self, mut packer: W) -> io::Result + where + W: io::Write, + { + match self { + Some(t) => Ok(OPTION_SOME.pack(packer.by_ref())? + t.pack(packer.by_ref())?), + None => OPTION_NONE.pack(packer.by_ref()), + } + } +} + +impl SizeableMessage for Result +where + T: SizeableMessage, + E: SizeableMessage, +{ + fn packed_len(&self) -> usize { + match self { + Ok(t) => RESULT_OK.packed_len() + t.packed_len(), + Err(e) => RESULT_ERR.packed_len() + e.packed_len(), + } + } +} + +impl Packable for Result +where + T: Packable, + E: Packable, +{ + fn pack(&self, mut packer: W) -> io::Result + where + W: io::Write, + { + match self { + Ok(t) => Ok(RESULT_OK.pack(packer.by_ref())? + t.pack(packer.by_ref())?), + Err(e) => Ok(RESULT_ERR.pack(packer.by_ref())? + e.pack(packer.by_ref())?), + } + } +} + +impl Unpackable for () { + fn unpack(_unpacker: R) -> io::Result + where + R: io::BufRead, + { + Ok(()) + } +} + +impl Unpackable for PhantomData { + fn unpack(_unpacker: R) -> io::Result + where + R: io::BufRead, + { + Ok(PhantomData) + } +} + +impl Unpackable for Option +where + T: Unpackable, +{ + fn unpack(mut unpacker: R) -> io::Result + where + R: io::BufRead, + { + ::unpack(unpacker.by_ref()).and_then(|r| match r { + OPTION_SOME => Some(T::unpack(unpacker.by_ref())).transpose(), + OPTION_NONE => Ok(None), + _ => Err(io::Error::new( + io::ErrorKind::InvalidData, + "expected option representation", + )), + }) + } +} + +impl Unpackable for Result +where + T: Unpackable, + E: Unpackable, +{ + fn unpack(mut unpacker: R) -> io::Result + where + R: io::BufRead, + { + ::unpack(unpacker.by_ref()).and_then(|r| match r { + RESULT_OK => T::unpack(unpacker.by_ref()).map(|t| Ok(t)), + RESULT_ERR => E::unpack(unpacker.by_ref()).map(|e| Err(e)), + _ => Err(io::Error::new( + io::ErrorKind::InvalidData, + "expected result representation", + )), + }) + } +} + +macro_rules! tuple { + ( $($name:ident)+) => ( + #[allow(non_snake_case)] + impl<$($name,)+> SizeableMessage for ($($name,)+) + where $($name: SizeableMessage,)+ + { + fn packed_len(&self) -> usize { + let ($(ref $name,)+) = *self; + + 0 $( + $name.packed_len())+ + } + } + + #[allow(non_snake_case)] + impl<$($name,)+> Packable for ($($name,)+) + where $($name: Packable,)+ + { + fn pack(&self, mut packer: W) -> io::Result + where + W: io::Write, + { + let ($(ref $name,)+) = *self; + + Ok(0 $( + $name.pack(packer.by_ref())?)+) + } + } + + #[allow(non_snake_case)] + impl<$($name,)+> Unpackable for ($($name,)+) + where $($name: Unpackable,)+ + { + fn unpack(mut unpacker: R) -> io::Result + where + R: io::BufRead, + { + Ok(($($name::unpack(unpacker.by_ref())?,)+)) + } + } + ); +} + +tuple! { A } +tuple! { A B } +tuple! { A B C } +tuple! { A B C D } +tuple! { A B C D E } +tuple! { A B C D E F } +tuple! { A B C D E F G } +tuple! { A B C D E F G H } +tuple! { A B C D E F G H I } +tuple! { A B C D E F G H I J } +tuple! { A B C D E F G H I J K } +tuple! { A B C D E F G H I J K L } +tuple! { A B C D E F G H I J K L M } +tuple! { A B C D E F G H I J K L M N } +tuple! { A B C D E F G H I J K L M N O } +tuple! { A B C D E F G H I J K L M N O P } diff --git a/msgpacker/tests/pack_unpack.rs b/msgpacker/tests/pack_unpack.rs index bbb98f7..4de5aeb 100644 --- a/msgpacker/tests/pack_unpack.rs +++ b/msgpacker/tests/pack_unpack.rs @@ -19,6 +19,7 @@ fn pack_unpack() -> io::Result<()> { Message::Integer(Integer::signed(i16::MIN as i64)), Message::Integer(Integer::signed(i8::MIN as i64 - 1)), Message::Integer(Integer::signed(i8::MIN as i64)), + Message::Integer(Integer::signed(-33)), Message::Integer(Integer::signed(-32)), Message::Integer(Integer::signed(-1)), Message::Integer(Integer::unsigned(0u64)),