Skip to content

Commit 6fe89e4

Browse files
committed
Add bits feature to deku-derive
* Use bits feature to correctly disallow bit related attributes from being used
1 parent 48d1feb commit 6fe89e4

File tree

29 files changed

+275
-31
lines changed

29 files changed

+275
-31
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ jobs:
2222
- run: cargo test --all
2323
# run examples
2424
- run: cargo run --example 2>&1 | grep -P ' ' | awk '{print $1}' | xargs -i cargo run --example {}
25+
# test with no bits feature (don't test docs)
26+
- run: cargo test --lib --examples --tests --features std --no-default-features
2527

2628
# Only build on MSRV, since trybuild will fail on older version
2729
build-msrv:

Cargo.toml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,31 @@ workspace = true
5656
# Triggers in macro generated code of darling
5757
# https://github.com/rust-lang/rust-clippy/issues/12643
5858
manual-unwrap-or-default = "allow"
59+
60+
[[example]]
61+
name = "custom_reader_and_writer"
62+
required-features = ["bits"]
63+
64+
[[example]]
65+
name = "deku_input"
66+
67+
[[example]]
68+
name = "enums_catch_all"
69+
required-features = ["bits"]
70+
71+
[[example]]
72+
name = "enums"
73+
74+
[[example]]
75+
name = "example"
76+
required-features = ["bits"]
77+
78+
[[example]]
79+
name = "ipv4"
80+
required-features = ["bits"]
81+
82+
[[example]]
83+
name = "many"
84+
85+
[[example]]
86+
name = "read_all"

deku-derive/src/lib.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ struct DekuData {
136136
id_type: Option<TokenStream>,
137137

138138
/// enum only: bit size of the enum `id`
139+
#[cfg(feature = "bits")]
139140
bits: Option<Num>,
140141

141142
/// enum only: byte size of the enum `id`
@@ -198,6 +199,7 @@ impl DekuData {
198199
magic: receiver.magic,
199200
id: receiver.id,
200201
id_type: receiver.id_type?,
202+
#[cfg(feature = "bits")]
201203
bits: receiver.bits,
202204
bytes: receiver.bytes,
203205
seek_rewind: receiver.seek_rewind,
@@ -224,7 +226,7 @@ impl DekuData {
224226
match data.data {
225227
ast::Data::Struct(_) => {
226228
// Validate id_* attributes are being used on an enum
227-
if data.id_type.is_some() {
229+
let ret = if data.id_type.is_some() {
228230
Err(cerror(
229231
data.id_type.span(),
230232
"`id_type` only supported on enum",
@@ -233,11 +235,16 @@ impl DekuData {
233235
Err(cerror(data.id.span(), "`id` only supported on enum"))
234236
} else if data.bytes.is_some() {
235237
Err(cerror(data.bytes.span(), "`bytes` only supported on enum"))
236-
} else if data.bits.is_some() {
237-
Err(cerror(data.bits.span(), "`bits` only supported on enum"))
238238
} else {
239239
Ok(())
240+
};
241+
242+
#[cfg(feature = "bits")]
243+
if ret.is_ok() && data.bits.is_some() {
244+
return Err(cerror(data.bits.span(), "`bits` only supported on enum"));
240245
}
246+
247+
ret
241248
}
242249
ast::Data::Enum(_) => {
243250
// Validate `id_type` or `id` is specified
@@ -257,6 +264,7 @@ impl DekuData {
257264
}
258265

259266
// Validate `id_*` used correctly
267+
#[cfg(feature = "bits")]
260268
if data.id.is_some() && data.bits.is_some() {
261269
return Err(cerror(
262270
data.ident.span(),
@@ -271,6 +279,7 @@ impl DekuData {
271279
}
272280

273281
// Validate either `bits` or `bytes` is specified
282+
#[cfg(feature = "bits")]
274283
if data.bits.is_some() && data.bytes.is_some() {
275284
return Err(cerror(
276285
data.bits.span(),
@@ -336,7 +345,10 @@ impl<'a> TryFrom<&'a DekuData> for DekuDataEnum<'a> {
336345

337346
let id_args = crate::macros::gen_id_args(
338347
deku_data.endian.as_ref(),
348+
#[cfg(feature = "bits")]
339349
deku_data.bits.as_ref(),
350+
#[cfg(not(feature = "bits"))]
351+
None,
340352
deku_data.bytes.as_ref(),
341353
)?;
342354

@@ -393,6 +405,7 @@ struct FieldData {
393405
endian: Option<syn::LitStr>,
394406

395407
/// field bit size
408+
#[cfg(feature = "bits")]
396409
bits: Option<Num>,
397410

398411
/// field byte size
@@ -402,6 +415,7 @@ struct FieldData {
402415
count: Option<TokenStream>,
403416

404417
/// tokens providing the number of bits for the length of the container
418+
#[cfg(feature = "bits")]
405419
bits_read: Option<TokenStream>,
406420

407421
/// tokens providing the number of bytes for the length of the container
@@ -432,12 +446,14 @@ struct FieldData {
432446
skip: bool,
433447

434448
/// pad a number of bits before
449+
#[cfg(feature = "bits")]
435450
pad_bits_before: Option<TokenStream>,
436451

437452
/// pad a number of bytes before
438453
pad_bytes_before: Option<TokenStream>,
439454

440455
/// pad a number of bits after
456+
#[cfg(feature = "bits")]
441457
pad_bits_after: Option<TokenStream>,
442458

443459
/// pad a number of bytes after
@@ -486,9 +502,11 @@ impl FieldData {
486502
ident: receiver.ident,
487503
ty: receiver.ty,
488504
endian: receiver.endian,
505+
#[cfg(feature = "bits")]
489506
bits: receiver.bits,
490507
bytes: receiver.bytes,
491508
count: receiver.count?,
509+
#[cfg(feature = "bits")]
492510
bits_read: receiver.bits_read?,
493511
bytes_read: receiver.bytes_read?,
494512
until: receiver.until?,
@@ -499,8 +517,10 @@ impl FieldData {
499517
reader: receiver.reader?,
500518
writer: receiver.writer?,
501519
skip: receiver.skip,
520+
#[cfg(feature = "bits")]
502521
pad_bits_before: receiver.pad_bits_before?,
503522
pad_bytes_before: receiver.pad_bytes_before?,
523+
#[cfg(feature = "bits")]
504524
pad_bits_after: receiver.pad_bits_after?,
505525
pad_bytes_after: receiver.pad_bytes_after?,
506526
temp: receiver.temp,
@@ -524,6 +544,7 @@ impl FieldData {
524544

525545
fn validate(data: &FieldData) -> Result<(), TokenStream> {
526546
// Validate either `read_bytes` or `read_bits` is specified
547+
#[cfg(feature = "bits")]
527548
if data.bits_read.is_some() && data.bytes_read.is_some() {
528549
return Err(cerror(
529550
data.bits_read.span(),
@@ -532,6 +553,7 @@ impl FieldData {
532553
}
533554

534555
// Validate either `count` or `bits_read`/`bytes_read` is specified
556+
#[cfg(feature = "bits")]
535557
if data.count.is_some() && (data.bits_read.is_some() || data.bytes_read.is_some()) {
536558
if data.bits_read.is_some() {
537559
return Err(cerror(
@@ -546,7 +568,16 @@ impl FieldData {
546568
}
547569
}
548570

571+
#[cfg(not(feature = "bits"))]
572+
if data.count.is_some() && data.bytes_read.is_some() {
573+
return Err(cerror(
574+
data.count.span(),
575+
"conflicting: both `count` and `bytes_read` specified on field",
576+
));
577+
}
578+
549579
// Validate either `bits` or `bytes` is specified
580+
#[cfg(feature = "bits")]
550581
if data.bits.is_some() && data.bytes.is_some() {
551582
// FIXME: Use `Span::join` once out of nightly
552583
return Err(cerror(
@@ -565,6 +596,7 @@ impl FieldData {
565596
}
566597

567598
// Validate usage of read_all
599+
#[cfg(feature = "bits")]
568600
if data.read_all
569601
&& (data.until.is_some()
570602
|| data.count.is_some()
@@ -707,6 +739,7 @@ struct DekuReceiver {
707739
id_type: Result<Option<TokenStream>, ReplacementError>,
708740

709741
/// enum only: bit size of the enum `id`
742+
#[cfg(feature = "bits")]
710743
#[darling(default)]
711744
bits: Option<Num>,
712745

@@ -816,6 +849,7 @@ struct DekuFieldReceiver {
816849
endian: Option<syn::LitStr>,
817850

818851
/// field bit size
852+
#[cfg(feature = "bits")]
819853
#[darling(default)]
820854
bits: Option<Num>,
821855

@@ -828,6 +862,7 @@ struct DekuFieldReceiver {
828862
count: Result<Option<TokenStream>, ReplacementError>,
829863

830864
/// tokens providing the number of bits for the length of the container
865+
#[cfg(feature = "bits")]
831866
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
832867
bits_read: Result<Option<TokenStream>, ReplacementError>,
833868

@@ -871,6 +906,7 @@ struct DekuFieldReceiver {
871906
skip: bool,
872907

873908
/// pad a number of bits before
909+
#[cfg(feature = "bits")]
874910
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
875911
pad_bits_before: Result<Option<TokenStream>, ReplacementError>,
876912

@@ -879,6 +915,7 @@ struct DekuFieldReceiver {
879915
pad_bytes_before: Result<Option<TokenStream>, ReplacementError>,
880916

881917
/// pad a number of bits after
918+
#[cfg(feature = "bits")]
882919
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
883920
pad_bits_after: Result<Option<TokenStream>, ReplacementError>,
884921

deku-derive/src/macros/deku_read.rs

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use quote::quote;
77

88
use crate::macros::{
99
gen_ctx_types_and_arg, gen_field_args, gen_internal_field_ident, gen_internal_field_idents,
10-
gen_type_from_ctx_id, pad_bits, token_contains_string, wrap_default_ctx,
10+
gen_type_from_ctx_id, token_contains_string, wrap_default_ctx,
1111
};
1212
use crate::{DekuData, DekuDataEnum, DekuDataStruct, FieldData, Id};
1313

@@ -557,6 +557,7 @@ fn emit_bit_byte_offsets(
557557
(bit_offset, byte_offset)
558558
}
559559

560+
#[cfg(feature = "bits")]
560561
fn emit_padding(bit_size: &TokenStream) -> TokenStream {
561562
let crate_ = super::get_crate_name();
562563
quote! {
@@ -584,6 +585,29 @@ fn emit_padding(bit_size: &TokenStream) -> TokenStream {
584585
}
585586
}
586587

588+
// TODO: if this is a simple calculation such as "8 + 2", this could be const
589+
#[cfg(not(feature = "bits"))]
590+
fn emit_padding_bytes(bit_size: &TokenStream) -> TokenStream {
591+
let crate_ = super::get_crate_name();
592+
quote! {
593+
{
594+
use core::convert::TryFrom;
595+
extern crate alloc;
596+
use alloc::borrow::Cow;
597+
let __deku_pad = usize::try_from(#bit_size).map_err(|e|
598+
::#crate_::DekuError::InvalidParam(Cow::from(format!(
599+
"Invalid padding param \"({})\": cannot convert to usize",
600+
stringify!(#bit_size)
601+
)))
602+
)?;
603+
604+
605+
let mut buf = vec![0; __deku_pad];
606+
let _ = __deku_reader.read_bytes(__deku_pad, &mut buf)?;
607+
}
608+
}
609+
}
610+
587611
fn emit_field_read(
588612
input: &DekuData,
589613
i: usize,
@@ -601,6 +625,7 @@ fn emit_field_read(
601625
// fields to check usage of bit/byte offset
602626
let field_check_vars = [
603627
&f.count,
628+
#[cfg(feature = "bits")]
604629
&f.bits_read,
605630
&f.bytes_read,
606631
&f.until,
@@ -704,7 +729,10 @@ fn emit_field_read(
704729
} else {
705730
let read_args = gen_field_args(
706731
field_endian,
732+
#[cfg(feature = "bits")]
707733
f.bits.as_ref(),
734+
#[cfg(not(feature = "bits"))]
735+
None,
708736
f.bytes.as_ref(),
709737
f.ctx.as_ref(),
710738
)?;
@@ -741,17 +769,6 @@ fn emit_field_read(
741769
)?
742770
}
743771
}
744-
} else if let Some(field_bits) = &f.bits_read {
745-
quote! {
746-
{
747-
use core::borrow::Borrow;
748-
#type_as_deku_read::from_reader_with_ctx
749-
(
750-
__deku_reader,
751-
(::#crate_::ctx::Limit::new_bit_size(::#crate_::ctx::BitSize(usize::try_from(*((#field_bits).borrow()))?)), (#read_args))
752-
)?
753-
}
754-
}
755772
} else if let Some(field_bytes) = &f.bytes_read {
756773
quote! {
757774
{
@@ -785,27 +802,54 @@ fn emit_field_read(
785802
}
786803
}
787804
} else {
788-
quote! {
789-
#type_as_deku_read::from_reader_with_ctx
790-
(
791-
__deku_reader,
792-
(#read_args)
793-
)?
805+
let mut ret = quote! {};
806+
807+
#[cfg(feature = "bits")]
808+
if let Some(field_bits) = &f.bits_read {
809+
ret.extend(quote! {
810+
{
811+
use core::borrow::Borrow;
812+
#type_as_deku_read::from_reader_with_ctx
813+
(
814+
__deku_reader,
815+
(::#crate_::ctx::Limit::new_bit_size(::#crate_::ctx::BitSize(usize::try_from(*((#field_bits).borrow()))?)), (#read_args))
816+
)?
817+
}
818+
})
819+
}
820+
if ret.is_empty() {
821+
ret.extend(quote! {
822+
#type_as_deku_read::from_reader_with_ctx
823+
(
824+
__deku_reader,
825+
(#read_args)
826+
)?
827+
})
794828
}
829+
830+
ret
795831
}
796832
};
797833

798-
let pad_bits_before = pad_bits(
834+
#[cfg(feature = "bits")]
835+
let pad_bits_before = crate::macros::pad_bits(
799836
f.pad_bits_before.as_ref(),
800837
f.pad_bytes_before.as_ref(),
801838
emit_padding,
802839
);
803-
let pad_bits_after = pad_bits(
840+
#[cfg(feature = "bits")]
841+
let pad_bits_after = crate::macros::pad_bits(
804842
f.pad_bits_after.as_ref(),
805843
f.pad_bytes_after.as_ref(),
806844
emit_padding,
807845
);
808846

847+
#[cfg(not(feature = "bits"))]
848+
let pad_bits_before = crate::macros::pad_bytes(f.pad_bytes_before.as_ref(), emit_padding_bytes);
849+
850+
#[cfg(not(feature = "bits"))]
851+
let pad_bits_after = crate::macros::pad_bytes(f.pad_bytes_after.as_ref(), emit_padding_bytes);
852+
809853
let field_read_normal = quote! {
810854
let __deku_value = #field_read_func;
811855
let __deku_value: #field_type = #field_map(__deku_value)?;

0 commit comments

Comments
 (0)