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

Specialize bytes #278

Merged
merged 13 commits into from
Oct 6, 2022
56 changes: 56 additions & 0 deletions benches/deku.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use deku::prelude::*;

#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct DekuBits {
#[deku(bits = "1")]
data_01: u8,
#[deku(bits = "7")]
data_02: u8,
}

#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct DekuByte {
data: u8,
Expand All @@ -13,13 +21,31 @@ enum DekuEnum {
VariantA(u8),
}

/// This is faster, because we go right to (endian, bytes)
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct DekuVecPerf {
#[deku(bytes = "1")]
count: u8,
#[deku(count = "count")]
#[deku(bytes = "1")]
data: Vec<u8>,
}

#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct DekuVec {
count: u8,
#[deku(count = "count")]
data: Vec<u8>,
}

fn deku_read_bits(input: &[u8]) {
let (_rest, _v) = DekuBits::from_bytes((input, 0)).unwrap();
}

fn deku_write_bits(input: &DekuBits) {
let _v = input.to_bytes().unwrap();
}

fn deku_read_byte(input: &[u8]) {
let (_rest, _v) = DekuByte::from_bytes((input, 0)).unwrap();
}
Expand All @@ -44,13 +70,32 @@ fn deku_write_vec(input: &DekuVec) {
let _v = input.to_bytes().unwrap();
}

fn deku_read_vec_perf(input: &[u8]) {
let (_rest, _v) = DekuVecPerf::from_bytes((input, 0)).unwrap();
}

fn deku_write_vec_perf(input: &DekuVecPerf) {
let _v = input.to_bytes().unwrap();
}

fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("deku_read_byte", |b| {
b.iter(|| deku_read_byte(black_box([0x01].as_ref())))
});
c.bench_function("deku_write_byte", |b| {
b.iter(|| deku_write_byte(black_box(&DekuByte { data: 0x01 })))
});
c.bench_function("deku_read_bits", |b| {
b.iter(|| deku_read_bits(black_box([0xf1].as_ref())))
});
c.bench_function("deku_write_bits", |b| {
b.iter(|| {
deku_write_bits(black_box(&DekuBits {
data_01: 0x0f,
data_02: 0x01,
}))
})
});

c.bench_function("deku_read_enum", |b| {
b.iter(|| deku_read_enum(black_box([0x01, 0x02].as_ref())))
Expand All @@ -74,6 +119,17 @@ fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("deku_write_vec", |b| {
b.iter(|| deku_write_vec(black_box(&deku_write_vec_input)))
});

let deku_write_vec_input = DekuVecPerf {
count: 100,
data: vec![0xFF; 100],
};
c.bench_function("deku_read_vec_perf", |b| {
b.iter(|| deku_read_vec_perf(black_box(&deku_read_vec_input)))
});
c.bench_function("deku_write_vec_perf", |b| {
b.iter(|| deku_write_vec_perf(black_box(&deku_write_vec_input)))
});
}

criterion_group!(benches, criterion_benchmark);
Expand Down
4 changes: 2 additions & 2 deletions deku-derive/src/macros/deku_read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,14 +604,14 @@ fn emit_field_read(
quote! {
{
use core::borrow::Borrow;
#type_as_deku_read::read(__deku_rest, (::#crate_::ctx::Limit::new_size(::#crate_::ctx::Size::Bits(usize::try_from(*((#field_bits).borrow()))?)), (#read_args)))
#type_as_deku_read::read(__deku_rest, (::#crate_::ctx::Limit::new_bit_size(::#crate_::ctx::BitSize(usize::try_from(*((#field_bits).borrow()))?)), (#read_args)))
}
}
} else if let Some(field_bytes) = &f.bytes_read {
quote! {
{
use core::borrow::Borrow;
#type_as_deku_read::read(__deku_rest, (::#crate_::ctx::Limit::new_size(::#crate_::ctx::Size::Bytes(usize::try_from(*((#field_bytes).borrow()))?)), (#read_args)))
#type_as_deku_read::read(__deku_rest, (::#crate_::ctx::Limit::new_byte_size(::#crate_::ctx::ByteSize(usize::try_from(*((#field_bytes).borrow()))?)), (#read_args)))
}
}
} else if let Some(field_until) = &f.until {
Expand Down
14 changes: 8 additions & 6 deletions deku-derive/src/macros/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,16 +204,17 @@ fn gen_type_from_ctx_id(
}

/// Generate argument for `id`:
/// `#deku(endian = "big", bits = "1")` -> `Endian::Big, Size::Bits(1)`
/// `#deku(endian = "big", bits = "1")` -> `Endian::Big, BitSize(1)`
/// `#deku(endian = "big", bytes = "1")` -> `Endian::Big, ByteSize(1)`
pub(crate) fn gen_id_args(
endian: Option<&syn::LitStr>,
bits: Option<&Num>,
bytes: Option<&Num>,
) -> syn::Result<TokenStream> {
let crate_ = get_crate_name();
let endian = endian.map(gen_endian_from_str).transpose()?;
let bits = bits.map(|n| quote! {::#crate_::ctx::Size::Bits(#n)});
let bytes = bytes.map(|n| quote! {::#crate_::ctx::Size::Bytes(#n)});
let bits = bits.map(|n| quote! {::#crate_::ctx::BitSize(#n)});
let bytes = bytes.map(|n| quote! {::#crate_::ctx::ByteSize(#n)});

// FIXME: Should be `into_iter` here, see https://github.com/rust-lang/rust/issues/66145.
let id_args = [endian.as_ref(), bits.as_ref(), bytes.as_ref()]
Expand All @@ -229,7 +230,8 @@ pub(crate) fn gen_id_args(

/// Generate argument for fields:
///
/// `#deku(endian = "big", bits = "1", ctx = "a")` -> `Endian::Big, Size::Bits(1), a`
/// `#deku(endian = "big", bits = "1", ctx = "a")` -> `Endian::Big, BitSize(1), a`
/// `#deku(endian = "big", bytes = "1", ctx = "a")` -> `Endian::Big, ByteSize(1), a`
fn gen_field_args(
endian: Option<&syn::LitStr>,
bits: Option<&Num>,
Expand All @@ -238,8 +240,8 @@ fn gen_field_args(
) -> syn::Result<TokenStream> {
let crate_ = get_crate_name();
let endian = endian.map(gen_endian_from_str).transpose()?;
let bits = bits.map(|n| quote! {::#crate_::ctx::Size::Bits(#n)});
let bytes = bytes.map(|n| quote! {::#crate_::ctx::Size::Bytes(#n)});
let bits = bits.map(|n| quote! {::#crate_::ctx::BitSize(#n)});
let bytes = bytes.map(|n| quote! {::#crate_::ctx::ByteSize(#n)});
let ctx = ctx.map(|c| quote! {#c});

// FIXME: Should be `into_iter` here, see https://github.com/rust-lang/rust/issues/66145.
Expand Down
10 changes: 5 additions & 5 deletions examples/custom_reader_and_writer.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use deku::bitvec::{BitSlice, BitVec, Msb0};
use deku::ctx::Size;
use deku::ctx::BitSize;
use deku::prelude::*;
use std::convert::TryInto;

fn bit_flipper_read(
field_a: u8,
rest: &BitSlice<Msb0, u8>,
bit_size: Size,
bit_size: BitSize,
) -> Result<(&BitSlice<Msb0, u8>, u8), DekuError> {
// Access to previously read fields
println!("field_a = 0x{:X}", field_a);
Expand All @@ -30,7 +30,7 @@ fn bit_flipper_write(
field_a: u8,
field_b: u8,
output: &mut BitVec<Msb0, u8>,
bit_size: Size,
bit_size: BitSize,
) -> Result<(), DekuError> {
// Access to previously written fields
println!("field_a = 0x{:X}", field_a);
Expand All @@ -52,8 +52,8 @@ struct DekuTest {
field_a: u8,

#[deku(
reader = "bit_flipper_read(*field_a, deku::rest, Size::Bits(8))",
writer = "bit_flipper_write(*field_a, *field_b, deku::output, Size::Bits(8))"
reader = "bit_flipper_read(*field_a, deku::rest, BitSize(8))",
writer = "bit_flipper_write(*field_a, *field_b, deku::output, BitSize(8))"
)]
field_b: u8,
}
Expand Down
4 changes: 2 additions & 2 deletions src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,7 @@ struct Type1 {
// is equivalent to
struct Type1 {
#[deku(ctx = "Endian::Big, Size::Bits(1)")]
#[deku(ctx = "Endian::Big, BitSize(1)")]
field: u8,
}
```
Expand All @@ -874,7 +874,7 @@ struct Type1 {
struct Type1 {
#[deku(ctx = "Endian::Big")]
field_a: u16,
#[deku(ctx = "Endian::Big, Size::Bits(5), *field_a")] // endian is prepended
#[deku(ctx = "Endian::Big, BitSize(5), *field_a")] // endian is prepended
field_b: SubType,
}
```
Expand Down
83 changes: 33 additions & 50 deletions src/ctx.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
//! Types for context representation
//! See [ctx attribute](super::attributes#ctx) for more information.

use crate::error::DekuError;
use core::marker::PhantomData;
use core::str::FromStr;

#[cfg(feature = "alloc")]
use alloc::format;

/// An endian
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Endian {
Expand Down Expand Up @@ -76,17 +72,23 @@ impl FromStr for Endian {
}
}

#[allow(clippy::derive_partial_eq_without_eq)]
// derive_partial_eq_without_eq false positive in struct using traits
// For details: https://github.com/rust-lang/rust-clippy/issues/9413
/// A limit placed on a container's elements
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
pub enum Limit<T, Predicate: FnMut(&T) -> bool> {
/// Read a specific count of elements
Count(usize),

/// Read until a given predicate holds true
Until(Predicate, PhantomData<T>),

/// Read until a given quantity of bytes have been read
ByteSize(ByteSize),

/// Read until a given quantity of bits have been read
Size(Size),
BitSize(BitSize),
}

impl<T> From<usize> for Limit<T, fn(&T) -> bool> {
Expand All @@ -101,9 +103,15 @@ impl<T, Predicate: for<'a> FnMut(&'a T) -> bool> From<Predicate> for Limit<T, Pr
}
}

impl<T> From<Size> for Limit<T, fn(&T) -> bool> {
fn from(size: Size) -> Self {
Limit::Size(size)
impl<T> From<ByteSize> for Limit<T, fn(&T) -> bool> {
fn from(size: ByteSize) -> Self {
Limit::ByteSize(size)
}
}

impl<T> From<BitSize> for Limit<T, fn(&T) -> bool> {
fn from(size: BitSize) -> Self {
Limit::BitSize(size)
}
}

Expand All @@ -123,35 +131,39 @@ impl<T> Limit<T, fn(&T) -> bool> {
}

/// Constructs a new Limit that reads until the given size
pub fn new_size(size: Size) -> Self {
pub fn new_bit_size(size: BitSize) -> Self {
size.into()
}

/// Constructs a new Limit that reads until the given size
pub fn new_byte_size(size: ByteSize) -> Self {
size.into()
}
}

/// The size of a field
/// The size of field in bytes
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum Size {
/// bit size
Bits(usize),
/// byte size
Bytes(usize),
}
pub struct ByteSize(pub usize);

impl Size {
/// The size of field in bits
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct BitSize(pub usize);

impl BitSize {
/// Convert the size in bytes to a bit size.
///
/// # Panic
/// Panic if `byte_size * 8` is greater than `usize::MAX`.
fn bits_from_bytes(byte_size: usize) -> Self {
Self::Bits(byte_size.checked_mul(8).expect("bit size overflow"))
Self(byte_size.checked_mul(8).expect("bit size overflow"))
}

/// Returns the bit size of a type.
/// # Examples
/// ```rust
/// # use deku::ctx::Size;
/// # use deku::ctx::BitSize;
///
/// assert_eq!(Size::of::<i32>(), Size::Bits(4 * 8));
/// assert_eq!(BitSize::of::<i32>(), BitSize(4 * 8));
/// ```
///
/// # Panics
Expand All @@ -164,33 +176,4 @@ impl Size {
pub fn of_val<T: ?Sized>(val: &T) -> Self {
Self::bits_from_bytes(core::mem::size_of_val(val))
}

/// Returns the size in bits of a Size
///
/// # Panics
/// Panic if the bit size of Size::Bytes(n) is greater than `usize::MAX`
pub fn bit_size(&self) -> usize {
match *self {
Size::Bits(size) => size,
Size::Bytes(size) => size.checked_mul(8).expect("bit size overflow"),
}
}

/// Returns the size in bytes of a Size
pub fn byte_size(&self) -> Result<usize, DekuError> {
match *self {
Size::Bits(size) => {
if size % 8 == 0 {
Ok(size / 8)
} else {
Err(DekuError::InvalidParam(format!(
"Bit size of {} is not a multiple of 8.
Cannot be represented in bytes",
size
)))
}
}
Size::Bytes(size) => Ok(size),
}
}
}
4 changes: 2 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use alloc::{format, string::String};

/// Number of bits needed to retry parsing
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NeedSize {
bits: usize,
}
Expand All @@ -28,7 +28,7 @@ impl NeedSize {
}

/// Deku errors
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum DekuError {
/// Parsing error when reading
Expand Down
2 changes: 1 addition & 1 deletion src/impls/bool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ mod tests {
let input = &[0b01_000000];
let bit_slice = input.view_bits::<Msb0>();

let (rest, res_read) = bool::read(bit_slice, crate::ctx::Size::Bits(2)).unwrap();
let (rest, res_read) = bool::read(bit_slice, crate::ctx::BitSize(2)).unwrap();
assert_eq!(true, res_read);
assert_eq!(6, rest.len());

Expand Down
Loading