Skip to content

Commit e5363bc

Browse files
amboarmkj
andauthored
Expose alloc as a selectable feature (sharksforarms#582)
Co-authored-by: Matt Johnston <[email protected]>
1 parent 257bbd2 commit e5363bc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+1180
-474
lines changed

.github/workflows/main.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ jobs:
2020
- run: cargo build --all
2121
# test
2222
- run: cargo test --all
23+
- run: cargo test --all-features
24+
- run: cargo test --no-default-features
25+
- run: cargo test --no-default-features --features=alloc
26+
- run: cargo test --no-default-features --features=descriptive-errors
27+
- run: cargo test --no-default-features --features=bits
28+
- run: cargo test --no-default-features --features=logging
2329
# run examples
2430
- 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
2731

2832
# Only build on MSRV, since trybuild will fail on older version
2933
build-msrv:
@@ -64,6 +68,7 @@ jobs:
6468
toolchain: nightly
6569
targets: thumbv7em-none-eabihf
6670
- run: cd ensure_no_std && cargo build --release --target thumbv7em-none-eabihf
71+
- run: cd ensure_no_std && cargo build --release --target thumbv7em-none-eabihf --no-default-features
6772

6873
ensure_wasm:
6974
name: Ensure wasm

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
## [Unreleased]
44

5+
## Added
6+
7+
- The `alloc` feature, allowing use in environments lacking a heap [#582](https://github.com/sharksforarms/deku/pull/582)
8+
- The `descriptive-errors` feature, replacing `no-assertion-string` [#582](https://github.com/sharksforarms/deku/pull/582)
9+
510
## [0.19.1] - 2025-05-22
611

712
## Added

Cargo.toml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@ members = [
2020
]
2121

2222
[features]
23-
default = ["std", "bits"]
23+
default = ["std", "bits", "descriptive-errors"]
2424
std = ["deku_derive/std", "bitvec?/std", "alloc", "no_std_io/std"]
25-
alloc = ["bitvec?/alloc"]
25+
alloc = ["bitvec?/alloc", "deku_derive/alloc", "no_std_io/alloc" ]
2626
logging = ["deku_derive/logging", "log"]
27-
no-assert-string = ["deku_derive/no-assert-string"]
28-
bits = ["dep:bitvec", "deku_derive/bits"]
27+
bits = ["dep:bitvec", "deku_derive/bits", "alloc" ]
28+
descriptive-errors = ["alloc"]
2929

3030
[dependencies]
3131
deku_derive = { version = "^0.19.1", path = "deku-derive", default-features = false}
3232
bitvec = { version = "1.0.1", default-features = false, optional = true }
3333
log = { version = "0.4.22", optional = true }
34-
no_std_io = { version = "0.9.0", default-features = false, features = ["alloc"], package = "no_std_io2" }
34+
no_std_io = { version = "0.9.0", default-features = false, package = "no_std_io2" }
3535
rustversion = "1.0.17"
3636

3737
[dev-dependencies]
@@ -48,6 +48,7 @@ log = { version = "0.4.22" }
4848
[[bench]]
4949
name = "deku"
5050
harness = false
51+
required-features = ["alloc"]
5152

5253
[lints]
5354
workspace = true
@@ -63,13 +64,15 @@ required-features = ["bits"]
6364

6465
[[example]]
6566
name = "deku_input"
67+
required-features = ["std"]
6668

6769
[[example]]
6870
name = "enums_catch_all"
6971
required-features = ["bits"]
7072

7173
[[example]]
7274
name = "enums"
75+
required-features = ["std"]
7376

7477
[[example]]
7578
name = "example"
@@ -85,6 +88,8 @@ required-features = ["bits"]
8588

8689
[[example]]
8790
name = "many"
91+
required-features = ["std"]
8892

8993
[[example]]
9094
name = "read_all"
95+
required-features = ["std"]

benches/deku.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::io::{Cursor, Read, Seek};
1+
use no_std_io::io::{Cursor, Read, Seek};
22

33
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
44
use deku::prelude::*;
@@ -132,6 +132,7 @@ pub fn read_all_vs_count_vs_read_exact(c: &mut Criterion) {
132132

133133
#[derive(DekuRead, DekuWrite)]
134134
#[deku(ctx = "len: usize")]
135+
#[expect(dead_code)]
135136
pub struct CountFromCtxWrapper {
136137
#[deku(count = "len")]
137138
pub data: Vec<u8>,

deku-derive/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ rust-version = "1.81"
1313
proc-macro = true
1414

1515
[features]
16-
std = ["proc-macro-crate"]
16+
std = ["proc-macro-crate", "alloc"]
17+
alloc = []
1718
logging = []
18-
no-assert-string = []
1919
bits = []
2020

2121
[dependencies]

deku-derive/src/lib.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,36 @@ use crate::macros::deku_write::emit_deku_write;
2121

2222
mod macros;
2323

24+
// Pad attribute values are not bounded by the implementation, however, for
25+
// implementation purposes we need a concrete object to contain padding data.
26+
// To allow use of padding in constrained environments we define the object as
27+
// an array (by contrast to a vec). PAD_ARRAY_SIZE controls the length of
28+
// these arrays.
29+
//
30+
// The length of the padding array is chosen with the following considerations:
31+
//
32+
// - A loop is necessary to avoid arbitrarily picking an upper-bound for
33+
// requested padding values
34+
//
35+
// - Given the loop there aren't any strong constraints on the size of the
36+
// padding array
37+
//
38+
// - My estimate is padding requests tend to be in the range of 1-16 bytes.
39+
// From a [brief inspection][github-search-pad-bytes] it seems that estimate
40+
// is reasonable
41+
//
42+
// - We desire a value large enough to cover common cases and avoid unnecessary
43+
// loop trips, but small enough not to cause trouble on the stack (for
44+
// instance, requesting a page of zeros feels unreasonable)
45+
//
46+
// - Try to pick something that only requires 1 execution of the loop body for
47+
// common cases.
48+
//
49+
// [github-search-pad-bytes]:
50+
// https://github.com/search?q=pad_bytes_before+OR+pad_bytes_after&type=code
51+
#[cfg(not(feature = "bits"))]
52+
const PAD_ARRAY_SIZE: usize = 64;
53+
2454
#[derive(Debug)]
2555
enum Id {
2656
TokenStream(TokenStream),

deku-derive/src/macros/deku_read.rs

Lines changed: 24 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@ use darling::ast::{Data, Fields};
44
use darling::ToTokens;
55
use proc_macro2::TokenStream;
66
use quote::quote;
7+
#[cfg(feature = "bits")]
78
use syn::LitStr;
89
use syn::{Ident, LitByteStr};
910

11+
#[cfg(feature = "bits")]
12+
use crate::macros::gen_bit_order_from_str;
13+
1014
use crate::macros::{
11-
assertion_failed, gen_bit_order_from_str, gen_ctx_types_and_arg, gen_field_args,
12-
gen_internal_field_idents, token_contains_string, wrap_default_ctx,
15+
assertion_failed, gen_ctx_types_and_arg, gen_field_args, gen_internal_field_idents,
16+
token_contains_string, wrap_default_ctx,
1317
};
1418
use crate::{from_token, DekuData, DekuDataEnum, DekuDataStruct, FieldData, Id};
1519

@@ -356,16 +360,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
356360
if !has_default_match && default_reader.is_none() {
357361
variant_matches.push(quote! {
358362
_ => {
359-
extern crate alloc;
360-
use alloc::borrow::Cow;
361-
use alloc::format;
362-
return Err(::#crate_::DekuError::Parse(
363-
Cow::from(format!(
364-
"Could not match enum variant id = {:?} on enum `{}`",
365-
__deku_variant_id,
366-
#ident_as_string
367-
))
368-
));
363+
return Err(::#crate_::deku_error!(::#crate_::DekuError::Parse, "Could not match enum variant", "ID {:?} not found on {}", __deku_variant_id, #ident_as_string));
369364
}
370365
});
371366
}
@@ -540,10 +535,7 @@ fn emit_magic_read_lit(crate_: &Ident, magic: &LitByteStr) -> TokenStream {
540535
for __deku_byte in __deku_magic {
541536
let __deku_read_byte = u8::from_reader_with_ctx(__deku_reader, ())?;
542537
if *__deku_byte != __deku_read_byte {
543-
extern crate alloc;
544-
use alloc::borrow::Cow;
545-
use alloc::format;
546-
return Err(::#crate_::DekuError::Parse(Cow::from(format!("Missing magic value {:?}", #magic))));
538+
return Err(::#crate_::deku_error!(::#crate_::DekuError::Parse, "Missing magic value", "{:?}", #magic));
547539
}
548540
}
549541
}
@@ -615,19 +607,13 @@ fn emit_padding(bit_size: &TokenStream, bit_order: Option<&LitStr>) -> TokenStre
615607
let order = gen_bit_order_from_str(bit_order).unwrap();
616608
quote! {
617609
{
618-
use core::convert::TryFrom;
619-
// TODO: I hope this consts in most cases?
620610
extern crate alloc;
621-
use alloc::borrow::Cow;
622-
use alloc::format;
611+
612+
use core::convert::TryFrom;
623613
let __deku_pad = usize::try_from(#bit_size).map_err(|e|
624-
::#crate_::DekuError::InvalidParam(Cow::from(format!(
625-
"Invalid padding param \"({})\": cannot convert to usize",
626-
stringify!(#bit_size)
627-
)))
614+
::#crate_::deku_error!(::#crate_::DekuError::InvalidParam, "Invalid padding param, cannot convert ot usize", "{}", stringify!(#bit_size))
628615
)?;
629616

630-
631617
if (__deku_pad % 8) == 0 {
632618
let bytes_read = __deku_pad / 8;
633619
let mut buf = alloc::vec![0; bytes_read];
@@ -642,19 +628,13 @@ fn emit_padding(bit_size: &TokenStream, bit_order: Option<&LitStr>) -> TokenStre
642628
} else {
643629
quote! {
644630
{
645-
use core::convert::TryFrom;
646-
// TODO: I hope this consts in most cases?
647631
extern crate alloc;
648-
use alloc::borrow::Cow;
649-
use alloc::format;
632+
633+
use core::convert::TryFrom;
650634
let __deku_pad = usize::try_from(#bit_size).map_err(|e|
651-
::#crate_::DekuError::InvalidParam(Cow::from(format!(
652-
"Invalid padding param \"({})\": cannot convert to usize",
653-
stringify!(#bit_size)
654-
)))
635+
::#crate_::deku_error!(::#crate_::DekuError::InvalidParam, "Invalid padding param, cannot convert to usize", "{}", stringify!(#bit_size))
655636
)?;
656637

657-
658638
if (__deku_pad % 8) == 0 {
659639
let bytes_read = __deku_pad / 8;
660640
let mut buf = alloc::vec![0; bytes_read];
@@ -673,22 +653,20 @@ fn emit_padding(bit_size: &TokenStream, bit_order: Option<&LitStr>) -> TokenStre
673653
#[cfg(not(feature = "bits"))]
674654
fn emit_padding_bytes(bit_size: &TokenStream) -> TokenStream {
675655
let crate_ = super::get_crate_name();
656+
let pad = crate::PAD_ARRAY_SIZE;
676657
quote! {
677658
{
678659
use core::convert::TryFrom;
679-
extern crate alloc;
680-
use alloc::borrow::Cow;
681-
use alloc::format;
682-
let __deku_pad = usize::try_from(#bit_size).map_err(|e|
683-
::#crate_::DekuError::InvalidParam(Cow::from(format!(
684-
"Invalid padding param \"({})\": cannot convert to usize",
685-
stringify!(#bit_size)
686-
)))
660+
let mut __deku_pad = usize::try_from(#bit_size).map_err(|e|
661+
::#crate_::deku_error!(::#crate_::DekuError::InvalidParam, "Invalid padding param, cannot convert to usize", "{}", stringify!(#bit_size))
687662
)?;
688663

689-
690-
let mut buf = alloc::vec![0; __deku_pad];
691-
let _ = __deku_reader.read_bytes(__deku_pad, &mut buf, ::#crate_::ctx::Order::default())?;
664+
while __deku_pad > 0 {
665+
let mut __deku_pad_source = [0u8; #pad];
666+
let __deku_pad_chunk = core::cmp::min(__deku_pad_source.len(), __deku_pad);
667+
__deku_reader.read_bytes(__deku_pad_chunk, &mut __deku_pad_source[..__deku_pad_chunk], ::#crate_::ctx::Order::default())?;
668+
__deku_pad -= __deku_pad_chunk;
669+
}
692670
}
693671
}
694672
}
@@ -1094,9 +1072,7 @@ pub fn emit_try_from(
10941072
let mut cursor = ::#crate_::no_std_io::Cursor::new(input);
10951073
let (amt_read, res) = <Self as ::#crate_::DekuContainerRead>::from_reader((&mut cursor, 0))?;
10961074
if (amt_read / 8) != total_len {
1097-
extern crate alloc;
1098-
use alloc::borrow::Cow;
1099-
return Err(::#crate_::DekuError::Parse(Cow::from("Too much data")));
1075+
return Err(::#crate_::deku_error!(::#crate_::DekuError::Parse, "Too much data"));
11001076
}
11011077
Ok(res)
11021078
}

0 commit comments

Comments
 (0)