From e9c40fada1e8c3d27bb90468213fc27cea98822e Mon Sep 17 00:00:00 2001 From: Matt Bryant <3358866+tehmatt@users.noreply.github.com> Date: Wed, 26 Feb 2025 23:28:38 -0800 Subject: [PATCH 01/15] chore: remove unnecessary nightly feature `maybe_uninit_uninit_array` isn't needed anymore, since it's directly replaceable with an inline const expression. --- msgpacker/src/lib.rs | 6 +----- msgpacker/src/unpack/common.rs | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/msgpacker/src/lib.rs b/msgpacker/src/lib.rs index ac49878..76127cb 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -1,9 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![feature( - iter_array_chunks, - maybe_uninit_array_assume_init, - maybe_uninit_uninit_array -)] +#![feature(iter_array_chunks, maybe_uninit_array_assume_init)] #![warn(missing_docs)] #![doc = include_str!("../README.md")] diff --git a/msgpacker/src/unpack/common.rs b/msgpacker/src/unpack/common.rs index 2968b15..949e985 100644 --- a/msgpacker/src/unpack/common.rs +++ b/msgpacker/src/unpack/common.rs @@ -97,7 +97,7 @@ macro_rules! array { type Error = ::Error; fn unpack(mut buf: &[u8]) -> Result<(usize, Self), Self::Error> { - let mut array = MaybeUninit::uninit_array(); + let mut array = [const { MaybeUninit::uninit() }; $n]; let n = array .iter_mut() @@ -117,7 +117,7 @@ macro_rules! array { I: IntoIterator, { let mut bytes = bytes.into_iter(); - let mut array = MaybeUninit::uninit_array(); + let mut array = [const { MaybeUninit::uninit() }; $n]; let n = array .iter_mut() From 3d3b1ab177c05086eea37a9d2007c2d99e2e8d1e Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 16 Mar 2025 19:21:01 -0500 Subject: [PATCH 02/15] 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 c48b3b8..b82646c 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 76127cb..b5104a7 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -17,7 +17,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 5596f9f8a3dd4b7cacdd07676a9b0b8c7cd990ca Mon Sep 17 00:00:00 2001 From: computermouth Date: Mon, 17 Mar 2025 17:06:44 -0500 Subject: [PATCH 03/15] 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 b5104a7..8a6989e 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -17,7 +17,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 069436bc426e0bd76bfdaa9f47c837cdd6c3a08b Mon Sep 17 00:00:00 2001 From: computermouth Date: Mon, 17 Mar 2025 17:58:13 -0500 Subject: [PATCH 04/15] 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 8a6989e..37a130a 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -16,9 +16,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 fcfb4deb72dc9eca6ea43f5f007af62e38123a77 Mon Sep 17 00:00:00 2001 From: computermouth Date: Mon, 17 Mar 2025 20:07:37 -0500 Subject: [PATCH 05/15] 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 166595a2d1d3427c5197c122386f837c5ae6ed69 Mon Sep 17 00:00:00 2001 From: computermouth Date: Mon, 24 Mar 2025 09:56:27 -0500 Subject: [PATCH 06/15] 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 37a130a..e9a1ec3 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -18,8 +18,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 974a44cea4871e5e084de04ba306cc6090244333 Mon Sep 17 00:00:00 2001 From: computermouth Date: Sat, 29 Mar 2025 08:37:29 -0500 Subject: [PATCH 07/15] 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 e9a1ec3..de11764 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -17,7 +17,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 949e985..6cc8593 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| { @@ -118,6 +137,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 e77e2a72bf956f2d43e3f73b3cb3f572a77b4853 Mon Sep 17 00:00:00 2001 From: computermouth Date: Sat, 5 Apr 2025 16:32:22 -0500 Subject: [PATCH 08/15] 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 de11764..bea2277 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -17,7 +17,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 6cc8593..f2402c8 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 = unsafe { MaybeUninit::array_assume_init(array) }; Ok((n, array)) @@ -153,7 +152,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 f46a154a76a0e4332319cec43b0575d1f2c54de9 Mon Sep 17 00:00:00 2001 From: computermouth Date: Sat, 5 Apr 2025 17:07:49 -0500 Subject: [PATCH 09/15] 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 f2402c8..66dc276 100644 --- a/msgpacker/src/unpack/common.rs +++ b/msgpacker/src/unpack/common.rs @@ -138,7 +138,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, @@ -155,7 +155,7 @@ macro_rules! array { return Err(Error::UnexpectedArrayLength.into()); } - let n = + n += array .iter_mut() .try_fold::<_, _, Result<_, Self::Error>>(0, |count, a| { From a066a36736b46d77be055ca2cebdc25252283e8d Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 6 Apr 2025 08:39:18 -0500 Subject: [PATCH 10/15] 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 bea2277..0e90baa 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -20,7 +20,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 c5225513a563e76e16f65e3b5c514b8b4854323d Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 6 Apr 2025 10:56:34 -0500 Subject: [PATCH 11/15] 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 4a4fd732c725a9b2e218e0c115133e9f4a96f899 Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 6 Apr 2025 10:57:22 -0500 Subject: [PATCH 12/15] 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 66dc276..4193b1a 100644 --- a/msgpacker/src/unpack/common.rs +++ b/msgpacker/src/unpack/common.rs @@ -155,14 +155,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 = unsafe { MaybeUninit::array_assume_init(array) }; Ok((n, array)) From acd9f5a9e564af96e1db2e9a9eb492a7c7f7aea0 Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 6 Apr 2025 18:15:54 -0500 Subject: [PATCH 13/15] 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 b82646c..7f10527 100644 --- a/msgpacker/Cargo.toml +++ b/msgpacker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "msgpacker" -version = "0.4.5" +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 74c715d..8d35b89 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, @@ -48,6 +42,16 @@ where .map(f) } +#[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 0e90baa..884fb42 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -16,13 +16,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 3fcde01d4049994a2ab82248c2519d5bd7b9e323 Mon Sep 17 00:00:00 2001 From: computermouth Date: Sun, 6 Apr 2025 18:42:52 -0500 Subject: [PATCH 14/15] no nightly --- msgpacker/src/helpers.rs | 12 ++++++------ msgpacker/src/lib.rs | 2 +- msgpacker/src/unpack/common.rs | 7 +++++-- rust-toolchain.toml | 2 -- 4 files changed, 12 insertions(+), 11 deletions(-) delete mode 100644 rust-toolchain.toml diff --git a/msgpacker/src/helpers.rs b/msgpacker/src/helpers.rs index 8d35b89..f30baba 100644 --- a/msgpacker/src/helpers.rs +++ b/msgpacker/src/helpers.rs @@ -31,15 +31,15 @@ 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, { - bytes - .array_chunks() - .next() - .ok_or(Error::BufferTooShort) - .map(f) + let mut array = [0u8; N]; // Initialize with zeroes + for byte in array.iter_mut() { + *byte = bytes.next().ok_or(Error::BufferTooShort)?; + } + Ok(f(array)) } #[cfg(feature = "alloc")] diff --git a/msgpacker/src/lib.rs b/msgpacker/src/lib.rs index 884fb42..4d6778e 100644 --- a/msgpacker/src/lib.rs +++ b/msgpacker/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] -#![feature(iter_array_chunks, maybe_uninit_array_assume_init)] +// #![feature(iter_array_chunks, maybe_uninit_array_assume_init)] #![warn(missing_docs)] #![doc = include_str!("../README.md")] diff --git a/msgpacker/src/unpack/common.rs b/msgpacker/src/unpack/common.rs index 4193b1a..eeeddf8 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,7 +127,8 @@ macro_rules! array { Ok(count + n) })?; // Safety: array is initialized - let array = unsafe { MaybeUninit::array_assume_init(array) }; + // let array = unsafe { MaybeUninit::array_assume_init(array) }; + let array = unsafe { ptr::read(&array as *const _ as *const [X; $n]) }; Ok((n, array)) } @@ -163,7 +165,8 @@ macro_rules! array { Ok(count + n) })?; // Safety: array is initialized - let array = unsafe { MaybeUninit::array_assume_init(array) }; + // let array = unsafe { MaybeUninit::array_assume_init(array) }; + 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 9fd4b386ce5e6b70143b8eac5f2c68bf77b1ad5a Mon Sep 17 00:00:00 2001 From: computermouth Date: Tue, 15 Apr 2025 17:32:57 -0500 Subject: [PATCH 15/15] 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);