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

Impl writer #355

Merged
merged 9 commits into from
Apr 29, 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
114 changes: 101 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,25 @@
## [Unreleased]

## Changes
[#390](https://github.com/sharksforarms/deku/pull/390) added MSRV for `1.67.1`.
[#389](https://github.com/sharksforarms/deku/pull/389) changed edition to 2021
[#352](https://github.com/sharksforarms/deku/pull/352) added a new function `from_reader` that uses `io::Read`.
`io::Read` is also now used internally, bringing massive performance and usability improvements.
- Added MSRV for `1.67.1` ([#390](https://github.com/sharksforarms/deku/pull/390))
- Changed edition to 2021 ([#389](https://github.com/sharksforarms/deku/pull/389))
- Refactored `logging` feature with massive usability increases ([#352](https://github.com/sharksforarms/deku/pull/352)), ([#355](https://github.com/sharksforarms/deku/pull/355))
- Bumped the `syn` library to 2.0, which required replacing `type` for Enums with `id_type` ([#386](https://github.com/sharksforarms/deku/pull/386))
```diff,rust
#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
-#[deku(type = "u8")]
+#[deku(id_type = "u8")]
enum DekuTest {
#[deku(id_pat = "_")]
VariantC((u8, u8)),
}
```

### Updated Reader API
- Changed API of reading to use `io::Read`, bringing massive performance and usability improvements ([#352](https://github.com/sharksforarms/deku/pull/352))
- Changed the trait `DekuRead` to `DekuReader`

### New `from_reader`
For example:
```rust
use std::io::{Seek, SeekFrom, Read};
use std::fs::File;
Expand All @@ -31,7 +44,7 @@ With the switch to internal streaming, the variables `deku::input`, `deku::input
`deku::reader` is a replacement for some of the functionality.
See [examples/deku_input.rs](examples/deku_input.rs) for a new example of caching all reads.

old:
Old:
```rust
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct DekuTest {
Expand All @@ -56,7 +69,7 @@ fn custom_read(
}
```

new:
New:
```rust
#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct DekuTest {
Expand All @@ -83,7 +96,7 @@ fn custom_read<R: std::io::Read>(

- With the addition of using `Read`, containing a byte slice with a reference is not supported:

old
Old
```rust
#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct TestStruct<'a> {
Expand All @@ -94,7 +107,7 @@ struct TestStruct<'a> {
}
```

new
New
```rust
#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
struct TestStruct {
Expand All @@ -108,20 +121,20 @@ struct TestStruct {
- `id_pat` is now required to be the same type as stored id.
This also disallows using tuples for storing the id:

old:
Old:
```rust
#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
#[deku(type = "u8")]
#[deku(id_type = "u8")]
enum DekuTest {
#[deku(id_pat = "_")]
VariantC((u8, u8)),
}
```

new:
New:
```rust
#[derive(PartialEq, Debug, DekuRead, DekuWrite)]
#[deku(type = "u8")]
#[deku(id_type = "u8")]
enum DekuTest {
#[deku(id_pat = "_")]
VariantC {
Expand All @@ -133,6 +146,81 @@ enum DekuTest {

- The feature `const_generics` was removed and is enabled by default.

### Updated Writer API
- Changed API of writing to use `io::Write`, bringing massive performance and usability improvements ([#355](https://github.com/sharksforarms/deku/pull/355))
- Changed the trait `DekuWrite` to `DekuWriter`
- The more internal (with context) `write(..)` was replaced with `to_writer(..)`.
With the switch to internal streaming, the variables `deku::output` are now not possible and were removed. `deku::writer` is a replacement for some of the functionality.

Old:
```rust
fn bit_flipper_write(
field_a: u8,
field_b: u8,
output: &mut BitVec<u8, Msb0>,
bit_size: BitSize,
) -> Result<(), DekuError> {
// Access to previously written fields
println!("field_a = 0x{:X}", field_a);

// value of field_b
println!("field_b = 0x{:X}", field_b);

// Size of the current field
println!("bit_size: {:?}", bit_size);

// flip the bits on value if field_a is 0x01
let value = if field_a == 0x01 { !field_b } else { field_b };

value.write(output, bit_size)
}

#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct DekuTest {
field_a: u8,

#[deku(
writer = "bit_flipper_write(*field_a, *field_b, deku::output, BitSize(8))"
)]
field_b: u8,
}
````

New:
```rust
fn bit_flipper_write<W: Write>(
field_a: u8,
field_b: u8,
writer: &mut Writer<W>,
bit_size: BitSize,
) -> Result<(), DekuError> {
// Access to previously written fields
println!("field_a = 0x{:X}", field_a);

// value of field_b
println!("field_b = 0x{:X}", field_b);

// Size of the current field
println!("bit_size: {:?}", bit_size);

// flip the bits on value if field_a is 0x01
let value = if field_a == 0x01 { !field_b } else { field_b };

value.to_writer(writer, bit_size)
}

#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
struct DekuTest {
field_a: u8,

#[deku(
writer = "bit_flipper_write(*field_a, *field_b, deku::writer, BitSize(8))"
)]
field_b: u8,
}
```
- Added `DekuError::Write` to denote `io::Write` errors

## [0.16.0] - 2023-02-28

### Changes
Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ log = { version = "0.4.17", optional = true }
no_std_io = { version = "0.5.0", default-features = false, features = ["alloc"] }

[dev-dependencies]
rstest = "0.16.0"
rstest = "0.18.0"
hexlit = "0.5.5"
criterion = "0.4.0"
alloc_counter = "0.0.4"
trybuild = "1.0.77"
rustc-hash = "1.1.0"
env_logger = "0.10.0"
assert_hex = "0.2.2"

[[bench]]
name = "deku"
Expand Down
2 changes: 1 addition & 1 deletion benches/deku.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct DekuBytes {
}

#[derive(Debug, PartialEq, DekuRead, DekuWrite)]
#[deku(type = "u8")]
#[deku(id_type = "u8")]
enum DekuEnum {
#[deku(id = "0x01")]
VariantA(u8),
Expand Down
6 changes: 3 additions & 3 deletions deku-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ logging = []

[dependencies]
quote = "1.0"
syn = "1.0"
syn = "2.0"
# extra-traits gives us Debug
# syn = {version = "1.0", features = ["extra-traits"]}
proc-macro2 = "1.0"
darling = "0.14"
darling = "0.20"
proc-macro-crate = { version = "1.3.0", optional = true }

[dev-dependencies]
rstest = "0.16"
rstest = "0.18"
32 changes: 15 additions & 17 deletions deku-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use proc_macro2::TokenStream;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::AttributeArgs;

use crate::macros::deku_read::emit_deku_read;
use crate::macros::deku_write::emit_deku_write;
Expand Down Expand Up @@ -208,7 +207,10 @@ impl DekuData {
ast::Data::Struct(_) => {
// Validate id_* attributes are being used on an enum
if data.id_type.is_some() {
Err(cerror(data.id_type.span(), "`type` only supported on enum"))
Err(cerror(
data.id_type.span(),
"`id_type` only supported on enum",
))
} else if data.id.is_some() {
Err(cerror(data.id.span(), "`id` only supported on enum"))
} else if data.bytes.is_some() {
Expand All @@ -220,19 +222,19 @@ impl DekuData {
}
}
ast::Data::Enum(_) => {
// Validate `type` or `id` is specified
// Validate `id_type` or `id` is specified
if data.id_type.is_none() && data.id.is_none() {
return Err(cerror(
data.ident.span(),
"`type` or `id` must be specified on enum",
"`id_type` or `id` must be specified on enum",
));
}

// Validate either `type` or `id` is specified
// Validate either `id_type` or `id` is specified
if data.id_type.is_some() && data.id.is_some() {
return Err(cerror(
data.ident.span(),
"conflicting: both `type` and `id` specified on enum",
"conflicting: both `id_type` and `id` specified on enum",
));
}

Expand Down Expand Up @@ -654,11 +656,7 @@ struct DekuReceiver {
id: Option<Id>,

/// enum only: type of the enum `id`
#[darling(
rename = "type",
default = "default_res_opt",
map = "map_litstr_as_tokenstream"
)]
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
id_type: Result<Option<TokenStream>, ReplacementError>,

/// enum only: bit size of the enum `id`
Expand Down Expand Up @@ -687,7 +685,7 @@ fn apply_replacements(input: &syn::LitStr) -> Result<Cow<'_, syn::LitStr>, Repla

let input_str = input_value
.replace("deku::reader", "__deku_reader")
.replace("deku::output", "__deku_output") // part of the public API `write`
.replace("deku::writer", "__deku_writer")
.replace("deku::bit_offset", "__deku_bit_offset")
.replace("deku::byte_offset", "__deku_byte_offset");

Expand Down Expand Up @@ -898,7 +896,7 @@ pub fn proc_deku_write(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
}

fn is_not_deku(attr: &syn::Attribute) -> bool {
attr.path
attr.path()
.get_ident()
.map(|ident| ident != "deku" && ident != "deku_derive")
.unwrap_or(true)
Expand Down Expand Up @@ -962,8 +960,8 @@ pub fn deku_derive(
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
// Parse `deku_derive` attribute
let attr_args = syn::parse_macro_input!(attr as AttributeArgs);
let args = match DekuDerive::from_list(&attr_args) {
let nested_meta = darling::ast::NestedMeta::parse_meta_list(attr.into()).unwrap();
let args = match DekuDerive::from_list(&nested_meta) {
Ok(v) => v,
Err(e) => {
return proc_macro::TokenStream::from(e.write_errors());
Expand Down Expand Up @@ -1062,9 +1060,9 @@ mod tests {
}"#),

// Valid Enum
case::enum_empty(r#"#[deku(type = "u8")] enum Test {}"#),
case::enum_empty(r#"#[deku(id_type = "u8")] enum Test {}"#),
case::enum_all(r#"
#[deku(type = "u8")]
#[deku(id_type = "u8")]
enum Test {
#[deku(id = "1")]
A,
Expand Down
2 changes: 1 addition & 1 deletion deku-derive/src/macros/deku_read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ fn emit_field_read(

let trace_field_log = if cfg!(feature = "logging") {
quote! {
log::trace!("Reading: {}::{}", #ident, #field_ident_str);
log::trace!("Reading: {}.{}", #ident, #field_ident_str);
}
} else {
quote! {}
Expand Down
Loading
Loading