Skip to content

Commit efbe7b4

Browse files
Add bits as an optional feature for bitvec (#446)
* Make bitvec completely optional * Add bits feature to bench * Add docs * Add bits to ensure_no_std features * Add bits feature to deku-derive * Use bits feature to correctly disallow bit related attributes from being used
1 parent f2aeeb1 commit efbe7b4

35 files changed

+380
-45
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: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@ members = [
2020
]
2121

2222
[features]
23-
default = ["std"]
24-
std = ["deku_derive/std", "bitvec/std", "alloc", "no_std_io/std"]
25-
alloc = ["bitvec/alloc"]
23+
default = ["std", "bits"]
24+
std = ["deku_derive/std", "bitvec?/std", "alloc", "no_std_io/std"]
25+
alloc = ["bitvec?/alloc"]
2626
logging = ["deku_derive/logging", "log"]
2727
no-assert-string = ["deku_derive/no-assert-string"]
2828
error_in_core = []
29+
bits = ["dep:bitvec", "deku_derive/bits"]
2930

3031
[dependencies]
3132
deku_derive = { version = "^0.17.0", path = "deku-derive", default-features = false}
32-
bitvec = { version = "1.0.1", default-features = false }
33+
bitvec = { version = "1.0.1", default-features = false, optional = true }
3334
log = { version = "0.4.21", optional = true }
3435
no_std_io = { version = "0.8.0", default-features = false, features = ["alloc"], package = "no_std_io2" }
3536
rustversion = "1.0.16"
@@ -55,3 +56,31 @@ workspace = true
5556
# Triggers in macro generated code of darling
5657
# https://github.com/rust-lang/rust-clippy/issues/12643
5758
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"

benches/deku.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::io::{Cursor, Read, Seek};
33
use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
44
use deku::prelude::*;
55

6+
#[cfg(feature = "bits")]
67
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
78
struct DekuBits {
89
#[deku(bits = 1)]
@@ -61,6 +62,7 @@ fn criterion_benchmark(c: &mut Criterion) {
6162
}))
6263
})
6364
});
65+
#[cfg(feature = "bits")]
6466
c.bench_function("deku_read_bits", |b| {
6567
let reader = Cursor::new(&[0x01; 1]);
6668
b.iter_batched(
@@ -69,6 +71,7 @@ fn criterion_benchmark(c: &mut Criterion) {
6971
BatchSize::SmallInput,
7072
)
7173
});
74+
#[cfg(feature = "bits")]
7275
c.bench_function("deku_write_bits", |b| {
7376
b.iter(|| {
7477
deku_write(black_box(&DekuBits {

deku-derive/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ proc-macro = true
1616
std = ["proc-macro-crate"]
1717
logging = []
1818
no-assert-string = []
19+
bits = []
1920

2021
[dependencies]
2122
quote = "1.0"

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

0 commit comments

Comments
 (0)