Closed wcampbell0x2a closed 5 months ago
Looks great! Thanks for your work.
Sometimes it's useful to have both read_all
and until
. Here's a workaround I made for this purpose, based on this branch:
#[derive(Debug)]
struct VecTilEnd<T>(Vec<T>);
impl<'a, T, Ctx, Predicate> DekuReader<'a, (deku::ctx::Limit<T, Predicate>, Ctx)> for VecTilEnd<T>
where
T: DekuReader<'a, Ctx>,
Ctx: Copy,
Predicate: FnMut(&T) -> bool,
{
fn from_reader_with_ctx<R: std::io::Read>(
reader: &mut deku::reader::Reader<R>,
(limit, inner_ctx): (deku::ctx::Limit<T, Predicate>, Ctx),
) -> Result<Self, DekuError>
where
Self: Sized,
{
let deku::ctx::Limit::Until(mut predicate, _) = limit else {
return Err(DekuError::Parse("`until` is required here".to_string()));
};
let mut res = Vec::new();
let start_read = reader.bits_read;
loop {
if reader.end() {
break;
}
let val = <T>::from_reader_with_ctx(reader, inner_ctx)?;
res.push(val);
if predicate(res.last().unwrap()) {
break;
}
}
Ok(Self(res))
}
}
#[derive(Debug, DekuRead)]
struct MaybeProperty {
#[deku(until = "|b: &u8| *b != b'\\n'")]
ws: VecTilEnd<u8>,
#[deku(cond = "ws.0.last().map_or(false, |c| c != &b'[')")]
property: Option<Property>,
}
(For this very silly thing: https://gist.github.com/passcod/9148782657f3f40df75ff85b8dd77c2a)
@passcod could you clarify your use case with some examples? Trying to understand what you're trying to achieve. I attempted something here, but I didn't need VecTilEnd
and could use a Vec
to get the same result. I suspect I'm missing something with the "til end" portion.
#[derive(Debug, PartialEq, Eq, DekuRead)]
struct MaybePropertyVecTilEnd {
#[deku(until = "|b: &u8| *b != b'\\n'")]
ws: VecTilEnd<u8>,
field: u8,
}
#[derive(Debug, PartialEq, Eq, DekuRead)]
struct MaybePropertyVec {
#[deku(until = "|b: &u8| *b != b'\\n'")]
ws: Vec<u8>,
field: u8,
}
fn main() {
{
let mut input = Cursor::new(b"\n\n\n[a");
let (_, prop) = MaybePropertyVecTilEnd::from_reader((&mut input, 0)).unwrap();
assert_eq!(
prop,
MaybePropertyVecTilEnd {
ws: VecTilEnd(b"\n\n\n[".to_vec()),
field: 0x61,
}
);
}
{
let mut input = Cursor::new(b"\n\n\n[a");
let (_b, prop) = MaybePropertyVec::from_reader((&mut input, 0)).unwrap();
assert_eq!(
prop,
MaybePropertyVec {
ws: b"\n\n\n[".to_vec(), // same as VecTilEnd?
field: 0x61,
}
);
}
}
I've added the input file to the gist.
The way I reasoned about it is that I needed to read ws
(which is a few layers deep in the structure) "until either a match or EOF".
[Section]
Key=value
For example this is:
"\n[Section]\nKey=value\n"
and so I read (in the full example in gist):
Unit::head_ws: (read until '['): \n[
Section::name: (read until ']'): Section]
MaybeProperty::ws: (read until not '\n'): \nK
Property::key: (read until '='): ey=
Property::value: (read until '\n'): value\n
MaybeProperty::ws: (read until not '\n'): !!! need one more byte !!!
(error!)
With VecTilEnd
it continues instead:
MaybeProperty::ws: (read until not '\n' or EOF): *EOF* -> empty vec
MaybeProperty::property (not read because ws is empty -> None)
Section::properties (closes out because MaybeProperty::property is None)
Unit::sections (closes out because Section is done)
(successful parse!)
Now, deku isn't really adapted for this particular usecase, but it does seem like "read until either EOF or a delimiter" would be useful in general.
handy, thank you - works for me.
@wcampbell0x2a what do you think of the above?
Could be neat to have an indicator of "eof" which could be used in attributes such as:
#[deku(until = "|v: &u8| *v == 0 || deku::eof")]
data: Vec<u8>
I'm ok with adding a read_all
attribute to facilitate this use case, but thinking about others such as the one mentioned by @\passcod
I was thinking about this and I'm not sure (out of ignorance; haven't looked) if it would work with deku internals, but something like
until_next = |b: Option<T>| { ... }
that would 'peek' at the next byte/bit/item (where None
is EOF or 'no more data available') and stop at the current if it returns true. That could cover both the EOF case and some part of #100.
@wcampbell0x2a what do you think of the above?
Could be neat to have an indicator of "eof" which could be used in attributes such as:
#[deku(until = "|v: &u8| *v == 0 || deku::eof")] data: Vec<u8>
I'm ok with adding a
read_all
attribute to facilitate this use case, but thinking about others such as the one mentioned by @\passcod
How would this convert into a Limit(_)
?
@sharksforarms I added a Add-To-Changelog
label, which is something I usually use to keep track of issues/MRs that are merged that don't have a changelog entry before releasing. I you don't need/use them lmk
Add read_all attribute to read until the reader.end() returns true
Fix couple of doc bugs
Closes #230