From f360de9b1316fcb929771007ef23a4c3e8f2dad3 Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 16 Mar 2025 19:21:01 -0500 Subject: [PATCH 01/14] Start serializing more like other libraries --- msgpacker-derive/src/lib.rs | 10 +++++++--- msgpacker/Cargo.toml | 4 +++- msgpacker/src/lib.rs | 2 +- msgpacker/src/pack/collections.rs | 33 ++++++++++++++++++++----------- msgpacker/src/pack/mod.rs | 2 +- 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/msgpacker-derive/src/lib.rs b/msgpacker-derive/src/lib.rs index 401cdd0..086403b 100644 --- a/msgpacker-derive/src/lib.rs +++ b/msgpacker-derive/src/lib.rs @@ -36,9 +36,11 @@ fn contains_attribute(field: &Field, name: &str) -> bool { fn impl_fields_named(name: Ident, f: FieldsNamed) -> impl Into { let mut values: Punctuated = Punctuated::new(); + let field_len = f.named.len(); let block_packable: Block = parse_quote! { { let mut n = 0; + n += ::msgpacker::get_array_info(buf, #field_len); } }; let block_unpackable: Block = parse_quote! { @@ -103,7 +105,9 @@ fn impl_fields_named(name: Ident, f: FieldsNamed) -> impl Into { t })?; }); - } else if contains_attribute(&field, "array") || is_vec && !is_vec_u8 { + // todo, delete? + // } else if contains_attribute(&field, "array") || is_vec && !is_vec_u8 { + } else if contains_attribute(&field, "array") || is_vec { block_packable.stmts.push(parse_quote! { n += ::msgpacker::pack_array(buf, &self.#ident); }); @@ -298,11 +302,11 @@ fn impl_fields_unnamed(name: Ident, f: FieldsUnnamed) -> impl Into fn impl_fields_unit(name: Ident) -> impl Into { quote! { impl ::msgpacker::Packable for #name { - fn pack(&self, _buf: &mut T) -> usize + fn pack(&self, buf: &mut T) -> usize where T: Extend, { - 0 + ::msgpacker::get_array_info(buf, 0) } } diff --git a/msgpacker/Cargo.toml b/msgpacker/Cargo.toml index aaa8549..9263f7e 100644 --- a/msgpacker/Cargo.toml +++ b/msgpacker/Cargo.toml @@ -11,7 +11,9 @@ repository = "https://github.com/codx-dev/msgpacker" description = "MessagePack protocol implementation for Rust." [dependencies] -msgpacker-derive = { version = "0.3", optional = true } +# todo, revert +# msgpacker-derive = { version = "0.3", optional = true } +msgpacker-derive = { version = "0.3", path = "../msgpacker-derive", optional = true } [dev-dependencies] proptest = "1.2" diff --git a/msgpacker/src/lib.rs b/msgpacker/src/lib.rs index 845444d..fddabaa 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -16,7 +16,7 @@ mod unpack; pub use error::Error; use format::Format; -pub use pack::{pack_array, pack_map}; +pub use pack::{pack_array, pack_map, get_array_info}; pub use unpack::{unpack_array, unpack_array_iter, unpack_map, unpack_map_iter}; #[cfg(feature = "alloc")] diff --git a/msgpacker/src/pack/collections.rs b/msgpacker/src/pack/collections.rs index bafee5f..97aac3a 100644 --- a/msgpacker/src/pack/collections.rs +++ b/msgpacker/src/pack/collections.rs @@ -1,18 +1,12 @@ use super::{Format, Packable}; use core::{borrow::Borrow, iter}; -/// Packs an array into the extendable buffer, returning the amount of written bytes. -#[allow(unreachable_code)] -pub fn pack_array(buf: &mut T, iter: A) -> usize -where - T: Extend, - A: IntoIterator, - I: Iterator + ExactSizeIterator, - V: Packable, +/// Writes the info for an array into the extendable buffer, returning the amount of written bytes. +pub fn get_array_info(buf: &mut T, len: usize) -> usize + where + T: Extend { - let values = iter.into_iter(); - let len = values.len(); - let n = if len <= 15 { + if len <= 15 { buf.extend(iter::once(((len & 0x0f) as u8) | 0x90)); 1 } else if len <= u16::MAX as usize { @@ -25,7 +19,22 @@ where #[cfg(feature = "strict")] panic!("strict serialization enabled; the buffer is too large"); return 0; - }; + } +} + +/// Packs an array into the extendable buffer, returning the amount of written bytes. +#[allow(unreachable_code)] +pub fn pack_array(buf: &mut T, iter: A) -> usize +where + T: Extend, + A: IntoIterator, + I: Iterator + ExactSizeIterator, + V: Packable, +{ + let values = iter.into_iter(); + let len = values.len(); + + let n = get_array_info(buf, len); n + values.map(|v| v.pack(buf)).sum::() } diff --git a/msgpacker/src/pack/mod.rs b/msgpacker/src/pack/mod.rs index d31ce85..55525f8 100644 --- a/msgpacker/src/pack/mod.rs +++ b/msgpacker/src/pack/mod.rs @@ -6,4 +6,4 @@ mod common; mod float; mod int; -pub use collections::{pack_array, pack_map}; +pub use collections::{pack_array, pack_map, get_array_info}; From 0407f2d67d5fef1d6bffee2be1550974c6a13aad Mon Sep 17 00:00:00 2001 From: computermouth Date: Mon, 17 Mar 2025 17:06:44 -0500 Subject: [PATCH 02/14] Fix u8 packing, add optional binary annotation --- msgpacker-derive/src/lib.rs | 21 +++++++++++-- msgpacker/src/lib.rs | 2 +- msgpacker/src/pack/binary.rs | 58 ++++++++++++++++-------------------- msgpacker/src/pack/mod.rs | 1 + rust-toolchain.toml | 2 ++ 5 files changed, 48 insertions(+), 36 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/msgpacker-derive/src/lib.rs b/msgpacker-derive/src/lib.rs index 086403b..10ae0c2 100644 --- a/msgpacker-derive/src/lib.rs +++ b/msgpacker-derive/src/lib.rs @@ -105,8 +105,25 @@ fn impl_fields_named(name: Ident, f: FieldsNamed) -> impl Into { t })?; }); - // todo, delete? - // } else if contains_attribute(&field, "array") || is_vec && !is_vec_u8 { + } else if contains_attribute(&field, "binary") && is_vec_u8 { + block_packable.stmts.push(parse_quote! { + n += ::msgpacker::pack_binary(buf, &self.#ident); + }); + + block_unpackable.stmts.push(parse_quote! { + let #ident = ::msgpacker::unpack_array(buf).map(|(nv, t)| { + n += nv; + buf = &buf[nv..]; + t + })?; + }); + + block_unpackable_iter.stmts.push(parse_quote! { + let #ident = ::msgpacker::unpack_array_iter(bytes.by_ref()).map(|(nv, t)| { + n += nv; + t + })?; + }); } else if contains_attribute(&field, "array") || is_vec { block_packable.stmts.push(parse_quote! { n += ::msgpacker::pack_array(buf, &self.#ident); diff --git a/msgpacker/src/lib.rs b/msgpacker/src/lib.rs index fddabaa..1290c19 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -16,7 +16,7 @@ mod unpack; pub use error::Error; use format::Format; -pub use pack::{pack_array, pack_map, get_array_info}; +pub use pack::{pack_array, pack_map, get_array_info, pack_binary}; pub use unpack::{unpack_array, unpack_array_iter, unpack_map, unpack_map_iter}; #[cfg(feature = "alloc")] diff --git a/msgpacker/src/pack/binary.rs b/msgpacker/src/pack/binary.rs index 01f1d2c..d857df9 100644 --- a/msgpacker/src/pack/binary.rs +++ b/msgpacker/src/pack/binary.rs @@ -1,29 +1,30 @@ use super::{Format, Packable}; use core::iter; -impl Packable for [u8] { - #[allow(unreachable_code)] - fn pack(&self, buf: &mut T) -> usize - where - T: Extend, - { - let n = if self.len() <= u8::MAX as usize { - buf.extend(iter::once(Format::BIN8).chain(iter::once(self.len() as u8))); - 2 - } else if self.len() <= u16::MAX as usize { - buf.extend(iter::once(Format::BIN16).chain((self.len() as u16).to_be_bytes())); - 3 - } else if self.len() <= u32::MAX as usize { - buf.extend(iter::once(Format::BIN32).chain((self.len() as u32).to_be_bytes())); - 5 - } else { - #[cfg(feature = "strict")] - panic!("strict serialization enabled; the buffer is too large"); - return 0; - }; - buf.extend(self.iter().copied()); - n + self.len() - } +/// Packs a u8 array as binary data into the extendable buffer, returning the amount of written bytes. +#[allow(unreachable_code)] +pub fn pack_binary(buf: &mut T, data: &[u8]) -> usize +where + T: Extend, +{ + let len = data.len(); + + let n = if len <= u8::MAX as usize { + buf.extend(iter::once(Format::BIN8).chain(iter::once(len as u8))); + 2 + } else if len <= u16::MAX as usize { + buf.extend(iter::once(Format::BIN16).chain(len.to_be_bytes())); + 3 + } else if len <= u32::MAX as usize { + buf.extend(iter::once(Format::BIN32).chain(len.to_be_bytes())); + 5 + } else { + #[cfg(feature = "strict")] + panic!("strict serialization enabled; the buffer is too large"); + return 0; + }; + buf.extend(data.iter().copied()); + n + len } #[allow(unreachable_code)] @@ -57,16 +58,7 @@ impl Packable for str { #[cfg(feature = "alloc")] mod alloc { use super::*; - use ::alloc::{string::String, vec::Vec}; - - impl Packable for Vec { - fn pack(&self, buf: &mut T) -> usize - where - T: Extend, - { - self.as_slice().pack(buf) - } - } + use ::alloc::string::String; impl Packable for String { fn pack(&self, buf: &mut T) -> usize diff --git a/msgpacker/src/pack/mod.rs b/msgpacker/src/pack/mod.rs index 55525f8..f64ce4c 100644 --- a/msgpacker/src/pack/mod.rs +++ b/msgpacker/src/pack/mod.rs @@ -6,4 +6,5 @@ mod common; mod float; mod int; +pub use binary::pack_binary; pub use collections::{pack_array, pack_map, get_array_info}; diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..271800c --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" \ No newline at end of file From a875df0b9b5f2caa57549622910e0c60449df9d4 Mon Sep 17 00:00:00 2001 From: computermouth Date: Mon, 17 Mar 2025 17:58:13 -0500 Subject: [PATCH 03/14] unpack now expecting the top level array with a packed struct --- msgpacker-derive/src/lib.rs | 28 ++++++++++++++-- msgpacker/src/error.rs | 2 ++ msgpacker/src/lib.rs | 5 +-- msgpacker/src/unpack/binary.rs | 60 +++++++++++++++------------------- msgpacker/src/unpack/mod.rs | 1 + 5 files changed, 58 insertions(+), 38 deletions(-) diff --git a/msgpacker-derive/src/lib.rs b/msgpacker-derive/src/lib.rs index 10ae0c2..7b45818 100644 --- a/msgpacker-derive/src/lib.rs +++ b/msgpacker-derive/src/lib.rs @@ -46,6 +46,28 @@ fn impl_fields_named(name: Ident, f: FieldsNamed) -> impl Into { let block_unpackable: Block = parse_quote! { { let mut n = 0; + let expected_len = #field_len; + + let format = ::msgpacker::take_byte(&mut buf)?; + + let (header_bytes, actual_len) = match format { + 0x90..=0x9f => (1, (format & 0x0f) as usize), + ::msgpacker::Format::ARRAY16 => { + let len = ::msgpacker::take_num(&mut buf, u16::from_be_bytes)? as usize; + (3, len) + } + ::msgpacker::Format::ARRAY32 => { + let len = ::msgpacker::take_num(&mut buf, u32::from_be_bytes)? as usize; + (5, len) + } + _ => return Err(::msgpacker::Error::UnexpectedFormatTag.into()), + }; + + if actual_len != expected_len { + return Err(::msgpacker::Error::UnexpectedStructLength.into()); + } + + n += header_bytes; } }; let block_unpackable_iter: Block = parse_quote! { @@ -111,15 +133,15 @@ fn impl_fields_named(name: Ident, f: FieldsNamed) -> impl Into { }); block_unpackable.stmts.push(parse_quote! { - let #ident = ::msgpacker::unpack_array(buf).map(|(nv, t)| { + let #ident = ::msgpacker::unpack_bytes(buf).map(|(nv, t)| { n += nv; buf = &buf[nv..]; - t + t.to_vec() })?; }); block_unpackable_iter.stmts.push(parse_quote! { - let #ident = ::msgpacker::unpack_array_iter(bytes.by_ref()).map(|(nv, t)| { + let #ident = ::msgpacker::unpack_bytes_iter(bytes.by_ref()).map(|(nv, t)| { n += nv; t })?; diff --git a/msgpacker/src/error.rs b/msgpacker/src/error.rs index 16e3ed4..08fc582 100644 --- a/msgpacker/src/error.rs +++ b/msgpacker/src/error.rs @@ -15,6 +15,8 @@ pub enum Error { UnexpectedFormatTag, /// The provided bin length is not valid. UnexpectedBinLength, + /// The struct we're targeting does not match the data. + UnexpectedStructLength, } impl fmt::Display for Error { diff --git a/msgpacker/src/lib.rs b/msgpacker/src/lib.rs index 1290c19..a560c6f 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -15,9 +15,10 @@ mod pack; mod unpack; pub use error::Error; -use format::Format; +pub use format::Format; +pub use helpers::{take_byte, take_num}; pub use pack::{pack_array, pack_map, get_array_info, pack_binary}; -pub use unpack::{unpack_array, unpack_array_iter, unpack_map, unpack_map_iter}; +pub use unpack::{unpack_array, unpack_array_iter, unpack_map, unpack_map_iter, unpack_bytes, unpack_bytes_iter}; #[cfg(feature = "alloc")] pub use extension::Extension; diff --git a/msgpacker/src/unpack/binary.rs b/msgpacker/src/unpack/binary.rs index b49bf7a..9687b9b 100644 --- a/msgpacker/src/unpack/binary.rs +++ b/msgpacker/src/unpack/binary.rs @@ -10,6 +10,7 @@ use super::{ use alloc::{string::String, vec::Vec}; use core::str; +/// Unpacks binary data from the buffer, returning a &[u8] and the amount of read bytes. pub fn unpack_bytes(mut buf: &[u8]) -> Result<(usize, &[u8]), Error> { let format = take_byte(&mut buf)?; let (n, len) = match format { @@ -24,6 +25,32 @@ pub fn unpack_bytes(mut buf: &[u8]) -> Result<(usize, &[u8]), Error> { Ok((n + len, &buf[..len])) } +/// Unpacks binary data from the iterator, returning a Vec and the amount of read bytes. +pub fn unpack_bytes_iter(bytes: I) -> Result<(usize, Vec), Error> +where + I: IntoIterator, +{ + let mut bytes = bytes.into_iter(); + let format = take_byte_iter(bytes.by_ref())?; + let (n, len) = match format { + Format::BIN8 => (2, take_byte_iter(bytes.by_ref())? as usize), + Format::BIN16 => ( + 3, + take_num_iter(bytes.by_ref(), u16::from_be_bytes)? as usize, + ), + Format::BIN32 => ( + 5, + take_num_iter(bytes.by_ref(), u32::from_be_bytes)? as usize, + ), + _ => return Err(Error::UnexpectedFormatTag), + }; + let v: Vec<_> = bytes.take(len).collect(); + if v.len() < len { + return Err(Error::BufferTooShort); + } + Ok((n + len, v)) +} + pub fn unpack_str(mut buf: &[u8]) -> Result<(usize, &str), Error> { let format = take_byte(&mut buf)?; let (n, len) = match format { @@ -40,39 +67,6 @@ pub fn unpack_str(mut buf: &[u8]) -> Result<(usize, &str), Error> { Ok((n + len, str)) } -impl Unpackable for Vec { - type Error = Error; - - fn unpack(buf: &[u8]) -> Result<(usize, Self), Self::Error> { - unpack_bytes(buf).map(|(n, b)| (n, b.to_vec())) - } - - fn unpack_iter(bytes: I) -> Result<(usize, Self), Self::Error> - where - I: IntoIterator, - { - let mut bytes = bytes.into_iter(); - let format = take_byte_iter(bytes.by_ref())?; - let (n, len) = match format { - Format::BIN8 => (2, take_byte_iter(bytes.by_ref())? as usize), - Format::BIN16 => ( - 3, - take_num_iter(bytes.by_ref(), u16::from_be_bytes)? as usize, - ), - Format::BIN32 => ( - 5, - take_num_iter(bytes.by_ref(), u32::from_be_bytes)? as usize, - ), - _ => return Err(Error::UnexpectedFormatTag), - }; - let v: Vec<_> = bytes.take(len).collect(); - if v.len() < len { - return Err(Error::BufferTooShort); - } - Ok((n + len, v)) - } -} - impl Unpackable for String { type Error = Error; diff --git a/msgpacker/src/unpack/mod.rs b/msgpacker/src/unpack/mod.rs index 5f20331..143b9a6 100644 --- a/msgpacker/src/unpack/mod.rs +++ b/msgpacker/src/unpack/mod.rs @@ -7,4 +7,5 @@ mod common; mod float; mod int; +pub use binary::{unpack_bytes, unpack_bytes_iter}; pub use collections::{unpack_array, unpack_array_iter, unpack_map, unpack_map_iter}; From 0577b5afa03ebd7a7c70afe84a270938aeba5704 Mon Sep 17 00:00:00 2001 From: computermouth Date: Mon, 17 Mar 2025 20:07:37 -0500 Subject: [PATCH 04/14] A little bit of docs --- msgpacker/src/format.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/msgpacker/src/format.rs b/msgpacker/src/format.rs index 7345495..986ed9b 100644 --- a/msgpacker/src/format.rs +++ b/msgpacker/src/format.rs @@ -1,40 +1,72 @@ pub struct Format {} impl Format { + /// Nil format stores nil in 1 byte. pub const NIL: u8 = 0xc0; + /// Bool format family stores false or true in 1 byte. pub const TRUE: u8 = 0xc3; + /// Bool format family stores false or true in 1 byte. pub const FALSE: u8 = 0xc2; + /// Positive fixint stores 7-bit positive integer pub const POSITIVE_FIXINT: u8 = 0x7f; + /// Uint 8 stores a 8-bit unsigned integer pub const UINT8: u8 = 0xcc; + /// Uint 16 stores a 16-bit big-endian unsigned integer pub const UINT16: u8 = 0xcd; + /// Uint 32 stores a 32-bit big-endian unsigned integer pub const UINT32: u8 = 0xce; + /// Uint 64 stores a 64-bit big-endian unsigned integer pub const UINT64: u8 = 0xcf; + /// Int 8 stores a 8-bit signed integer pub const INT8: u8 = 0xd0; + /// Int 16 stores a 16-bit big-endian signed integer pub const INT16: u8 = 0xd1; + /// Int 32 stores a 32-bit big-endian signed integer pub const INT32: u8 = 0xd2; + /// Int 64 stores a 64-bit big-endian signed integer pub const INT64: u8 = 0xd3; + /// Float 32 stores a floating point number in IEEE 754 single precision floating point number pub const FLOAT32: u8 = 0xca; + /// Float 64 stores a floating point number in IEEE 754 double precision floating point number pub const FLOAT64: u8 = 0xcb; + /// Bin 8 stores a byte array whose length is upto (2^8)-1 bytes pub const BIN8: u8 = 0xc4; + /// Bin 16 stores a byte array whose length is upto (2^16)-1 bytes pub const BIN16: u8 = 0xc5; + /// Bin 32 stores a byte array whose length is upto (2^32)-1 bytes pub const BIN32: u8 = 0xc6; + /// Str 8 stores a byte array whose length is upto (2^8)-1 bytes pub const STR8: u8 = 0xd9; + /// Str 16 stores a byte array whose length is upto (2^16)-1 bytes pub const STR16: u8 = 0xda; + /// Str 32 stores a byte array whose length is upto (2^32)-1 bytes pub const STR32: u8 = 0xdb; + /// Array 16 stores an array whose length is upto (2^16)-1 elements pub const ARRAY16: u8 = 0xdc; + /// Array 32 stores an array whose length is upto (2^32)-1 elements pub const ARRAY32: u8 = 0xdd; + /// Map 16 stores a map whose length is upto (2^16)-1 elements pub const MAP16: u8 = 0xde; + /// Map 32 stores a map whose length is upto (2^32)-1 elements pub const MAP32: u8 = 0xdf; } #[cfg(feature = "alloc")] impl Format { + /// Fixext 1 stores an integer and a byte array whose length is 1 byte pub const FIXEXT1: u8 = 0xd4; + /// Fixext 2 stores an integer and a byte array whose length is 2 byte pub const FIXEXT2: u8 = 0xd5; + /// Fixext 4 stores an integer and a byte array whose length is 4 byte pub const FIXEXT4: u8 = 0xd6; + /// Fixext 8 stores an integer and a byte array whose length is 8 byte pub const FIXEXT8: u8 = 0xd7; + /// Fixext 16 stores an integer and a byte array whose length is 16 byte pub const FIXEXT16: u8 = 0xd8; + /// Ext 8 stores an integer and a byte array whose length is upto (2^8)-1 bytes pub const EXT8: u8 = 0xc7; + /// Ext 16 stores an integer and a byte array whose length is upto (2^16)-1 bytes pub const EXT16: u8 = 0xc8; + /// Ext 32 stores an integer and a byte array whose length is upto (2^32)-1 bytes pub const EXT32: u8 = 0xc9; } From adf2bedad55c410e8ac64afee7cc207df5ad4e58 Mon Sep 17 00:00:00 2001 From: computermouth Date: Mon, 24 Mar 2025 09:56:27 -0500 Subject: [PATCH 05/14] some array stuff --- msgpacker/src/lib.rs | 6 ++++-- msgpacker/src/pack/collections.rs | 16 ++++++++++++++-- msgpacker/src/pack/common.rs | 6 ++++-- msgpacker/src/pack/mod.rs | 2 +- msgpacker/src/unpack/collections.rs | 18 ++++++++++++++++++ 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/msgpacker/src/lib.rs b/msgpacker/src/lib.rs index a560c6f..ab3ed78 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -17,8 +17,10 @@ mod unpack; pub use error::Error; pub use format::Format; pub use helpers::{take_byte, take_num}; -pub use pack::{pack_array, pack_map, get_array_info, pack_binary}; -pub use unpack::{unpack_array, unpack_array_iter, unpack_map, unpack_map_iter, unpack_bytes, unpack_bytes_iter}; +pub use pack::{get_array_info, pack_array, pack_binary, pack_map}; +pub use unpack::{ + unpack_array, unpack_array_iter, unpack_bytes, unpack_bytes_iter, unpack_map, unpack_map_iter, +}; #[cfg(feature = "alloc")] pub use extension::Extension; diff --git a/msgpacker/src/pack/collections.rs b/msgpacker/src/pack/collections.rs index 97aac3a..b181665 100644 --- a/msgpacker/src/pack/collections.rs +++ b/msgpacker/src/pack/collections.rs @@ -3,8 +3,8 @@ use core::{borrow::Borrow, iter}; /// Writes the info for an array into the extendable buffer, returning the amount of written bytes. pub fn get_array_info(buf: &mut T, len: usize) -> usize - where - T: Extend +where + T: Extend, { if len <= 15 { buf.extend(iter::once(((len & 0x0f) as u8) | 0x90)); @@ -138,6 +138,18 @@ mod alloc { pack_map(buf, self) } } + + impl Packable for Vec + where + X: Packable, + { + fn pack(&self, buf: &mut T) -> usize + where + T: Extend, + { + pack_array(buf, self) + } + } } #[cfg(feature = "std")] diff --git a/msgpacker/src/pack/common.rs b/msgpacker/src/pack/common.rs index 9c946d3..f419518 100644 --- a/msgpacker/src/pack/common.rs +++ b/msgpacker/src/pack/common.rs @@ -1,4 +1,4 @@ -use super::{Format, Packable}; +use super::{get_array_info, Format, Packable}; use core::{iter, marker::PhantomData}; impl Packable for () { @@ -61,7 +61,9 @@ macro_rules! array { where T: Extend, { - self.iter().map(|t| t.pack(buf)).sum() + let len = self.len(); + let n = get_array_info(buf, len); + n + self.iter().map(|t| t.pack(buf)).sum::() } } }; diff --git a/msgpacker/src/pack/mod.rs b/msgpacker/src/pack/mod.rs index f64ce4c..6d7f04f 100644 --- a/msgpacker/src/pack/mod.rs +++ b/msgpacker/src/pack/mod.rs @@ -7,4 +7,4 @@ mod float; mod int; pub use binary::pack_binary; -pub use collections::{pack_array, pack_map, get_array_info}; +pub use collections::{get_array_info, pack_array, pack_map}; diff --git a/msgpacker/src/unpack/collections.rs b/msgpacker/src/unpack/collections.rs index 4bf9ff5..f76a331 100644 --- a/msgpacker/src/unpack/collections.rs +++ b/msgpacker/src/unpack/collections.rs @@ -230,6 +230,24 @@ mod alloc { unpack_map_iter(bytes) } } + + impl Unpackable for Vec + where + X: Unpackable, + { + type Error = ::Error; + + fn unpack(buf: &[u8]) -> Result<(usize, Self), Self::Error> { + unpack_array(buf) + } + + fn unpack_iter(bytes: I) -> Result<(usize, Self), Self::Error> + where + I: IntoIterator, + { + unpack_array_iter(bytes) + } + } } #[cfg(feature = "std")] From 3ad61f8b5fdf4c0ed02b79511e92811f8607c02d Mon Sep 17 00:00:00 2001 From: computermouth Date: Sat, 29 Mar 2025 08:37:29 -0500 Subject: [PATCH 06/14] feature parity with rmp --- msgpacker-derive/src/lib.rs | 15 +++++++++-- msgpacker/src/error.rs | 2 ++ msgpacker/src/lib.rs | 2 +- msgpacker/src/unpack/collections.rs | 1 + msgpacker/src/unpack/common.rs | 42 +++++++++++++++++++++++++++-- 5 files changed, 57 insertions(+), 5 deletions(-) diff --git a/msgpacker-derive/src/lib.rs b/msgpacker-derive/src/lib.rs index 7b45818..140df53 100644 --- a/msgpacker-derive/src/lib.rs +++ b/msgpacker-derive/src/lib.rs @@ -353,14 +353,25 @@ fn impl_fields_unit(name: Ident) -> impl Into { type Error = ::msgpacker::Error; fn unpack(mut buf: &[u8]) -> Result<(usize, Self), Self::Error> { - Ok((0, Self)) + let format = ::msgpacker::take_byte(&mut buf)?; + let (_, len) = match format { + 0x90 => (1, 0), + _ => return Err(Error::UnexpectedFormatTag.into()), + }; + Ok((1, Self)) } fn unpack_iter(bytes: I) -> Result<(usize, Self), Self::Error> where I: IntoIterator, { - Ok((0, Self)) + let mut bytes = bytes.into_iter(); + let format = ::msgpacker::take_byte_iter(bytes.by_ref())?; + let (_, len) = match format { + 0x90 => (1, 0), + _ => return Err(Error::UnexpectedFormatTag.into()), + }; + Ok((1, Self)) } } } diff --git a/msgpacker/src/error.rs b/msgpacker/src/error.rs index 08fc582..ddeaffb 100644 --- a/msgpacker/src/error.rs +++ b/msgpacker/src/error.rs @@ -17,6 +17,8 @@ pub enum Error { UnexpectedBinLength, /// The struct we're targeting does not match the data. UnexpectedStructLength, + /// The array we're targeting does not match the data. + UnexpectedArrayLength, } impl fmt::Display for Error { diff --git a/msgpacker/src/lib.rs b/msgpacker/src/lib.rs index ab3ed78..1a9c2ce 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -16,7 +16,7 @@ mod unpack; pub use error::Error; pub use format::Format; -pub use helpers::{take_byte, take_num}; +pub use helpers::{take_byte, take_byte_iter, take_num}; pub use pack::{get_array_info, pack_array, pack_binary, pack_map}; pub use unpack::{ unpack_array, unpack_array_iter, unpack_bytes, unpack_bytes_iter, unpack_map, unpack_map_iter, diff --git a/msgpacker/src/unpack/collections.rs b/msgpacker/src/unpack/collections.rs index f76a331..f0d15af 100644 --- a/msgpacker/src/unpack/collections.rs +++ b/msgpacker/src/unpack/collections.rs @@ -22,6 +22,7 @@ where ), _ => return Err(Error::UnexpectedFormatTag.into()), }; + let array: C = (0..len) .map(|_| { let (count, v) = V::unpack(buf)?; diff --git a/msgpacker/src/unpack/common.rs b/msgpacker/src/unpack/common.rs index 58821b5..a8710da 100644 --- a/msgpacker/src/unpack/common.rs +++ b/msgpacker/src/unpack/common.rs @@ -1,5 +1,5 @@ use super::{ - helpers::{take_byte, take_byte_iter}, + helpers::{take_byte, take_byte_iter, take_num, take_num_iter}, Error, Format, Unpackable, }; use core::{marker::PhantomData, mem::MaybeUninit}; @@ -98,7 +98,26 @@ macro_rules! array { fn unpack(mut buf: &[u8]) -> Result<(usize, Self), Self::Error> { let mut array = [const { MaybeUninit::uninit() }; $n]; - let n = + + let format = take_byte(&mut buf)?; + let (mut n, len) = match format { + 0x90..=0x9f => (1, (format & 0x0f) as usize), + Format::ARRAY16 => ( + 3, + take_num(&mut buf, u16::from_be_bytes).map(|v| v as usize)?, + ), + Format::ARRAY32 => ( + 5, + take_num(&mut buf, u32::from_be_bytes).map(|v| v as usize)?, + ), + _ => return Err(Error::UnexpectedFormatTag.into()), + }; + + if len != $n { + return Err(Error::UnexpectedArrayLength.into()) + } + + n += array .iter_mut() .try_fold::<_, _, Result<_, Self::Error>>(0, |count, a| { @@ -122,6 +141,25 @@ macro_rules! array { { let mut bytes = bytes.into_iter(); let mut array = [const { MaybeUninit::uninit() }; $n]; + + let format = take_byte_iter(bytes.by_ref())?; + let (_, len) = match format { + 0x90..=0x9f => (1, (format & 0x0f) as usize), + Format::ARRAY16 => ( + 3, + take_num_iter(bytes.by_ref(), u16::from_be_bytes).map(|v| v as usize)?, + ), + Format::ARRAY32 => ( + 5, + take_num_iter(bytes.by_ref(), u32::from_be_bytes).map(|v| v as usize)?, + ), + _ => return Err(Error::UnexpectedFormatTag.into()), + }; + + if len != $n { + return Err(Error::UnexpectedArrayLength.into()) + } + let n = array .iter_mut() From 8e45ff20360c01449a58cd5ffc626d0428d7a534 Mon Sep 17 00:00:00 2001 From: computermouth Date: Sat, 5 Apr 2025 16:32:22 -0500 Subject: [PATCH 07/14] fixed derive vec unpack_iter --- msgpacker-derive/src/lib.rs | 18 ++++++++++++++++++ msgpacker/src/lib.rs | 2 +- msgpacker/src/unpack/common.rs | 21 ++++++++++----------- msgpacker/tests/binary.rs | 2 +- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/msgpacker-derive/src/lib.rs b/msgpacker-derive/src/lib.rs index 140df53..f83735e 100644 --- a/msgpacker-derive/src/lib.rs +++ b/msgpacker-derive/src/lib.rs @@ -74,6 +74,24 @@ fn impl_fields_named(name: Ident, f: FieldsNamed) -> impl Into { { let mut bytes = bytes.into_iter(); let mut n = 0; + let expected_len = #field_len; + let format = ::msgpacker::take_byte_iter(bytes.by_ref())?; + let (header_bytes, actual_len) = match format { + 0x90..=0x9f => (1, (format & 0x0f) as usize), + ::msgpacker::Format::ARRAY16 => { + let len = ::msgpacker::take_num_iter(&mut bytes, u16::from_be_bytes)? as usize; + (3, len) + } + ::msgpacker::Format::ARRAY32 => { + let len = ::msgpacker::take_num_iter(&mut bytes, u16::from_be_bytes)? as usize; + (5, len) + } + _ => return Err(::msgpacker::Error::UnexpectedFormatTag.into()), + }; + if actual_len != expected_len { + return Err(::msgpacker::Error::UnexpectedStructLength.into()); + } + n += header_bytes; } }; diff --git a/msgpacker/src/lib.rs b/msgpacker/src/lib.rs index 1a9c2ce..adb0d84 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -16,7 +16,7 @@ mod unpack; pub use error::Error; pub use format::Format; -pub use helpers::{take_byte, take_byte_iter, take_num}; +pub use helpers::{take_byte, take_byte_iter, take_num, take_num_iter}; pub use pack::{get_array_info, pack_array, pack_binary, pack_map}; pub use unpack::{ unpack_array, unpack_array_iter, unpack_bytes, unpack_bytes_iter, unpack_map, unpack_map_iter, diff --git a/msgpacker/src/unpack/common.rs b/msgpacker/src/unpack/common.rs index a8710da..1ff23a7 100644 --- a/msgpacker/src/unpack/common.rs +++ b/msgpacker/src/unpack/common.rs @@ -114,18 +114,17 @@ macro_rules! array { }; if len != $n { - return Err(Error::UnexpectedArrayLength.into()) + return Err(Error::UnexpectedArrayLength.into()); } - n += - array - .iter_mut() - .try_fold::<_, _, Result<_, Self::Error>>(0, |count, a| { - let (n, x) = X::unpack(buf)?; - buf = &buf[n..]; - a.write(x); - Ok(count + n) - })?; + n += array + .iter_mut() + .try_fold::<_, _, Result<_, Self::Error>>(0, |count, a| { + let (n, x) = X::unpack(buf)?; + buf = &buf[n..]; + a.write(x); + Ok(count + n) + })?; // Safety: array is initialized let array = ::core::array::from_fn(|i| { let mut x = MaybeUninit::zeroed(); @@ -157,7 +156,7 @@ macro_rules! array { }; if len != $n { - return Err(Error::UnexpectedArrayLength.into()) + return Err(Error::UnexpectedArrayLength.into()); } let n = diff --git a/msgpacker/tests/binary.rs b/msgpacker/tests/binary.rs index ce79bbe..7f08f5c 100644 --- a/msgpacker/tests/binary.rs +++ b/msgpacker/tests/binary.rs @@ -5,7 +5,7 @@ mod utils; #[test] fn empty_vec() { - let v = vec![]; + let v: Vec = vec![]; let mut bytes = vec![]; let n = v.pack(&mut bytes); let (o, x) = Vec::::unpack(&bytes).unwrap(); From 859b999865904b83c245fb70cc9c423f7512bee6 Mon Sep 17 00:00:00 2001 From: computermouth Date: Sat, 5 Apr 2025 17:07:49 -0500 Subject: [PATCH 08/14] fix sized array unpack iter --- msgpacker/src/unpack/common.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/msgpacker/src/unpack/common.rs b/msgpacker/src/unpack/common.rs index 1ff23a7..207905e 100644 --- a/msgpacker/src/unpack/common.rs +++ b/msgpacker/src/unpack/common.rs @@ -142,7 +142,7 @@ macro_rules! array { let mut array = [const { MaybeUninit::uninit() }; $n]; let format = take_byte_iter(bytes.by_ref())?; - let (_, len) = match format { + let (mut n, len) = match format { 0x90..=0x9f => (1, (format & 0x0f) as usize), Format::ARRAY16 => ( 3, @@ -159,7 +159,7 @@ macro_rules! array { return Err(Error::UnexpectedArrayLength.into()); } - let n = + n += array .iter_mut() .try_fold::<_, _, Result<_, Self::Error>>(0, |count, a| { From bfbeee4a5630bf3a5d8852c7002eae4306567d08 Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 6 Apr 2025 08:39:18 -0500 Subject: [PATCH 09/14] rename bytes functions to binary, change tests to use new binary functions --- msgpacker/src/lib.rs | 2 +- msgpacker/src/unpack/binary.rs | 4 ++-- msgpacker/src/unpack/mod.rs | 2 +- msgpacker/tests/binary.rs | 6 +++--- msgpacker/tests/collections.rs | 13 +++++++++++++ 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/msgpacker/src/lib.rs b/msgpacker/src/lib.rs index adb0d84..a7c1279 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -19,7 +19,7 @@ pub use format::Format; pub use helpers::{take_byte, take_byte_iter, take_num, take_num_iter}; pub use pack::{get_array_info, pack_array, pack_binary, pack_map}; pub use unpack::{ - unpack_array, unpack_array_iter, unpack_bytes, unpack_bytes_iter, unpack_map, unpack_map_iter, + unpack_array, unpack_array_iter, unpack_binary, unpack_binary_iter, unpack_map, unpack_map_iter, }; #[cfg(feature = "alloc")] diff --git a/msgpacker/src/unpack/binary.rs b/msgpacker/src/unpack/binary.rs index 9687b9b..64b742e 100644 --- a/msgpacker/src/unpack/binary.rs +++ b/msgpacker/src/unpack/binary.rs @@ -11,7 +11,7 @@ use alloc::{string::String, vec::Vec}; use core::str; /// Unpacks binary data from the buffer, returning a &[u8] and the amount of read bytes. -pub fn unpack_bytes(mut buf: &[u8]) -> Result<(usize, &[u8]), Error> { +pub fn unpack_binary(mut buf: &[u8]) -> Result<(usize, &[u8]), Error> { let format = take_byte(&mut buf)?; let (n, len) = match format { Format::BIN8 => (2, take_byte(&mut buf)? as usize), @@ -26,7 +26,7 @@ pub fn unpack_bytes(mut buf: &[u8]) -> Result<(usize, &[u8]), Error> { } /// Unpacks binary data from the iterator, returning a Vec and the amount of read bytes. -pub fn unpack_bytes_iter(bytes: I) -> Result<(usize, Vec), Error> +pub fn unpack_binary_iter(bytes: I) -> Result<(usize, Vec), Error> where I: IntoIterator, { diff --git a/msgpacker/src/unpack/mod.rs b/msgpacker/src/unpack/mod.rs index 143b9a6..a6b8f4e 100644 --- a/msgpacker/src/unpack/mod.rs +++ b/msgpacker/src/unpack/mod.rs @@ -7,5 +7,5 @@ mod common; mod float; mod int; -pub use binary::{unpack_bytes, unpack_bytes_iter}; +pub use binary::{unpack_binary, unpack_binary_iter}; pub use collections::{unpack_array, unpack_array_iter, unpack_map, unpack_map_iter}; diff --git a/msgpacker/tests/binary.rs b/msgpacker/tests/binary.rs index 7f08f5c..63646aa 100644 --- a/msgpacker/tests/binary.rs +++ b/msgpacker/tests/binary.rs @@ -7,9 +7,9 @@ mod utils; fn empty_vec() { let v: Vec = vec![]; let mut bytes = vec![]; - let n = v.pack(&mut bytes); - let (o, x) = Vec::::unpack(&bytes).unwrap(); - let (p, y) = Vec::::unpack_iter(bytes).unwrap(); + let n = msgpacker::pack_binary(&mut bytes, &v); + let (o, x) = msgpacker::unpack_binary(&bytes).unwrap(); + let (p, y) = msgpacker::unpack_binary_iter(bytes.clone()).unwrap(); assert_eq!(o, n); assert_eq!(p, n); assert_eq!(v, x); diff --git a/msgpacker/tests/collections.rs b/msgpacker/tests/collections.rs index 57b19fb..301e0ee 100644 --- a/msgpacker/tests/collections.rs +++ b/msgpacker/tests/collections.rs @@ -49,6 +49,19 @@ proptest! { assert_eq!(value, y); } + #[test] + fn slice(value: [Value;8]) { + let mut bytes = Vec::new(); + let n = msgpacker::pack_array(&mut bytes, &value); + assert_eq!(n, bytes.len()); + let (o, x): (usize, Vec) = msgpacker::unpack_array(&bytes).unwrap(); + let (p, y): (usize, Vec) = msgpacker::unpack_array_iter(bytes).unwrap(); + assert_eq!(n, o); + assert_eq!(n, p); + assert_eq!(value, x.as_slice()); + assert_eq!(value, y.as_slice()); + } + #[test] fn map(map: HashMap) { let mut bytes = Vec::new(); From d40a2dfe0d7b018783bf9a506d983d70721879ba Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 6 Apr 2025 10:56:34 -0500 Subject: [PATCH 10/14] fix bin packing --- msgpacker/src/pack/binary.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/msgpacker/src/pack/binary.rs b/msgpacker/src/pack/binary.rs index d857df9..41948b7 100644 --- a/msgpacker/src/pack/binary.rs +++ b/msgpacker/src/pack/binary.rs @@ -13,10 +13,10 @@ where buf.extend(iter::once(Format::BIN8).chain(iter::once(len as u8))); 2 } else if len <= u16::MAX as usize { - buf.extend(iter::once(Format::BIN16).chain(len.to_be_bytes())); + buf.extend(iter::once(Format::BIN16).chain((len as u16).to_be_bytes())); 3 } else if len <= u32::MAX as usize { - buf.extend(iter::once(Format::BIN32).chain(len.to_be_bytes())); + buf.extend(iter::once(Format::BIN32).chain((len as u32).to_be_bytes())); 5 } else { #[cfg(feature = "strict")] From 98091f9f6ac27633dacfc8ef19072c12c1d8e5e6 Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 6 Apr 2025 10:57:22 -0500 Subject: [PATCH 11/14] fmt --- msgpacker/src/unpack/common.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/msgpacker/src/unpack/common.rs b/msgpacker/src/unpack/common.rs index 207905e..1b94b38 100644 --- a/msgpacker/src/unpack/common.rs +++ b/msgpacker/src/unpack/common.rs @@ -159,14 +159,13 @@ macro_rules! array { return Err(Error::UnexpectedArrayLength.into()); } - n += - array - .iter_mut() - .try_fold::<_, _, Result<_, Self::Error>>(0, |count, a| { - let (n, x) = X::unpack_iter(bytes.by_ref())?; - a.write(x); - Ok(count + n) - })?; + n += array + .iter_mut() + .try_fold::<_, _, Result<_, Self::Error>>(0, |count, a| { + let (n, x) = X::unpack_iter(bytes.by_ref())?; + a.write(x); + Ok(count + n) + })?; // Safety: array is initialized let array = ::core::array::from_fn(|i| { let mut x = MaybeUninit::zeroed(); From 8ab7ecbd8ed1136d57d21abe7b6533cf2ee4101f Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 6 Apr 2025 18:15:54 -0500 Subject: [PATCH 12/14] version bumps, minor api cleanup --- msgpacker-derive/Cargo.toml | 2 +- msgpacker-derive/src/lib.rs | 28 +++++++++++++-------------- msgpacker/Cargo.toml | 6 ++---- msgpacker/src/format.rs | 1 + msgpacker/src/helpers.rs | 38 ++++++++++++++++++++----------------- msgpacker/src/lib.rs | 13 ++++++++++--- 6 files changed, 49 insertions(+), 39 deletions(-) diff --git a/msgpacker-derive/Cargo.toml b/msgpacker-derive/Cargo.toml index 7ccc16a..0c205f2 100644 --- a/msgpacker-derive/Cargo.toml +++ b/msgpacker-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "msgpacker-derive" -version = "0.3.1" +version = "0.4.0" 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 f83735e..5977a77 100644 --- a/msgpacker-derive/src/lib.rs +++ b/msgpacker-derive/src/lib.rs @@ -40,7 +40,7 @@ fn impl_fields_named(name: Ident, f: FieldsNamed) -> impl Into { let block_packable: Block = parse_quote! { { let mut n = 0; - n += ::msgpacker::get_array_info(buf, #field_len); + n += ::msgpacker::derive_util::get_array_info(buf, #field_len); } }; let block_unpackable: Block = parse_quote! { @@ -48,16 +48,16 @@ fn impl_fields_named(name: Ident, f: FieldsNamed) -> impl Into { let mut n = 0; let expected_len = #field_len; - let format = ::msgpacker::take_byte(&mut buf)?; + let format = ::msgpacker::derive_util::take_byte(&mut buf)?; let (header_bytes, actual_len) = match format { 0x90..=0x9f => (1, (format & 0x0f) as usize), - ::msgpacker::Format::ARRAY16 => { - let len = ::msgpacker::take_num(&mut buf, u16::from_be_bytes)? as usize; + ::msgpacker::derive_util::Format::ARRAY16 => { + let len = ::msgpacker::derive_util::take_num(&mut buf, u16::from_be_bytes)? as usize; (3, len) } - ::msgpacker::Format::ARRAY32 => { - let len = ::msgpacker::take_num(&mut buf, u32::from_be_bytes)? as usize; + ::msgpacker::derive_util::Format::ARRAY32 => { + let len = ::msgpacker::derive_util::take_num(&mut buf, u32::from_be_bytes)? as usize; (5, len) } _ => return Err(::msgpacker::Error::UnexpectedFormatTag.into()), @@ -75,15 +75,15 @@ fn impl_fields_named(name: Ident, f: FieldsNamed) -> impl Into { let mut bytes = bytes.into_iter(); let mut n = 0; let expected_len = #field_len; - let format = ::msgpacker::take_byte_iter(bytes.by_ref())?; + let format = ::msgpacker::derive_util::take_byte_iter(bytes.by_ref())?; let (header_bytes, actual_len) = match format { 0x90..=0x9f => (1, (format & 0x0f) as usize), - ::msgpacker::Format::ARRAY16 => { - let len = ::msgpacker::take_num_iter(&mut bytes, u16::from_be_bytes)? as usize; + ::msgpacker::derive_util::Format::ARRAY16 => { + let len = ::msgpacker::derive_util::take_num_iter(&mut bytes, u16::from_be_bytes)? as usize; (3, len) } - ::msgpacker::Format::ARRAY32 => { - let len = ::msgpacker::take_num_iter(&mut bytes, u16::from_be_bytes)? as usize; + ::msgpacker::derive_util::Format::ARRAY32 => { + let len = ::msgpacker::derive_util::take_num_iter(&mut bytes, u16::from_be_bytes)? as usize; (5, len) } _ => return Err(::msgpacker::Error::UnexpectedFormatTag.into()), @@ -363,7 +363,7 @@ fn impl_fields_unit(name: Ident) -> impl Into { where T: Extend, { - ::msgpacker::get_array_info(buf, 0) + ::msgpacker::derive_util::get_array_info(buf, 0) } } @@ -371,7 +371,7 @@ fn impl_fields_unit(name: Ident) -> impl Into { type Error = ::msgpacker::Error; fn unpack(mut buf: &[u8]) -> Result<(usize, Self), Self::Error> { - let format = ::msgpacker::take_byte(&mut buf)?; + let format = ::msgpacker::derive_util::take_byte(&mut buf)?; let (_, len) = match format { 0x90 => (1, 0), _ => return Err(Error::UnexpectedFormatTag.into()), @@ -384,7 +384,7 @@ fn impl_fields_unit(name: Ident) -> impl Into { I: IntoIterator, { let mut bytes = bytes.into_iter(); - let format = ::msgpacker::take_byte_iter(bytes.by_ref())?; + let format = ::msgpacker::derive_util::take_byte_iter(bytes.by_ref())?; let (_, len) = match format { 0x90 => (1, 0), _ => return Err(Error::UnexpectedFormatTag.into()), diff --git a/msgpacker/Cargo.toml b/msgpacker/Cargo.toml index 9263f7e..7f10527 100644 --- a/msgpacker/Cargo.toml +++ b/msgpacker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "msgpacker" -version = "0.4.7" +version = "0.5.0" authors = ["Victor Lopez "] categories = ["compression", "encoding", "parser-implementations"] edition = "2021" @@ -11,9 +11,7 @@ repository = "https://github.com/codx-dev/msgpacker" description = "MessagePack protocol implementation for Rust." [dependencies] -# todo, revert -# msgpacker-derive = { version = "0.3", optional = true } -msgpacker-derive = { version = "0.3", path = "../msgpacker-derive", optional = true } +msgpacker-derive = { path = "../msgpacker-derive", optional = true } [dev-dependencies] proptest = "1.2" diff --git a/msgpacker/src/format.rs b/msgpacker/src/format.rs index 986ed9b..e1d22fb 100644 --- a/msgpacker/src/format.rs +++ b/msgpacker/src/format.rs @@ -1,3 +1,4 @@ +/// A container for the Format constants pub struct Format {} impl Format { diff --git a/msgpacker/src/helpers.rs b/msgpacker/src/helpers.rs index 8efb98e..c7365a1 100644 --- a/msgpacker/src/helpers.rs +++ b/msgpacker/src/helpers.rs @@ -1,12 +1,6 @@ use super::Error; -pub fn take_byte_iter(mut bytes: I) -> Result -where - I: Iterator, -{ - bytes.next().ok_or(Error::BufferTooShort) -} - +/// Take one byte off the provided buffer, advance the pointer, or error. pub fn take_byte(buf: &mut &[u8]) -> Result { if buf.is_empty() { return Err(Error::BufferTooShort); @@ -16,6 +10,15 @@ pub fn take_byte(buf: &mut &[u8]) -> Result { Ok(l[0]) } +/// Take one byte from the iterator or error. +pub fn take_byte_iter(mut bytes: I) -> Result +where + I: Iterator, +{ + bytes.next().ok_or(Error::BufferTooShort) +} + +/// Read bytes off the buffer, using the provided function, or error. pub fn take_num(buf: &mut &[u8], f: fn([u8; N]) -> V) -> Result { if buf.len() < N { return Err(Error::BufferTooShort); @@ -27,16 +30,7 @@ pub fn take_num(buf: &mut &[u8], f: fn([u8; N]) -> V) -> Resu Ok(f(val)) } -#[cfg(feature = "alloc")] -pub fn take_buffer<'a>(buf: &mut &'a [u8], len: usize) -> Result<&'a [u8], Error> { - if buf.len() < len { - return Err(Error::BufferTooShort); - } - let (l, r) = buf.split_at(len); - *buf = r; - Ok(l) -} - +/// Read a number off the iterator, using the provided function, or error. pub fn take_num_iter(bytes: I, f: fn([u8; N]) -> V) -> Result where I: Iterator, @@ -60,6 +54,16 @@ where Ok(f(array)) } +#[cfg(feature = "alloc")] +pub fn take_buffer<'a>(buf: &mut &'a [u8], len: usize) -> Result<&'a [u8], Error> { + if buf.len() < len { + return Err(Error::BufferTooShort); + } + let (l, r) = buf.split_at(len); + *buf = r; + Ok(l) +} + #[cfg(feature = "alloc")] pub fn take_buffer_iter(bytes: I, len: usize) -> Result, Error> where diff --git a/msgpacker/src/lib.rs b/msgpacker/src/lib.rs index a7c1279..6e077d6 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -15,13 +15,20 @@ mod pack; mod unpack; pub use error::Error; -pub use format::Format; -pub use helpers::{take_byte, take_byte_iter, take_num, take_num_iter}; -pub use pack::{get_array_info, pack_array, pack_binary, pack_map}; +use format::Format; +pub use pack::{pack_array, pack_binary, pack_map}; pub use unpack::{ unpack_array, unpack_array_iter, unpack_binary, unpack_binary_iter, unpack_map, unpack_map_iter, }; +/// This module exposes some utility variables and functions for msgpacker-derive +#[cfg(feature = "derive")] +pub mod derive_util { + pub use crate::format::Format; + pub use crate::helpers::{take_byte, take_byte_iter, take_num, take_num_iter}; + pub use crate::pack::get_array_info; +} + #[cfg(feature = "alloc")] pub use extension::Extension; From 7b359b45a1299fe581fa33f838d654eca3cb59ef Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 6 Apr 2025 18:42:52 -0500 Subject: [PATCH 13/14] no nightly --- msgpacker/src/helpers.rs | 20 ++++---------------- msgpacker/src/unpack/common.rs | 13 +++---------- rust-toolchain.toml | 2 -- 3 files changed, 7 insertions(+), 28 deletions(-) delete mode 100644 rust-toolchain.toml diff --git a/msgpacker/src/helpers.rs b/msgpacker/src/helpers.rs index c7365a1..f30baba 100644 --- a/msgpacker/src/helpers.rs +++ b/msgpacker/src/helpers.rs @@ -31,26 +31,14 @@ pub fn take_num(buf: &mut &[u8], f: fn([u8; N]) -> V) -> Resu } /// Read a number off the iterator, using the provided function, or error. -pub fn take_num_iter(bytes: I, f: fn([u8; N]) -> V) -> Result +pub fn take_num_iter(mut bytes: I, f: fn([u8; N]) -> V) -> Result where I: Iterator, { - let mut array = [0u8; N]; - let mut i = 0; - - for b in bytes { - array[i] = b; - i += 1; - - if i == N { - break; - } - } - - if i < N { - return Err(Error::BufferTooShort); + let mut array = [0u8; N]; // Initialize with zeroes + for byte in array.iter_mut() { + *byte = bytes.next().ok_or(Error::BufferTooShort)?; } - Ok(f(array)) } diff --git a/msgpacker/src/unpack/common.rs b/msgpacker/src/unpack/common.rs index 1b94b38..4646c65 100644 --- a/msgpacker/src/unpack/common.rs +++ b/msgpacker/src/unpack/common.rs @@ -3,6 +3,7 @@ use super::{ Error, Format, Unpackable, }; use core::{marker::PhantomData, mem::MaybeUninit}; +use std::ptr; impl Unpackable for () { type Error = Error; @@ -126,11 +127,7 @@ macro_rules! array { Ok(count + n) })?; // Safety: array is initialized - let array = ::core::array::from_fn(|i| { - let mut x = MaybeUninit::zeroed(); - ::core::mem::swap(&mut array[i], &mut x); - unsafe { MaybeUninit::assume_init(x) } - }); + let array = unsafe { ptr::read(&array as *const _ as *const [X; $n]) }; Ok((n, array)) } @@ -167,11 +164,7 @@ macro_rules! array { Ok(count + n) })?; // Safety: array is initialized - let array = ::core::array::from_fn(|i| { - let mut x = MaybeUninit::zeroed(); - ::core::mem::swap(&mut array[i], &mut x); - unsafe { MaybeUninit::assume_init(x) } - }); + let array = unsafe { ptr::read(&array as *const _ as *const [X; $n]) }; Ok((n, array)) } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 271800c..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "nightly" \ No newline at end of file From 9074f28ad3da7ada789aedda67d56808242aeea0 Mon Sep 17 00:00:00 2001 From: computermouth Date: Tue, 15 Apr 2025 17:32:57 -0500 Subject: [PATCH 14/14] Support boxed slices --- msgpacker/src/pack/binary.rs | 9 +++++++++ msgpacker/src/unpack/binary.rs | 33 +++++++++++++++++++++++++++++++++ msgpacker/tests/binary.rs | 13 +++++++++++++ 3 files changed, 55 insertions(+) diff --git a/msgpacker/src/pack/binary.rs b/msgpacker/src/pack/binary.rs index 41948b7..c73d798 100644 --- a/msgpacker/src/pack/binary.rs +++ b/msgpacker/src/pack/binary.rs @@ -68,4 +68,13 @@ mod alloc { self.as_str().pack(buf) } } + + impl Packable for Box<[u8]> { + fn pack(&self, buf: &mut T) -> usize + where + T: Extend, + { + pack_binary(buf, self) + } + } } diff --git a/msgpacker/src/unpack/binary.rs b/msgpacker/src/unpack/binary.rs index 64b742e..8056729 100644 --- a/msgpacker/src/unpack/binary.rs +++ b/msgpacker/src/unpack/binary.rs @@ -101,3 +101,36 @@ impl Unpackable for String { Ok((n + len, s)) } } + +impl Unpackable for Box<[u8]> { + type Error = Error; + + fn unpack(buf: &[u8]) -> Result<(usize, Self), Self::Error> { + unpack_binary(buf).map(|(n, b)| (n, b.to_vec().into_boxed_slice())) + } + + fn unpack_iter(bytes: I) -> Result<(usize, Self), Self::Error> + where + I: IntoIterator, + { + let mut bytes = bytes.into_iter(); + let format = take_byte_iter(bytes.by_ref())?; + let (n, len) = match format { + Format::BIN8 => (2, take_byte_iter(bytes.by_ref())? as usize), + Format::BIN16 => ( + 3, + take_num_iter(bytes.by_ref(), u16::from_be_bytes)? as usize, + ), + Format::BIN32 => ( + 5, + take_num_iter(bytes.by_ref(), u32::from_be_bytes)? as usize, + ), + _ => return Err(Error::UnexpectedFormatTag), + }; + let v: Vec<_> = bytes.take(len).collect(); + if v.len() < len { + return Err(Error::BufferTooShort); + } + Ok((n + len, v.into_boxed_slice())) + } +} diff --git a/msgpacker/tests/binary.rs b/msgpacker/tests/binary.rs index 63646aa..6e68711 100644 --- a/msgpacker/tests/binary.rs +++ b/msgpacker/tests/binary.rs @@ -30,6 +30,19 @@ fn empty_str() { } proptest! { + #[test] + fn slice(value: Box<[u8]>) { + let mut bytes = Vec::new(); + let n = msgpacker::pack_binary(&mut bytes, &value); + assert_eq!(n, bytes.len()); + let (o, x): (usize, &[u8]) = msgpacker::unpack_binary(&bytes).unwrap(); + let (p, y): (usize, Vec) = msgpacker::unpack_binary_iter(bytes.clone()).unwrap(); + assert_eq!(n, o); + assert_eq!(n, p); + assert_eq!(value, x.into()); + assert_eq!(value, y.into()); + } + #[test] fn vec(v: Vec) { utils::case(v);