Skip to content

Commit

Permalink
Add support for specifying the id's endianness
Browse files Browse the repository at this point in the history
Doesn't propagate any further down the dependency tree, *only* affects ID parsing.
  • Loading branch information
mpalmer authored and sharksforarms committed Sep 5, 2024
1 parent 616c53d commit dda8d6e
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 1 deletion.
11 changes: 11 additions & 0 deletions deku-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ struct DekuData {
/// enum only: type of the enum `id`
id_type: Option<TokenStream>,

/// enum only: endianness of the enum `id`
id_endian: Option<syn::LitStr>,

/// enum only: bit size of the enum `id`
#[cfg(feature = "bits")]
bits: Option<Num>,
Expand Down Expand Up @@ -202,6 +205,7 @@ impl DekuData {
magic: receiver.magic,
id: receiver.id,
id_type: receiver.id_type?,
id_endian: receiver.id_endian,
#[cfg(feature = "bits")]
bits: receiver.bits,
bytes: receiver.bytes,
Expand Down Expand Up @@ -236,6 +240,8 @@ impl DekuData {
))
} else if data.id.is_some() {
Err(cerror(data.id.span(), "`id` only supported on enum"))
} else if data.id_endian.is_some() {
Err(cerror(data.id.span(), "`id_endian` only supported on enum"))
} else if data.bytes.is_some() {
Err(cerror(data.bytes.span(), "`bytes` only supported on enum"))
} else {
Expand Down Expand Up @@ -348,6 +354,7 @@ impl<'a> TryFrom<&'a DekuData> for DekuDataEnum<'a> {

let id_args = crate::macros::gen_id_args(
deku_data.endian.as_ref(),
deku_data.id_endian.as_ref(),
#[cfg(feature = "bits")]
deku_data.bits.as_ref(),
#[cfg(not(feature = "bits"))]
Expand Down Expand Up @@ -741,6 +748,10 @@ struct DekuReceiver {
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
id_type: Result<Option<TokenStream>, ReplacementError>,

/// enum only: endianness of the enum `id`
#[darling(default)]
id_endian: Option<syn::LitStr>,

/// enum only: bit size of the enum `id`
#[cfg(feature = "bits")]
#[darling(default)]
Expand Down
6 changes: 5 additions & 1 deletion deku-derive/src/macros/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,11 +248,15 @@ fn gen_type_from_ctx_id(
/// `#deku(endian = "big", bytes = 1)` -> `Endian::Big, ByteSize(1)`
pub(crate) fn gen_id_args(
endian: Option<&syn::LitStr>,
id_endian: Option<&syn::LitStr>,
bits: Option<&Num>,
bytes: Option<&Num>,
) -> syn::Result<TokenStream> {
let crate_ = get_crate_name();
let endian = endian.map(gen_endian_from_str).transpose()?;
let endian = id_endian
.map(gen_endian_from_str)
.or_else(|| endian.map(gen_endian_from_str))
.transpose()?;
let bits = bits.map(|n| quote! {::#crate_::ctx::BitSize(#n)});
let bytes = bytes.map(|n| quote! {::#crate_::ctx::ByteSize(#n)});

Expand Down
43 changes: 43 additions & 0 deletions src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ enum DekuEnum {
| [ctx](#ctx) | top-level, field| Context list for context sensitive parsing
| [ctx_default](#ctx_default) | top-level, field| Default context values
| enum: [id](#id) | top-level, variant | enum or variant id value
| enum: [id_endian](#id_endian) | top-level | Endianness of *just* the enum `id`
| enum: [id_pat](#id_pat) | variant | variant id match pattern
| enum: [type](#type) | top-level | Set the type of the variant `id`
| enum: [bits](#bits-1) | top-level | Set the bit-size of the variant `id`
Expand Down Expand Up @@ -1379,6 +1380,48 @@ let variant_bytes: Vec<u8> = value.try_into().unwrap();
assert_eq!(vec![0x02], variant_bytes);
```
# id_endian
Specify the endianness of the variant `id`, without mandating the same endianness for the fields.
Example:
```rust
# use deku::prelude::*;
# use std::convert::{TryInto, TryFrom};
# #[derive(PartialEq, Debug, DekuRead, DekuWrite)]
#[deku(id_type = "u16", id_endian = "big", endian = "little")]
enum DekuTest {
// Takes its endianness from the enum spec
#[deku(id = "0x01")]
VariantLittle(u16),
// Force the endianness on the field
#[deku(id = "0x02")]
VariantBig {
#[deku(endian = "big")]
x: u16,
},
}
let data: Vec<u8> = vec![0x00, 0x01, 0x01, 0x00];
let (_, value) = DekuTest::from_bytes((data.as_ref(), 0)).unwrap();
assert_eq!(
DekuTest::VariantLittle(1),
value
);
// ID changes, data bytes the same
let data: Vec<u8> = vec![0x00, 0x02, 0x01, 0x00];
let (_, value) = DekuTest::from_bytes((data.as_ref(), 0)).unwrap();
assert_eq!(
DekuTest::VariantBig { x: 256 },
value
);
```
# id_pat
Specify the identifier in the form of a match pattern for the enum variant.
Expand Down
24 changes: 24 additions & 0 deletions tests/test_enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,27 @@ fn test_litbool_as_id() {
);
assert_eq!(input, &*v.to_bytes().unwrap());
}

#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
#[deku(id_type = "u16", id_endian = "big", endian = "little")]
enum VariableEndian {
#[deku(id = "0x01")]
Little(u16),
#[deku(id = "0x02")]
Big {
#[deku(endian = "big")]
x: u16,
},
}

#[rstest(input, expected,
case(&hex!("00010100"), VariableEndian::Little(1)),
case(&hex!("00020100"), VariableEndian::Big{x: 256})
)]
fn test_variable_endian_enum(input: &[u8], expected: VariableEndian) {
let ret_read = VariableEndian::try_from(input).unwrap();
assert_eq!(expected, ret_read);

let ret_write: Vec<u8> = ret_read.try_into().unwrap();
assert_eq!(input.to_vec(), ret_write);
}

0 comments on commit dda8d6e

Please sign in to comment.