diff --git a/CHANGELOG.md b/CHANGELOG.md index 7176d82..47b7115 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ * Aliases have been added for the `read` and `write` methods * Add a `hint::Hints` parameter to `Parcel` * Add a `logic::Aligned` type for automatic alignment + * Add a new length prefix type `elements`. Length prefixes of this + type specify the number of elements in a collection. # 0.5 diff --git a/protocol-derive/src/attr.rs b/protocol-derive/src/attr.rs index 7463ed1..1ca3853 100644 --- a/protocol-derive/src/attr.rs +++ b/protocol-derive/src/attr.rs @@ -18,6 +18,7 @@ pub enum Protocol { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum LengthPrefixKind { Bytes, + Elements, } impl LengthPrefixKind { @@ -25,6 +26,7 @@ impl LengthPrefixKind { pub fn path_expr(&self) -> TokenStream { match *self { LengthPrefixKind::Bytes => quote!(protocol::hint::LengthPrefixKind::Bytes), + LengthPrefixKind::Elements => quote!(protocol::hint::LengthPrefixKind::Elements), } } } @@ -60,6 +62,7 @@ pub fn protocol(attrs: &[syn::Attribute]) .expect("expected a nested list"); let prefix_kind = match &nested_list.ident.to_string()[..] { "bytes" => LengthPrefixKind::Bytes, + "elements" => LengthPrefixKind::Elements, invalid_prefix => panic!("invalid length prefix type: '{}'", invalid_prefix), }; diff --git a/protocol/src/attributes/mod.rs b/protocol/src/attributes/mod.rs index a403513..0b6b716 100644 --- a/protocol/src/attributes/mod.rs +++ b/protocol/src/attributes/mod.rs @@ -11,7 +11,6 @@ //! magic_number: u8, //! payload: Vec, //! } -//! //! ``` //! //! # Attributes that apply to items @@ -31,6 +30,9 @@ //! //! #### `bytes` //! +//! When the length prefix type is `bytes`, the length prefix +//! represents the total number of bytes that make up a field. +//! //! ``` //! #[macro_use] extern crate protocol_derive; //! @@ -49,6 +51,28 @@ //! } //! ``` //! +//! #### `elements` +//! +//! When the length prefix type is 'elements', the length prefix +//! represents the number of elements in a collection or list. +//! +//! ``` +//! #[macro_use] extern crate protocol_derive; +//! +//! #[derive(Protocol)] +//! pub struct Bar { +//! /// This field specifes the number of elements in 'data'. +//! pub reason_length: u16, +//! pub other_stuff_inbetween: [u16; 16], +//! pub thingy: bool, +//! /// This field +//! #[protocol(length_prefix(elements(reason_length)))] +//! pub reason: Vec<(u32, u32)>, +//! } +//! ``` +//! +//! # Notes +//! //! This attribute can only be used with named fields. This means structs like //! `struct Hello(u32)` cannot be supported. This is because the length prefix //! field must be specified by a name, and therefore only items with named fields diff --git a/protocol/src/hint.rs b/protocol/src/hint.rs index d54027b..cbd9f4f 100644 --- a/protocol/src/hint.rs +++ b/protocol/src/hint.rs @@ -23,6 +23,8 @@ pub struct FieldLength { pub enum LengthPrefixKind { /// The length prefix stores the total number of bytes making up another field. Bytes, + /// The length prefix stores the total number of elements inside another field. + Elements, } diff --git a/protocol/src/util.rs b/protocol/src/util.rs index d0adc9b..5a28e9a 100644 --- a/protocol/src/util.rs +++ b/protocol/src/util.rs @@ -103,6 +103,9 @@ pub fn read_list_ext(read: &mut Read, Ok(items) }, + hint::LengthPrefixKind::Elements => { + read_items(length.length, read, settings).map(|i| i.collect()) + }, } }, None => { diff --git a/tests/src/length_prefix.rs b/tests/src/length_prefix.rs index 08f99d9..f1db1ba 100644 --- a/tests/src/length_prefix.rs +++ b/tests/src/length_prefix.rs @@ -21,6 +21,14 @@ pub struct Prefix { pub reason_length: u8, } +#[derive(Protocol, Debug, PartialEq, Eq)] +pub struct WithElementsLength { + pub count: u32, + pub foo: bool, + #[protocol(length_prefix(elements(count)))] + pub data: Vec, +} + #[test] fn can_read_length_prefix_5_bytes_string() { assert_eq!(Foo { @@ -39,3 +47,19 @@ fn can_read_length_prefix_8_bytes_u32_array() { }, Foo::from_raw_bytes(&[0, 8, 123, 0, !0, 0, !0, 0, !0, 0, !0], &Settings::default()).unwrap()); } + +#[test] +fn can_read_length_prefix_3_elements() { + assert_eq!(WithElementsLength { + count: 3, + foo: true, + data: vec![1, 2, 3], + }, WithElementsLength::from_raw_bytes( + &[0, 0, 0, 3, // disjoint length prefix + 1, // boolean true + 0, 0, 0, 1, // 1 + 0, 0, 0, 2, // 2 + 0, 0, 0, 3], // 3 + &Settings::default()).unwrap()); +} +