diff --git a/Cargo.toml b/Cargo.toml index caf0034..9a7077c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,8 @@ arbitrary = { version = "1", optional = true } borsh = { version = "1.2.0", optional = true, default-features = false } # Implements the trait `Array` for `GenericArray` struct. generic-array = { version = "1.1.1", optional = true, default-features = false } +# Provides derived `BitEncode` and `BitDecode` implementations +bin-proto = { version = "0.12.5", optional = true, default-features = false } # Provides `Format` implementations defmt = { version = "1.0", optional = true } @@ -81,7 +83,7 @@ experimental_write_impl = [] real_blackbox = ["criterion/real_blackbox"] [package.metadata.docs.rs] -features = ["alloc", "std", "grab_spare_slice", "latest_stable_rust", "serde", "borsh", "defmt"] +features = ["alloc", "std", "grab_spare_slice", "latest_stable_rust", "serde", "borsh", "defmt", "bin-proto"] rustdoc-args = ["--cfg","docs_rs"] [package.metadata.playground] diff --git a/src/arrayvec.rs b/src/arrayvec.rs index dd8a23c..04d6dac 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -287,6 +287,81 @@ where } } +#[cfg(feature = "bin-proto")] +#[cfg_attr(docs_rs, doc(cfg(feature = "bin-proto")))] +impl bin_proto::BitEncode for ArrayVec +where + A: Array, + ::Item: bin_proto::BitEncode, +{ + fn encode( + &self, write: &mut W, ctx: &mut Ctx, tag: bin_proto::Untagged, + ) -> bin_proto::Result<()> + where + W: bin_proto::BitWrite, + E: bin_proto::Endianness, + { + <[::Item] as bin_proto::BitEncode<_, _>>::encode::<_, E>( + self.as_slice(), + write, + ctx, + tag, + ) + } +} + +#[cfg(feature = "bin-proto")] +#[cfg_attr(docs_rs, doc(cfg(feature = "bin-proto")))] +impl bin_proto::BitDecode> for ArrayVec +where + A: Array, + ::Item: bin_proto::BitDecode, + Tag: ::core::convert::TryInto, +{ + fn decode( + read: &mut R, ctx: &mut Ctx, tag: bin_proto::Tag, + ) -> bin_proto::Result + where + R: bin_proto::BitRead, + E: bin_proto::Endianness, + { + let item_count = + tag.0.try_into().map_err(|_| bin_proto::Error::TagConvert)?; + if item_count > A::CAPACITY { + return Err(bin_proto::Error::Other("insufficient capacity")); + } + let mut values = Self::default(); + for _ in 0..item_count { + values.push(bin_proto::BitDecode::<_, _>::decode::<_, E>(read, ctx, ())?); + } + Ok(values) + } +} + +#[cfg(feature = "bin-proto")] +#[cfg_attr(docs_rs, doc(cfg(feature = "bin-proto")))] +impl bin_proto::BitDecode for ArrayVec +where + A: Array, + ::Item: bin_proto::BitDecode, +{ + fn decode( + read: &mut R, ctx: &mut Ctx, _tag: bin_proto::Untagged, + ) -> bin_proto::Result + where + R: bin_proto::BitRead, + E: bin_proto::Endianness, + { + let mut values = Self::default(); + for item in bin_proto::util::decode_items_to_eof::<_, E, _, _>(read, ctx) { + if values.try_push(item?).is_some() { + return Err(bin_proto::Error::Other("insufficient capacity")); + } + } + Ok(values) + } +} + impl ArrayVec { /// Move all values from `other` into this vec. /// diff --git a/src/lib.rs b/src/lib.rs index 22f4293..b8110cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,9 @@ //! * `borsh` provides a `BorshSerialize` and `BorshDeserialize` implementation //! for [`TinyVec`] and [`ArrayVec`] types, provided the inner item also has //! an implementation. +//! * `bin-proto` provides a `BitEncode` and `BitDecode` implementation for +//! [`TinyVec`] and [`ArrayVec`] types, provided the inner item also has an +//! implementation. //! * `defmt` provides a `Format` implementation for all types, provided the //! inner item also has an implementation. //! diff --git a/src/tinyvec.rs b/src/tinyvec.rs index 9f5d48e..28e4102 100644 --- a/src/tinyvec.rs +++ b/src/tinyvec.rs @@ -273,6 +273,72 @@ where } } +#[cfg(feature = "bin-proto")] +#[cfg_attr(docs_rs, doc(cfg(feature = "bin-proto")))] +impl bin_proto::BitEncode for TinyVec +where + A: Array, + ::Item: bin_proto::BitEncode, +{ + fn encode( + &self, write: &mut W, ctx: &mut Ctx, tag: bin_proto::Untagged, + ) -> bin_proto::Result<()> + where + W: bin_proto::BitWrite, + E: bin_proto::Endianness, + { + <[::Item] as bin_proto::BitEncode<_, _>>::encode::<_, E>( + self.as_slice(), + write, + ctx, + tag, + ) + } +} + +#[cfg(feature = "bin-proto")] +#[cfg_attr(docs_rs, doc(cfg(feature = "bin-proto")))] +impl bin_proto::BitDecode> for TinyVec +where + A: Array, + ::Item: bin_proto::BitDecode, + Tag: ::core::convert::TryInto, +{ + fn decode( + read: &mut R, ctx: &mut Ctx, tag: bin_proto::Tag, + ) -> bin_proto::Result + where + R: bin_proto::BitRead, + E: bin_proto::Endianness, + { + let item_count = + tag.0.try_into().map_err(|_| bin_proto::Error::TagConvert)?; + let mut values = Self::with_capacity(item_count); + for _ in 0..item_count { + values.push(bin_proto::BitDecode::<_, _>::decode::<_, E>(read, ctx, ())?); + } + Ok(values) + } +} + +#[cfg(feature = "bin-proto")] +#[cfg_attr(docs_rs, doc(cfg(feature = "bin-proto")))] +impl bin_proto::BitDecode for TinyVec +where + A: Array, + ::Item: bin_proto::BitDecode, +{ + fn decode( + read: &mut R, ctx: &mut Ctx, _tag: bin_proto::Untagged, + ) -> bin_proto::Result + where + R: bin_proto::BitRead, + E: bin_proto::Endianness, + { + bin_proto::util::decode_items_to_eof::<_, E, _, _>(read, ctx).collect() + } +} + impl TinyVec { /// Returns whether elements are on heap #[inline(always)] diff --git a/tests/arrayvec.rs b/tests/arrayvec.rs index fa08273..f74d7e2 100644 --- a/tests/arrayvec.rs +++ b/tests/arrayvec.rs @@ -1,6 +1,8 @@ #![allow(bad_style)] #![allow(clippy::clone_on_copy)] +#[cfg(feature = "bin-proto")] +use bin_proto::{BigEndian, BitDecodeExt, BitEncodeExt, Tag, Untagged}; #[cfg(feature = "serde")] use serde_test::{assert_tokens, Token}; use std::iter::FromIterator; @@ -469,6 +471,79 @@ fn ArrayVec_borsh_de() { assert_eq!(tv, des); } +#[cfg(feature = "bin-proto")] +#[test] +fn ArrayVec_bin_proto_encode_untagged() { + let mut values = ArrayVec::<[u8; 4]>::new(); + values.push(0x12); + values.push(0x34); + values.push(0x56); + let mut data = [0u8; 16]; + let n_bytes = values + .encode_bytes_ctx_buf(BigEndian, &mut (), Untagged, &mut data) + .unwrap() as usize; + assert_eq!(&[0x12, 0x34, 0x56], &data[0..n_bytes]); +} + +#[cfg(feature = "bin-proto")] +#[test] +fn ArrayVec_bin_proto_decode_tagged_too_long() { + assert!(ArrayVec::<[u8; 4]>::decode_bytes_ctx( + &[0x12, 0x34, 0x56, 0x78, 0x9A], + BigEndian, + &mut (), + Tag(5usize), + ) + .is_err()); +} + +#[cfg(feature = "bin-proto")] +#[test] +fn ArrayVec_bin_proto_decode_tagged() { + let (decoded, read_bits) = ArrayVec::<[u8; 4]>::decode_bytes_ctx( + &[0x12, 0x34, 0x56, 0x78], + BigEndian, + &mut (), + Tag(3usize), + ) + .unwrap(); + let mut expected = ArrayVec::<[u8; 4]>::new(); + expected.push(0x12); + expected.push(0x34); + expected.push(0x56); + assert_eq!(24, read_bits); + assert_eq!(expected, decoded); +} + +#[cfg(feature = "bin-proto")] +#[test] +fn ArrayVec_bin_proto_decode_untagged_too_long() { + let result = ArrayVec::<[u8; 4]>::decode_all_bytes_ctx( + &[0x12, 0x34, 0x56, 0x78, 0x9A], + BigEndian, + &mut (), + Untagged, + ); + assert!(result.is_err()); +} + +#[cfg(feature = "bin-proto")] +#[test] +fn ArrayVec_bin_proto_decode_untagged() { + let decoded = ArrayVec::<[u8; 4]>::decode_all_bytes_ctx( + &[0x12, 0x34, 0x56], + BigEndian, + &mut (), + Untagged, + ) + .unwrap(); + let mut expected = ArrayVec::<[u8; 4]>::new(); + expected.push(0x12); + expected.push(0x34); + expected.push(0x56); + assert_eq!(expected, decoded); +} + #[test] fn ArrayVec_try_from_slice() { use std::convert::TryFrom; diff --git a/tests/tinyvec.rs b/tests/tinyvec.rs index 68c4696..12311ef 100644 --- a/tests/tinyvec.rs +++ b/tests/tinyvec.rs @@ -2,6 +2,8 @@ #![allow(bad_style)] #![allow(clippy::redundant_clone)] +#[cfg(feature = "bin-proto")] +use bin_proto::{BigEndian, BitDecodeExt, BitEncodeExt, Tag, Untagged}; #[cfg(feature = "serde")] use serde_test::{assert_tokens, Token}; use std::iter::FromIterator; @@ -476,6 +478,55 @@ fn TinyVec_borsh_de_heap() { assert_eq!(tv, des); } +#[cfg(feature = "bin-proto")] +#[test] +fn TinyVec_bin_proto_encode_untagged() { + let mut values = TinyVec::<[u8; 2]>::new(); + values.push(0x12); + values.push(0x34); + values.push(0x56); + let mut data = [0u8; 16]; + let n_bytes = values + .encode_bytes_ctx_buf(BigEndian, &mut (), Untagged, &mut data) + .unwrap() as usize; + assert_eq!(&[0x12, 0x34, 0x56], &data[0..n_bytes]); +} + +#[cfg(feature = "bin-proto")] +#[test] +fn TinyVec_bin_proto_decode_tagged() { + let (decoded, read_bits) = TinyVec::<[u8; 2]>::decode_bytes_ctx( + &[0x12, 0x34, 0x56, 0x78], + BigEndian, + &mut (), + Tag(3usize), + ) + .unwrap(); + let mut expected = TinyVec::<[u8; 2]>::new(); + expected.push(0x12); + expected.push(0x34); + expected.push(0x56); + assert_eq!(24, read_bits); + assert_eq!(expected, decoded); +} + +#[cfg(feature = "bin-proto")] +#[test] +fn TinyVec_bin_proto_decode_untagged() { + let decoded = TinyVec::<[u8; 2]>::decode_all_bytes_ctx( + &[0x12, 0x34, 0x56], + BigEndian, + &mut (), + Untagged, + ) + .unwrap(); + let mut expected = TinyVec::<[u8; 2]>::new(); + expected.push(0x12); + expected.push(0x34); + expected.push(0x56); + assert_eq!(expected, decoded); +} + #[test] fn TinyVec_pretty_debug() { let tv: TinyVec<[i32; 6]> = tiny_vec![1, 2, 3];