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 reader+writer seek #360

Merged
merged 11 commits into from
Sep 4, 2024
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## [Unreleased]
- Add attributes `seek_from_start`, `seek_from_current`, `seek_from_end`, and `seek_rewind` to control the position of the reader before reading a field ([#360](https://github.com/sharksforarms/deku/pull/360))

## [0.17.0] - 2024-04-23

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ error_in_core = []
deku_derive = { version = "^0.17.0", path = "deku-derive", default-features = false}
bitvec = { version = "1.0.1", default-features = false }
log = { version = "0.4.21", optional = true }
no_std_io = { version = "0.6.0", default-features = false, features = ["alloc"] }
no_std_io = { version = "0.8.0", default-features = false, features = ["alloc"], package = "no_std_io2" }
rustversion = "1.0.16"

[dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions benches/deku.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::io::{Cursor, Read};
use std::io::{Cursor, Read, Seek};

use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion};
use deku::prelude::*;
Expand Down Expand Up @@ -38,7 +38,7 @@ fn deku_write<T: DekuContainerWrite>(input: &T) {
let _v = input.to_bytes().unwrap();
}

fn deku_read<T: for<'a> DekuContainerRead<'a>>(mut reader: impl Read) {
fn deku_read<T: for<'a> DekuContainerRead<'a>>(mut reader: impl Read + Seek) {
let mut reader = Reader::new(&mut reader);
let _v = T::from_reader_with_ctx(&mut reader, ()).unwrap();
}
Expand Down
77 changes: 77 additions & 0 deletions deku-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ struct DekuData {

/// enum only: byte size of the enum `id`
bytes: Option<Num>,

/// struct only: seek from current position
seek_rewind: bool,

/// struct only: seek from current position
seek_from_current: Option<TokenStream>,

/// struct only: seek from end position
seek_from_end: Option<TokenStream>,

/// struct only: seek from start position
seek_from_start: Option<TokenStream>,
}

impl DekuData {
Expand Down Expand Up @@ -188,6 +200,10 @@ impl DekuData {
id_type: receiver.id_type?,
bits: receiver.bits,
bytes: receiver.bytes,
seek_rewind: receiver.seek_rewind,
seek_from_current: receiver.seek_from_current?,
seek_from_end: receiver.seek_from_end?,
seek_from_start: receiver.seek_from_start?,
};

DekuData::validate(&data)?;
Expand Down Expand Up @@ -444,6 +460,18 @@ struct FieldData {

// assert value of field
assert_eq: Option<TokenStream>,

/// seek from current position
seek_rewind: bool,

/// seek from current position
seek_from_current: Option<TokenStream>,

/// seek from end position
seek_from_end: Option<TokenStream>,

/// seek from start position
seek_from_start: Option<TokenStream>,
}

impl FieldData {
Expand Down Expand Up @@ -481,6 +509,10 @@ impl FieldData {
cond: receiver.cond?,
assert: receiver.assert?,
assert_eq: receiver.assert_eq?,
seek_rewind: receiver.seek_rewind,
seek_from_current: receiver.seek_from_current?,
seek_from_end: receiver.seek_from_end?,
seek_from_start: receiver.seek_from_start?,
};

FieldData::validate(&data)?;
Expand Down Expand Up @@ -544,6 +576,19 @@ impl FieldData {
));
}

// Validate usage of seek_*
if (data.seek_from_current.is_some() as u8
+ data.seek_from_end.is_some() as u8
+ data.seek_from_start.is_some() as u8
+ data.seek_rewind as u8)
> 1
{
return Err(cerror(
data.bits.span(),
"conflicting: only one `seek` attribute can be used at one time",
));
}

Ok(())
}

Expand Down Expand Up @@ -668,6 +713,22 @@ struct DekuReceiver {
/// enum only: byte size of the enum `id`
#[darling(default)]
bytes: Option<Num>,

/// struct only: seek from current position
#[darling(default)]
seek_rewind: bool,

/// struct only: seek from current position
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
seek_from_current: Result<Option<TokenStream>, ReplacementError>,

/// struct only: seek from end position
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
seek_from_end: Result<Option<TokenStream>, ReplacementError>,

/// struct only: seek from start position
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
seek_from_start: Result<Option<TokenStream>, ReplacementError>,
}

type ReplacementError = TokenStream;
Expand Down Expand Up @@ -848,6 +909,22 @@ struct DekuFieldReceiver {
// assert value of field
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
assert_eq: Result<Option<TokenStream>, ReplacementError>,

/// seek from current position
#[darling(default)]
seek_rewind: bool,

/// seek from current position
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
seek_from_current: Result<Option<TokenStream>, ReplacementError>,

/// seek from end position
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
seek_from_end: Result<Option<TokenStream>, ReplacementError>,

/// seek from start position
#[darling(default = "default_res_opt", map = "map_litstr_as_tokenstream")]
seek_from_start: Result<Option<TokenStream>, ReplacementError>,
}

/// Receiver for the variant-level attributes inside a enum
Expand Down
99 changes: 94 additions & 5 deletions deku-derive/src/macros/deku_read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,49 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
fields,
} = DekuDataStruct::try_from(input)?;

let seek = if let Some(num) = &input.seek_from_current {
quote! {
{
use ::#crate_::no_std_io::Seek;
use ::#crate_::no_std_io::SeekFrom;
if let Err(e) = __deku_reader.seek(SeekFrom::Current(i64::try_from(#num).unwrap())) {
return Err(DekuError::Io(e.kind()));
}
}
}
} else if let Some(num) = &input.seek_from_end {
quote! {
{
use ::#crate_::no_std_io::Seek;
use ::#crate_::no_std_io::SeekFrom;
if let Err(e) = __deku_reader.seek(SeekFrom::End(i64::try_from(#num).unwrap())) {
return Err(DekuError::Io(e.kind()));
}
}
}
} else if let Some(num) = &input.seek_from_start {
quote! {
{
use ::#crate_::no_std_io::Seek;
use ::#crate_::no_std_io::SeekFrom;
if let Err(e) = __deku_reader.seek(SeekFrom::Start(u64::try_from(#num).unwrap())) {
return Err(DekuError::Io(e.kind()));
}
}
}
} else if input.seek_rewind {
quote! {
{
use ::#crate_::no_std_io::Seek;
if let Err(e) = __deku_reader.rewind() {
return Err(DekuError::Io(e.kind()));
}
}
}
} else {
quote! {}
};

let magic_read = emit_magic_read(input);

// check if the first field has an ident, if not, it's a unnamed struct
Expand Down Expand Up @@ -107,6 +150,8 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
let read_body = quote! {
use core::convert::TryFrom;

#seek

#magic_read

#(#field_reads)*
Expand All @@ -118,7 +163,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
tokens.extend(quote! {
impl #imp ::#crate_::DekuReader<#lifetime, #ctx_types> for #ident #wher {
#[inline]
fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read>(__deku_reader: &mut ::#crate_::reader::Reader<R>, #ctx_arg) -> core::result::Result<Self, ::#crate_::DekuError> {
fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read + ::#crate_::no_std_io::Seek>(__deku_reader: &mut ::#crate_::reader::Reader<R>, #ctx_arg) -> core::result::Result<Self, ::#crate_::DekuError> {
#read_body
}
}
Expand All @@ -130,7 +175,7 @@ fn emit_struct(input: &DekuData) -> Result<TokenStream, syn::Error> {
tokens.extend(quote! {
impl #imp ::#crate_::DekuReader<#lifetime> for #ident #wher {
#[inline]
fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read>(__deku_reader: &mut ::#crate_::reader::Reader<R>, _: ()) -> core::result::Result<Self, ::#crate_::DekuError> {
fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read + ::#crate_::no_std_io::Seek>(__deku_reader: &mut ::#crate_::reader::Reader<R>, _: ()) -> core::result::Result<Self, ::#crate_::DekuError> {
#read_body
}
}
Expand Down Expand Up @@ -381,7 +426,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
#[allow(non_snake_case)]
impl #imp ::#crate_::DekuReader<#lifetime, #ctx_types> for #ident #wher {
#[inline]
fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read>(__deku_reader: &mut ::#crate_::reader::Reader<R>, #ctx_arg) -> core::result::Result<Self, ::#crate_::DekuError> {
fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read + ::#crate_::no_std_io::Seek>(__deku_reader: &mut ::#crate_::reader::Reader<R>, #ctx_arg) -> core::result::Result<Self, ::#crate_::DekuError> {
#read_body
}
}
Expand All @@ -394,7 +439,7 @@ fn emit_enum(input: &DekuData) -> Result<TokenStream, syn::Error> {
#[allow(non_snake_case)]
impl #imp ::#crate_::DekuReader<#lifetime> for #ident #wher {
#[inline]
fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read>(__deku_reader: &mut ::#crate_::reader::Reader<R>, _: ()) -> core::result::Result<Self, ::#crate_::DekuError> {
fn from_reader_with_ctx<R: ::#crate_::no_std_io::Read + ::#crate_::no_std_io::Seek>(__deku_reader: &mut ::#crate_::reader::Reader<R>, _: ()) -> core::result::Result<Self, ::#crate_::DekuError> {
#read_body
}
}
Expand Down Expand Up @@ -568,6 +613,49 @@ fn emit_field_read(
&f.assert_eq,
];

let seek = if let Some(num) = &f.seek_from_current {
quote! {
{
use ::#crate_::no_std_io::Seek;
use ::#crate_::no_std_io::SeekFrom;
if let Err(e) = __deku_reader.seek(SeekFrom::Current(i64::try_from(#num).unwrap())) {
return Err(DekuError::Io(e.kind()));
}
}
}
} else if let Some(num) = &f.seek_from_end {
quote! {
{
use ::#crate_::no_std_io::Seek;
use ::#crate_::no_std_io::SeekFrom;
if let Err(e) = __deku_reader.seek(SeekFrom::End(i64::try_from(#num).unwrap())) {
return Err(DekuError::Io(e.kind()));
}
}
}
} else if let Some(num) = &f.seek_from_start {
quote! {
{
use ::#crate_::no_std_io::Seek;
use ::#crate_::no_std_io::SeekFrom;
if let Err(e) = __deku_reader.seek(SeekFrom::Start(u64::try_from(#num).unwrap())) {
return Err(DekuError::Io(e.kind()));
}
}
}
} else if f.seek_rewind {
quote! {
{
use ::#crate_::no_std_io::Seek;
if let Err(e) = __deku_reader.rewind() {
return Err(DekuError::Io(e.kind()));
}
}
}
} else {
quote! {}
};

let (bit_offset, byte_offset) = emit_bit_byte_offsets(&field_check_vars);

let field_map = f
Expand Down Expand Up @@ -761,6 +849,7 @@ fn emit_field_read(
};

let field_read = quote! {
#seek
#pad_bits_before

#bit_offset
Expand Down Expand Up @@ -795,7 +884,7 @@ pub fn emit_container_read(
impl #imp ::#crate_::DekuContainerRead<#lifetime> for #ident #wher {
#[allow(non_snake_case)]
#[inline]
fn from_reader<'a, R: ::#crate_::no_std_io::Read>(__deku_input: (&'a mut R, usize)) -> core::result::Result<(usize, Self), ::#crate_::DekuError> {
fn from_reader<'a, R: ::#crate_::no_std_io::Read + ::#crate_::no_std_io::Seek>(__deku_input: (&'a mut R, usize)) -> core::result::Result<(usize, Self), ::#crate_::DekuError> {
#from_reader_body
}

Expand Down
Loading
Loading