Get stream position relative to object start #284
-
Is there a way to get a stream position which is not absolute to the stream? I want to parse multiple elements within a stream where I need a relative stream position, because otherwise the calculations are off. See the test code below.
Greetings and thanks, Christian #[test]
fn relative_stream() {
struct RelativeStream<T> {
inner: T,
starting_position: Option<u64>,
}
impl<T: binrw::io::Seek> RelativeStream<T> {
/// Get the relative stream position to the starting position which is saved when `new` is called
pub fn relative_stream_position(&mut self) -> Result<u64, binrw::io::Error> {
let current_position = self.inner.stream_position()?;
let relative_position = current_position
.checked_sub(
self.starting_position
.clone()
.ok_or(binrw::io::Error::other("No starting position"))?,
)
.ok_or(binrw::io::Error::other(
"Current position must be greater than staring position",
))?;
Ok(relative_position)
}
}
impl<T: binrw::io::Seek> RelativeStream<T> {
fn new(mut inner: T) -> Self {
// No result can be returned here so the error handling is deferred
let starting_position = inner.stream_position().ok();
Self {
inner,
starting_position,
}
}
}
impl<T: binrw::io::Read> binrw::io::Read for RelativeStream<T> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.inner.read(buf)
}
}
impl<T: binrw::io::Seek> binrw::io::Seek for RelativeStream<T> {
fn seek(&mut self, pos: binrw::io::SeekFrom) -> binrw::io::Result<u64> {
self.inner.seek(pos)
}
}
#[binrw::binrw]
#[derive(Debug)]
struct Outer {
len: u8,
#[br(args(len))]
inner: Inner,
}
#[binrw::binrw]
#[derive(Debug)]
#[br(import(len: u8))]
#[br(stream = r, map_stream = RelativeStream::new)]
struct Inner {
pre_1: u8,
pre_2: u16,
// Uses relative stream position to ensure that not only the first element within the stream is parsed correctly
// Relative stream position avoids hardcoding the offset caused by `pre_1` and `pre_2`
#[br(count = (len - r.relative_stream_position()? as u8) as usize)]
data: Vec<u8>,
}
#[binrw::binrw]
#[derive(Debug)]
struct Multi {
#[br(parse_with = binrw::helpers::until_eof)]
all: Vec<Outer>,
}
let mut cursor = binrw::io::Cursor::new(hex!("080102FFFFFFFFFFFF060304DDDDDDDD"));
let multi = Multi::read_le(&mut cursor).unwrap();
dbg!(multi);
} |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
I think this might solve my use case as well. I have a struct that has a e.g.,
Unless there is already a way to do this and I missed it. |
Beta Was this translation helpful? Give feedback.
-
For this use case you should be able to use use binrw::io::TakeSeekExt;
#[binrw::binrw]
struct Outer {
len: u8,
#[br(map_stream(|s| s.take_seek(len.into())))]
inner: Inner,
}
#[binrw::binrw]
struct Inner {
pre_1: u8,
pre_2: u16,
#[br(parse_with = binrw::helpers::until_eof)]
data: Vec<u8>,
} |
Beta Was this translation helpful? Give feedback.
For this use case you should be able to use
map_stream
,TakeSeekExt
, anduntil_eof
: