Skip to content

Commit

Permalink
Merge 6b6276e into 03ff770
Browse files Browse the repository at this point in the history
  • Loading branch information
wcampbell0x2a authored May 31, 2024
2 parents 03ff770 + 6b6276e commit 6bea42b
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 11 deletions.
9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ 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"]
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.6.0", default-features = false, features = ["alloc"] }
rustversion = "1.0.16"
Expand Down
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};
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
8 changes: 7 additions & 1 deletion deku-derive/src/macros/deku_write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {

// Implement `DekuContainerWrite` for types that don't need a context
if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) {
#[cfg(feature = "bits")]
tokens.extend(quote! {
impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> #wher {
type Error = ::#crate_::DekuError;
Expand All @@ -56,7 +57,9 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
input.to_bits()
}
}
});

tokens.extend(quote! {
impl #imp core::convert::TryFrom<#ident> for Vec<u8> #wher {
type Error = ::#crate_::DekuError;

Expand Down Expand Up @@ -243,6 +246,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {

// Implement `DekuContainerWrite` for types that don't need a context
if input.ctx.is_none() || (input.ctx.is_some() && input.ctx_default.is_some()) {
#[cfg(feature = "bits")]
tokens.extend(quote! {
impl #imp core::convert::TryFrom<#ident> for ::#crate_::bitvec::BitVec<u8, ::#crate_::bitvec::Msb0> #wher {
type Error = ::#crate_::DekuError;
Expand All @@ -252,7 +256,9 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
input.to_bits()
}
}
});

tokens.extend(quote! {
impl #imp core::convert::TryFrom<#ident> for Vec<u8> #wher {
type Error = ::#crate_::DekuError;

Expand All @@ -263,7 +269,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
}

impl #imp DekuContainerWrite for #ident #wher {}
})
});
}

let (ctx_types, ctx_arg) = gen_ctx_types_and_arg(input.ctx.as_ref())?;
Expand Down
2 changes: 1 addition & 1 deletion ensure_no_std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ alloc = []

[dependencies]
cortex-m-rt = "0.7.3"
deku = { path = "../", default-features = false, features = ["alloc"] }
deku = { path = "../", default-features = false, features = ["alloc", "bits"] }
embedded-alloc = "0.5.1"

1 change: 1 addition & 0 deletions src/impls/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ macro_rules! ImplDekuTraitsCtx {
macro_rules! ImplDekuTraits {
($typ:ty, $readtype:ty) => {
ImplDekuTraitsCtx!($typ, $readtype, (), ());
#[cfg(feature = "bits")]
ImplDekuTraitsCtx!($typ, $readtype, (endian, bitsize), (Endian, BitSize));
ImplDekuTraitsCtx!($typ, $readtype, (endian, bytesize), (Endian, ByteSize));
ImplDekuTraitsCtx!($typ, $readtype, endian, Endian);
Expand Down
20 changes: 20 additions & 0 deletions src/impls/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ use alloc::borrow::Cow;
use alloc::format;
use core::convert::TryInto;

#[cfg(feature = "bits")]
use bitvec::prelude::*;

use no_std_io::io::{Read, Write};

use crate::ctx::*;
Expand All @@ -12,6 +14,7 @@ use crate::writer::Writer;
use crate::{DekuError, DekuReader, DekuWriter};

/// "Read" trait: read bits and construct type
#[cfg(feature = "bits")]
trait DekuRead<'a, Ctx = ()> {
/// Read bits and construct type
/// * **input** - Input as bits
Expand All @@ -32,7 +35,9 @@ trait DekuRead<'a, Ctx = ()> {
}

// specialize u8 for ByteSize
#[cfg(feature = "bits")]
impl DekuRead<'_, (Endian, ByteSize)> for u8 {
#[cfg(feature = "bits")]
#[inline]
fn read(
input: &BitSlice<u8, Msb0>,
Expand All @@ -57,6 +62,7 @@ impl DekuReader<'_, (Endian, ByteSize)> for u8 {
let ret = reader.read_bytes_const::<MAX_TYPE_BYTES>(&mut buf)?;
let a = match ret {
ReaderRet::Bytes => <u8>::from_be_bytes(buf),
#[cfg(feature = "bits")]
ReaderRet::Bits(bits) => {
let Some(bits) = bits else {
return Err(DekuError::Parse(Cow::from("no bits read from reader")));
Expand All @@ -71,6 +77,7 @@ impl DekuReader<'_, (Endian, ByteSize)> for u8 {

macro_rules! ImplDekuReadBits {
($typ:ty, $inner:ty) => {
#[cfg(feature = "bits")]
impl DekuRead<'_, (Endian, BitSize)> for $typ {
#[inline(never)]
fn read(
Expand Down Expand Up @@ -147,6 +154,7 @@ macro_rules! ImplDekuReadBits {
}
}

#[cfg(feature = "bits")]
impl DekuReader<'_, (Endian, BitSize)> for $typ {
#[inline(always)]
fn from_reader_with_ctx<R: Read>(
Expand All @@ -173,6 +181,7 @@ macro_rules! ImplDekuReadBits {

macro_rules! ImplDekuReadBytes {
($typ:ty, $inner:ty) => {
#[cfg(feature = "bits")]
impl DekuRead<'_, (Endian, ByteSize)> for $typ {
#[inline(never)]
fn read(
Expand Down Expand Up @@ -213,10 +222,12 @@ macro_rules! ImplDekuReadBytes {
<$typ>::from_be_bytes(buf.try_into().unwrap())
}
}
#[cfg(feature = "bits")]
ReaderRet::Bits(Some(bits)) => {
let a = <$typ>::read(&bits, (endian, size))?;
a.1
}
#[cfg(feature = "bits")]
ReaderRet::Bits(None) => {
return Err(DekuError::Parse(Cow::from("no bits read from reader")));
}
Expand All @@ -229,6 +240,7 @@ macro_rules! ImplDekuReadBytes {

macro_rules! ImplDekuReadSignExtend {
($typ:ty, $inner:ty) => {
#[cfg(feature = "bits")]
impl DekuRead<'_, (Endian, ByteSize)> for $typ {
#[inline(never)]
fn read(
Expand Down Expand Up @@ -262,6 +274,7 @@ macro_rules! ImplDekuReadSignExtend {
<$typ>::from_be_bytes(buf.try_into()?)
}
}
#[cfg(feature = "bits")]
ReaderRet::Bits(bits) => {
let Some(bits) = bits else {
return Err(DekuError::Parse(Cow::from("no bits read from reader")));
Expand All @@ -279,6 +292,7 @@ macro_rules! ImplDekuReadSignExtend {
}
}

#[cfg(feature = "bits")]
impl DekuRead<'_, (Endian, BitSize)> for $typ {
#[inline(never)]
fn read(
Expand All @@ -296,6 +310,7 @@ macro_rules! ImplDekuReadSignExtend {
}
}

#[cfg(feature = "bits")]
impl DekuReader<'_, (Endian, BitSize)> for $typ {
#[inline(always)]
fn from_reader_with_ctx<R: Read>(
Expand Down Expand Up @@ -340,10 +355,12 @@ macro_rules! ForwardDekuRead {
<$typ>::from_be_bytes(buf)
}
}
#[cfg(feature = "bits")]
ReaderRet::Bits(Some(bits)) => {
let a = <$typ>::read(&bits, (endian, ByteSize(MAX_TYPE_BYTES)))?;
a.1
}
#[cfg(feature = "bits")]
ReaderRet::Bits(None) => {
return Err(DekuError::Parse(Cow::from("no bits read from reader")));
}
Expand All @@ -367,6 +384,7 @@ macro_rules! ForwardDekuRead {
}

//// Only have `bit_size`, set `endian` to `Endian::default`.
#[cfg(feature = "bits")]
impl DekuReader<'_, BitSize> for $typ {
#[inline(always)]
fn from_reader_with_ctx<R: Read>(
Expand Down Expand Up @@ -397,6 +415,7 @@ macro_rules! ForwardDekuRead {

macro_rules! ImplDekuWrite {
($typ:ty) => {
#[cfg(feature = "bits")]
impl DekuWriter<(Endian, BitSize)> for $typ {
#[inline(always)]
fn to_writer<W: Write>(
Expand Down Expand Up @@ -494,6 +513,7 @@ macro_rules! ImplDekuWrite {

macro_rules! ForwardDekuWrite {
($typ:ty) => {
#[cfg(feature = "bits")]
impl DekuWriter<BitSize> for $typ {
#[inline(always)]
fn to_writer<W: Write>(
Expand Down
8 changes: 8 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,12 @@ environment, you will see logging messages as Deku does its deserialising.
- `DekuError` whenever possible will use a `'static str`, to make the errors compile away when following a
guide such as [min-sized-rust](/~https://github.com/johnthagen/min-sized-rust).
# Performance: Compile without `bitvec`
The feature `bits` enables the `bitvec` crate to use when reading and writing, which is enabled by default.
This however slows down the reading and writing process if your code doesn't use `bits` and the `bit_offset`
in `from_bytes`. While this option isn't fully tested, you can be sure to be creating the most performant
code possible for byte only reading by _not_ using this feature.
*/
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
Expand All @@ -358,6 +364,7 @@ pub mod no_std_io {
}

/// re-export of bitvec
#[cfg(feature = "bits")]
pub mod bitvec {
pub use bitvec::prelude::*;
pub use bitvec::view::BitView;
Expand Down Expand Up @@ -537,6 +544,7 @@ pub trait DekuContainerWrite: DekuWriter<()> {
/// assert_eq!(deku::bitvec::bitvec![1, 1, 1, 1, 0, 0, 0, 1, 1], bits);
/// ```
#[inline(always)]
#[cfg(feature = "bits")]
fn to_bits(&self) -> Result<bitvec::BitVec<u8, bitvec::Msb0>, DekuError> {
let mut out_buf = Vec::new();
let mut __deku_writer = Writer::new(&mut out_buf);
Expand Down
27 changes: 23 additions & 4 deletions src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use core::cmp::Ordering;

#[cfg(feature = "bits")]
use bitvec::prelude::*;
use no_std_io::io::{ErrorKind, Read};

Expand All @@ -16,6 +17,7 @@ pub enum ReaderRet {
/// Successfully read bytes
Bytes,
/// Successfully read bits
#[cfg(feature = "bits")]
Bits(Option<BitVec<u8, Msb0>>),
}

Expand All @@ -24,6 +26,7 @@ pub const MAX_BITS_AMT: usize = 128;

enum Leftover {
Byte(u8),
#[cfg(feature = "bits")]
Bits(BitVec<u8, Msb0>),
}

Expand Down Expand Up @@ -86,6 +89,7 @@ impl<'a, R: Read> Reader<'a, R> {
/// ```
#[inline]
pub fn rest(&mut self) -> Vec<bool> {
#[cfg(feature = "bits")]
match &self.leftover {
Some(Leftover::Bits(bits)) => bits.iter().by_vals().collect(),
Some(Leftover::Byte(byte)) => {
Expand All @@ -95,6 +99,8 @@ impl<'a, R: Read> Reader<'a, R> {
}
None => alloc::vec![],
}
#[cfg(not(feature = "bits"))]
alloc::vec![]
}

/// Return true if we are at the end of a reader and there are no cached bits in the reader.
Expand Down Expand Up @@ -130,10 +136,20 @@ impl<'a, R: Read> Reader<'a, R> {
// TODO: maybe send into read_bytes() if amt >= 8
#[inline]
pub fn skip_bits(&mut self, amt: usize) -> Result<(), DekuError> {
#[cfg(feature = "logging")]
log::trace!("skip_bits: {amt}");
// Save, and keep the leftover bits since the read will most likely be less than a byte
self.read_bits(amt)?;
#[cfg(feature = "bits")]
{
#[cfg(feature = "logging")]
log::trace!("skip_bits: {amt}");
// Save, and keep the leftover bits since the read will most likely be less than a byte
self.read_bits(amt)?;
}

#[cfg(not(feature = "bits"))]
{
if amt > 0 {
panic!("deku features no-bits was used");
}
}

Ok(())
}
Expand All @@ -149,6 +165,7 @@ impl<'a, R: Read> Reader<'a, R> {
/// # Params
/// `amt` - Amount of bits that will be read. Must be <= [`MAX_BITS_AMT`].
#[inline(never)]
#[cfg(feature = "bits")]
pub fn read_bits(&mut self, amt: usize) -> Result<Option<BitVec<u8, Msb0>>, DekuError> {
#[cfg(feature = "logging")]
log::trace!("read_bits: requesting {amt} bits");
Expand Down Expand Up @@ -278,6 +295,7 @@ impl<'a, R: Read> Reader<'a, R> {
fn read_bytes_other(&mut self, amt: usize, buf: &mut [u8]) -> Result<ReaderRet, DekuError> {
match self.leftover {
Some(Leftover::Byte(byte)) => self.read_bytes_leftover(buf, byte, amt),
#[cfg(feature = "bits")]
Some(Leftover::Bits(_)) => Ok(ReaderRet::Bits(self.read_bits(amt * 8)?)),
_ => unreachable!(),
}
Expand Down Expand Up @@ -363,6 +381,7 @@ impl<'a, R: Read> Reader<'a, R> {
) -> Result<ReaderRet, DekuError> {
match self.leftover {
Some(Leftover::Byte(byte)) => self.read_bytes_const_leftover(buf, byte),
#[cfg(feature = "bits")]
Some(Leftover::Bits(_)) => Ok(ReaderRet::Bits(self.read_bits(N * 8)?)),
_ => unreachable!(),
}
Expand Down
Loading

0 comments on commit 6bea42b

Please sign in to comment.