Skip to content

Commit 59fc760

Browse files
Add count Vec<u8> Specializations (#481)
* For Vec<u8> when using count, specialize into reading the bytes all at once See #462
1 parent d6383b6 commit 59fc760

File tree

8 files changed

+126
-20
lines changed

8 files changed

+126
-20
lines changed

benches/deku.rs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,16 +111,28 @@ fn criterion_benchmark(c: &mut Criterion) {
111111
});
112112
}
113113

114-
pub fn read_all_vs_count(c: &mut Criterion) {
114+
pub fn read_all_vs_count_vs_read_exact(c: &mut Criterion) {
115115
#[derive(DekuRead, DekuWrite)]
116116
pub struct AllWrapper {
117117
#[deku(read_all)]
118118
pub data: Vec<u8>,
119119
}
120120

121121
#[derive(DekuRead, DekuWrite)]
122-
#[deku(ctx = "len: usize")]
123122
pub struct CountWrapper {
123+
#[deku(count = "1500")]
124+
pub data: Vec<u8>,
125+
}
126+
127+
#[derive(DekuRead, DekuWrite)]
128+
pub struct CountNonSpecialize {
129+
#[deku(count = "(1500/2)")]
130+
pub data: Vec<u16>,
131+
}
132+
133+
#[derive(DekuRead, DekuWrite)]
134+
#[deku(ctx = "len: usize")]
135+
pub struct CountFromCtxWrapper {
124136
#[deku(count = "len")]
125137
pub data: Vec<u8>,
126138
}
@@ -137,14 +149,34 @@ pub fn read_all_vs_count(c: &mut Criterion) {
137149
})
138150
});
139151

140-
c.bench_function("count", |b| {
152+
c.bench_function("count_specialize", |b| {
153+
b.iter(|| {
154+
let mut cursor = Cursor::new([1u8; 1500].as_ref());
155+
let mut reader = Reader::new(&mut cursor);
156+
CountWrapper::from_reader_with_ctx(black_box(&mut reader), ())
157+
})
158+
});
159+
160+
c.bench_function("count_from_u8_specialize", |b| {
161+
b.iter(|| {
162+
let mut cursor = Cursor::new([1u8; 1500].as_ref());
163+
let mut reader = Reader::new(&mut cursor);
164+
CountWrapper::from_reader_with_ctx(black_box(&mut reader), ())
165+
})
166+
});
167+
168+
c.bench_function("count_no_specialize", |b| {
141169
b.iter(|| {
142170
let mut cursor = Cursor::new([1u8; 1500].as_ref());
143171
let mut reader = Reader::new(&mut cursor);
144-
CountWrapper::from_reader_with_ctx(black_box(&mut reader), 1500)
172+
CountNonSpecialize::from_reader_with_ctx(black_box(&mut reader), ())
145173
})
146174
});
147175
}
148176

149-
criterion_group!(benches, criterion_benchmark, read_all_vs_count);
177+
criterion_group!(
178+
benches,
179+
criterion_benchmark,
180+
read_all_vs_count_vs_read_exact
181+
);
150182
criterion_main!(benches);

deku-derive/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extern crate alloc;
88
use alloc::borrow::Cow;
99
use std::convert::TryFrom;
1010
use std::fmt::Display;
11+
use syn::Type;
1112

1213
use darling::{ast, FromDeriveInput, FromField, FromMeta, FromVariant, ToTokens};
1314
use proc_macro2::TokenStream;
@@ -412,7 +413,7 @@ impl<'a> TryFrom<&'a DekuData> for DekuDataStruct<'a> {
412413
#[derive(Debug)]
413414
struct FieldData {
414415
ident: Option<syn::Ident>,
415-
ty: syn::Type,
416+
ty: Type,
416417

417418
/// endianness for the field
418419
endian: Option<syn::LitStr>,
@@ -859,7 +860,7 @@ fn default_res_opt<T, E>() -> Result<Option<T>, E> {
859860
#[darling(attributes(deku))]
860861
struct DekuFieldReceiver {
861862
ident: Option<syn::Ident>,
862-
ty: syn::Type,
863+
ty: Type,
863864

864865
/// Endianness for the field
865866
#[darling(default)]

deku-derive/src/macros/deku_read.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -777,14 +777,44 @@ fn emit_field_read(
777777
}
778778
}
779779
} else if let Some(field_count) = &f.count {
780-
quote! {
781-
{
782-
use core::borrow::Borrow;
783-
#type_as_deku_read::from_reader_with_ctx
784-
(
785-
__deku_reader,
786-
(::#crate_::ctx::Limit::new_count(usize::try_from(*((#field_count).borrow()))?), (#read_args))
787-
)?
780+
use syn::{GenericArgument, PathArguments, Type};
781+
let mut is_vec_u8 = false;
782+
if let Type::Path(type_path) = &f.ty {
783+
if type_path.path.segments.len() == 1 && type_path.path.segments[0].ident == "Vec" {
784+
if let PathArguments::AngleBracketed(ref generic_args) =
785+
type_path.path.segments[0].arguments
786+
{
787+
if generic_args.args.len() == 1 {
788+
if let GenericArgument::Type(Type::Path(ref arg_path)) =
789+
generic_args.args[0]
790+
{
791+
is_vec_u8 = arg_path.path.is_ident("u8");
792+
}
793+
}
794+
}
795+
}
796+
}
797+
if is_vec_u8 {
798+
quote! {
799+
{
800+
use core::borrow::Borrow;
801+
#type_as_deku_read::from_reader_with_ctx
802+
(
803+
__deku_reader,
804+
::#crate_::ctx::ReadExact(usize::try_from(*((#field_count).borrow()))?)
805+
)?
806+
}
807+
}
808+
} else {
809+
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_count(usize::try_from(*((#field_count).borrow()))?), (#read_args))
816+
)?
817+
}
788818
}
789819
}
790820
} else if let Some(field_bytes) = &f.bytes_read {

ensure_no_std/src/bin/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ struct DekuTest {
2424
count: u8,
2525
#[deku(count = "count", pad_bytes_after = "8")]
2626
data: Vec<u8>,
27+
#[deku(count = "1")]
28+
after: Vec<u8>,
2729
}
2830

2931
#[entry]
@@ -39,7 +41,7 @@ fn main() -> ! {
3941
// now the allocator is ready types like Box, Vec can be used.
4042

4143
#[allow(clippy::unusual_byte_groupings)]
42-
let test_data: &[u8] = &[0b10101_101, 0x02, 0xBE, 0xEF, 0xff];
44+
let test_data: &[u8] = &[0b10101_101, 0x02, 0xBE, 0xEF, 0xff, 0xaa];
4345
let mut cursor = deku::no_std_io::Cursor::new(test_data);
4446

4547
// Test reading
@@ -49,7 +51,8 @@ fn main() -> ! {
4951
field_a: 0b10101,
5052
field_b: 0b101,
5153
count: 0x02,
52-
data: vec![0xBE, 0xEF]
54+
data: vec![0xBE, 0xEF],
55+
after: vec![0xaa],
5356
},
5457
val
5558
);

src/attributes.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,8 @@ assert_eq!(data, value);
649649
650650
**Note**: See [update](#update) for more information on the attribute!
651651
652+
## Specializations
653+
- `Vec<u8>`: `count` used with a byte vector will result in one invocation to `read_bytes`, thus improving performance.
652654
653655
# bytes_read
654656
@@ -769,7 +771,6 @@ let value: Vec<u8> = value.try_into().unwrap();
769771
assert_eq!(&*data, value);
770772
```
771773
772-
773774
# update
774775
775776
Specify custom code to run on the field when `.update()` is called on the struct/enum

src/ctx.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,3 +201,6 @@ impl BitSize {
201201
Self::bits_from_reader(core::mem::size_of_val(val))
202202
}
203203
}
204+
205+
/// Amount of bytes to read_exact
206+
pub struct ReadExact(pub usize);

src/impls/vec.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,20 @@ use crate::writer::Writer;
88
use crate::{ctx::*, DekuReader};
99
use crate::{DekuError, DekuWriter};
1010

11+
impl<'a> DekuReader<'a, ReadExact> for Vec<u8> {
12+
fn from_reader_with_ctx<R: Read + Seek>(
13+
reader: &mut Reader<R>,
14+
exact: ReadExact,
15+
) -> Result<Self, DekuError>
16+
where
17+
Self: Sized,
18+
{
19+
let mut bytes = alloc::vec![0x00; exact.0];
20+
let _ = reader.read_bytes(exact.0, &mut bytes)?;
21+
Ok(bytes)
22+
}
23+
}
24+
1125
/// Read `T`s into a vec until a given predicate returns true
1226
/// * `capacity` - an optional capacity to pre-allocate the vector with
1327
/// * `ctx` - The context required by `T`. It will be passed to every `T` when constructing.

tests/test_attributes/test_limits/test_count.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,28 @@ mod test_slice {
2727
assert_eq!(test_data, ret_write);
2828
}
2929

30+
#[test]
31+
fn test_count_static_non_u8() {
32+
#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
33+
struct TestStruct {
34+
#[deku(count = "1")]
35+
data: Vec<(u8, u8)>,
36+
}
37+
38+
let test_data: Vec<u8> = [0xaa, 0xbb].to_vec();
39+
40+
let ret_read = TestStruct::try_from(test_data.as_slice()).unwrap();
41+
assert_eq!(
42+
TestStruct {
43+
data: vec![(0xaa, 0xbb)],
44+
},
45+
ret_read
46+
);
47+
48+
let ret_write: Vec<u8> = ret_read.try_into().unwrap();
49+
assert_eq!(test_data, ret_write);
50+
}
51+
3052
#[test]
3153
fn test_count_from_field() {
3254
#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
@@ -74,7 +96,7 @@ mod test_slice {
7496
}
7597

7698
#[test]
77-
#[should_panic(expected = "Incomplete(NeedSize { bits: 8 })")]
99+
#[should_panic(expected = "Incomplete(NeedSize { bits: 24 })")]
78100
fn test_count_error() {
79101
#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
80102
struct TestStruct {
@@ -156,7 +178,7 @@ mod test_vec {
156178
}
157179

158180
#[test]
159-
#[should_panic(expected = "Incomplete(NeedSize { bits: 8 })")]
181+
#[should_panic(expected = "Incomplete(NeedSize { bits: 24 })")]
160182
fn test_count_error() {
161183
#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
162184
struct TestStruct {

0 commit comments

Comments
 (0)