Skip to content

Commit a9ebc26

Browse files
committed
Add bits feature to deku-derive
* Use bits feature to correctly disallow bit related attributes from being used
1 parent d33e632 commit a9ebc26

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

@@ -558,6 +558,7 @@ fn emit_bit_byte_offsets(
558558
(bit_offset, byte_offset)
559559
}
560560

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

589+
// TODO: if this is a simple calculation such as "8 + 2", this could be const
590+
#[cfg(not(feature = "bits"))]
591+
fn emit_padding_bytes(bit_size: &TokenStream) -> TokenStream {
592+
let crate_ = super::get_crate_name();
593+
quote! {
594+
{
595+
use core::convert::TryFrom;
596+
extern crate alloc;
597+
use alloc::borrow::Cow;
598+
let __deku_pad = usize::try_from(#bit_size).map_err(|e|
599+
::#crate_::DekuError::InvalidParam(Cow::from(format!(
600+
"Invalid padding param \"({})\": cannot convert to usize",
601+
stringify!(#bit_size)
602+
)))
603+
)?;
604+
605+
606+
let mut buf = vec![0; __deku_pad];
607+
let _ = __deku_reader.read_bytes(__deku_pad, &mut buf)?;
608+
}
609+
}
610+
}
611+
588612
fn emit_field_read(
589613
input: &DekuData,
590614
i: usize,
@@ -602,6 +626,7 @@ fn emit_field_read(
602626
// fields to check usage of bit/byte offset
603627
let field_check_vars = [
604628
&f.count,
629+
#[cfg(feature = "bits")]
605630
&f.bits_read,
606631
&f.bytes_read,
607632
&f.until,
@@ -705,7 +730,10 @@ fn emit_field_read(
705730
} else {
706731
let read_args = gen_field_args(
707732
field_endian,
733+
#[cfg(feature = "bits")]
708734
f.bits.as_ref(),
735+
#[cfg(not(feature = "bits"))]
736+
None,
709737
f.bytes.as_ref(),
710738
f.ctx.as_ref(),
711739
)?;
@@ -751,17 +779,6 @@ fn emit_field_read(
751779
)?
752780
}
753781
}
754-
} else if let Some(field_bits) = &f.bits_read {
755-
quote! {
756-
{
757-
use core::borrow::Borrow;
758-
#type_as_deku_read::from_reader_with_ctx
759-
(
760-
__deku_reader,
761-
(::#crate_::ctx::Limit::new_bit_size(::#crate_::ctx::BitSize(usize::try_from(*((#field_bits).borrow()))?)), (#read_args))
762-
)?
763-
}
764-
}
765782
} else if let Some(field_bytes) = &f.bytes_read {
766783
quote! {
767784
{
@@ -795,27 +812,54 @@ fn emit_field_read(
795812
}
796813
}
797814
} else {
798-
quote! {
799-
#type_as_deku_read::from_reader_with_ctx
800-
(
801-
__deku_reader,
802-
(#read_args)
803-
)?
815+
let mut ret = quote! {};
816+
817+
#[cfg(feature = "bits")]
818+
if let Some(field_bits) = &f.bits_read {
819+
ret.extend(quote! {
820+
{
821+
use core::borrow::Borrow;
822+
#type_as_deku_read::from_reader_with_ctx
823+
(
824+
__deku_reader,
825+
(::#crate_::ctx::Limit::new_bit_size(::#crate_::ctx::BitSize(usize::try_from(*((#field_bits).borrow()))?)), (#read_args))
826+
)?
827+
}
828+
})
829+
}
830+
if ret.is_empty() {
831+
ret.extend(quote! {
832+
#type_as_deku_read::from_reader_with_ctx
833+
(
834+
__deku_reader,
835+
(#read_args)
836+
)?
837+
})
804838
}
839+
840+
ret
805841
}
806842
};
807843

808-
let pad_bits_before = pad_bits(
844+
#[cfg(feature = "bits")]
845+
let pad_bits_before = crate::macros::pad_bits(
809846
f.pad_bits_before.as_ref(),
810847
f.pad_bytes_before.as_ref(),
811848
emit_padding,
812849
);
813-
let pad_bits_after = pad_bits(
850+
#[cfg(feature = "bits")]
851+
let pad_bits_after = crate::macros::pad_bits(
814852
f.pad_bits_after.as_ref(),
815853
f.pad_bytes_after.as_ref(),
816854
emit_padding,
817855
);
818856

857+
#[cfg(not(feature = "bits"))]
858+
let pad_bits_before = crate::macros::pad_bytes(f.pad_bytes_before.as_ref(), emit_padding_bytes);
859+
860+
#[cfg(not(feature = "bits"))]
861+
let pad_bits_after = crate::macros::pad_bytes(f.pad_bytes_after.as_ref(), emit_padding_bytes);
862+
819863
let field_read_normal = quote! {
820864
let __deku_value = #field_read_func;
821865
let __deku_value: #field_type = #field_map(__deku_value)?;

0 commit comments

Comments
 (0)