Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for specifying the id's endianness #476

Merged
merged 1 commit into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
Loading