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 bits as an optional feature for bitvec #446

Merged
merged 5 commits 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
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ jobs:
- run: cargo test --all
# run examples
- run: cargo run --example 2>&1 | grep -P ' ' | awk '{print $1}' | xargs -i cargo run --example {}
# test with no bits feature (don't test docs)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

- run: cargo test --lib --examples --tests --features std --no-default-features

# Only build on MSRV, since trybuild will fail on older version
build-msrv:
Expand Down
37 changes: 33 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@ members = [
]

[features]
default = ["std"]
std = ["deku_derive/std", "bitvec/std", "alloc", "no_std_io/std"]
alloc = ["bitvec/alloc"]
default = ["std", "bits"]
std = ["deku_derive/std", "bitvec?/std", "alloc", "no_std_io/std"]
alloc = ["bitvec?/alloc"]
logging = ["deku_derive/logging", "log"]
no-assert-string = ["deku_derive/no-assert-string"]
error_in_core = []
bits = ["dep:bitvec", "deku_derive/bits"]

[dependencies]
deku_derive = { version = "^0.17.0", path = "deku-derive", default-features = false}
bitvec = { version = "1.0.1", default-features = false }
bitvec = { version = "1.0.1", default-features = false, optional = true }
log = { version = "0.4.21", optional = true }
no_std_io = { version = "0.8.0", default-features = false, features = ["alloc"], package = "no_std_io2" }
rustversion = "1.0.16"
Expand All @@ -55,3 +56,31 @@ workspace = true
# Triggers in macro generated code of darling
# /~https://github.com/rust-lang/rust-clippy/issues/12643
manual-unwrap-or-default = "allow"

[[example]]
name = "custom_reader_and_writer"
required-features = ["bits"]

[[example]]
name = "deku_input"

[[example]]
name = "enums_catch_all"
required-features = ["bits"]

[[example]]
name = "enums"

[[example]]
name = "example"
required-features = ["bits"]

[[example]]
name = "ipv4"
required-features = ["bits"]

[[example]]
name = "many"

[[example]]
name = "read_all"
3 changes: 3 additions & 0 deletions benches/deku.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::io::{Cursor, Read, Seek};
use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
use deku::prelude::*;

#[cfg(feature = "bits")]
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct DekuBits {
#[deku(bits = 1)]
Expand Down Expand Up @@ -61,6 +62,7 @@ fn criterion_benchmark(c: &mut Criterion) {
}))
})
});
#[cfg(feature = "bits")]
c.bench_function("deku_read_bits", |b| {
let reader = Cursor::new(&[0x01; 1]);
b.iter_batched(
Expand All @@ -69,6 +71,7 @@ fn criterion_benchmark(c: &mut Criterion) {
BatchSize::SmallInput,
)
});
#[cfg(feature = "bits")]
c.bench_function("deku_write_bits", |b| {
b.iter(|| {
deku_write(black_box(&DekuBits {
Expand Down
1 change: 1 addition & 0 deletions deku-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ proc-macro = true
std = ["proc-macro-crate"]
logging = []
no-assert-string = []
bits = []

[dependencies]
quote = "1.0"
Expand Down
43 changes: 40 additions & 3 deletions deku-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ struct DekuData {
id_type: Option<TokenStream>,

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

/// enum only: byte size of the enum `id`
Expand Down Expand Up @@ -198,6 +199,7 @@ impl DekuData {
magic: receiver.magic,
id: receiver.id,
id_type: receiver.id_type?,
#[cfg(feature = "bits")]
bits: receiver.bits,
bytes: receiver.bytes,
seek_rewind: receiver.seek_rewind,
Expand All @@ -224,7 +226,7 @@ impl DekuData {
match data.data {
ast::Data::Struct(_) => {
// Validate id_* attributes are being used on an enum
if data.id_type.is_some() {
let ret = if data.id_type.is_some() {
Err(cerror(
data.id_type.span(),
"`id_type` only supported on enum",
Expand All @@ -233,11 +235,16 @@ impl DekuData {
Err(cerror(data.id.span(), "`id` only supported on enum"))
} else if data.bytes.is_some() {
Err(cerror(data.bytes.span(), "`bytes` only supported on enum"))
} else if data.bits.is_some() {
Err(cerror(data.bits.span(), "`bits` only supported on enum"))
} else {
Ok(())
};

#[cfg(feature = "bits")]
if ret.is_ok() && data.bits.is_some() {
return Err(cerror(data.bits.span(), "`bits` only supported on enum"));
}

ret
}
ast::Data::Enum(_) => {
// Validate `id_type` or `id` is specified
Expand All @@ -257,6 +264,7 @@ impl DekuData {
}

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

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

let id_args = crate::macros::gen_id_args(
deku_data.endian.as_ref(),
#[cfg(feature = "bits")]
deku_data.bits.as_ref(),
#[cfg(not(feature = "bits"))]
None,
deku_data.bytes.as_ref(),
)?;

Expand Down Expand Up @@ -393,6 +405,7 @@ struct FieldData {
endian: Option<syn::LitStr>,

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

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

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

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

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

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

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

/// pad a number of bytes after
Expand Down Expand Up @@ -486,9 +502,11 @@ impl FieldData {
ident: receiver.ident,
ty: receiver.ty,
endian: receiver.endian,
#[cfg(feature = "bits")]
bits: receiver.bits,
bytes: receiver.bytes,
count: receiver.count?,
#[cfg(feature = "bits")]
bits_read: receiver.bits_read?,
bytes_read: receiver.bytes_read?,
until: receiver.until?,
Expand All @@ -499,8 +517,10 @@ impl FieldData {
reader: receiver.reader?,
writer: receiver.writer?,
skip: receiver.skip,
#[cfg(feature = "bits")]
pad_bits_before: receiver.pad_bits_before?,
pad_bytes_before: receiver.pad_bytes_before?,
#[cfg(feature = "bits")]
pad_bits_after: receiver.pad_bits_after?,
pad_bytes_after: receiver.pad_bytes_after?,
temp: receiver.temp,
Expand All @@ -524,6 +544,7 @@ impl FieldData {

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

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

#[cfg(not(feature = "bits"))]
if data.count.is_some() && data.bytes_read.is_some() {
return Err(cerror(
data.count.span(),
"conflicting: both `count` and `bytes_read` specified on field",
));
}

// Validate either `bits` or `bytes` is specified
#[cfg(feature = "bits")]
if data.bits.is_some() && data.bytes.is_some() {
// FIXME: Use `Span::join` once out of nightly
return Err(cerror(
Expand All @@ -565,6 +596,7 @@ impl FieldData {
}

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

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

Expand Down Expand Up @@ -816,6 +849,7 @@ struct DekuFieldReceiver {
endian: Option<syn::LitStr>,

/// field bit size
#[cfg(feature = "bits")]
#[darling(default)]
bits: Option<Num>,

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

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

Expand Down Expand Up @@ -871,6 +906,7 @@ struct DekuFieldReceiver {
skip: bool,

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

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

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

Expand Down
Loading
Loading