diff --git a/src/lib.rs b/src/lib.rs index d083f0b..8d7f257 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,16 +20,16 @@ pub enum KError<'a> { } pub type KResult<'a, T> = Result>; -pub trait KStruct<'a>: Default { - type Parent: KStruct<'a>; - type Root: KStruct<'a>; +pub trait KStruct<'r, 's: 'r>: Default { + type Root: KStruct<'r, 's>; + type ParentStack; /// Parse this struct (and any children) from the supplied stream - fn read<'s: 'a, S: KStream>( + fn read( &mut self, _io: &'s S, - _root: Option<&Self::Root>, - _parent: Option<&Self::Parent>, + _root: Option<&'r Self::Root>, + _parent: TypedStack, ) -> KResult<'s, ()>; } @@ -38,20 +38,55 @@ pub trait KStruct<'a>: Default { /// `KStruct` trait. #[derive(Debug, Default, Copy, Clone, PartialEq)] pub struct KStructUnit; -impl<'a> KStruct<'a> for KStructUnit { - type Parent = KStructUnit; +impl KStructUnit { + pub fn parent_stack() -> TypedStack<(KStructUnit)> { + TypedStack { current: (KStructUnit) } + } +} +impl<'r, 's: 'r> KStruct<'r, 's> for KStructUnit { type Root = KStructUnit; + type ParentStack = (KStructUnit); - fn read<'s: 'a, S: KStream>( + fn read( &mut self, _io: &'s S, - _root: Option<&Self::Root>, - _parent: Option<&Self::Parent>, + _root: Option<&'r Self::Root>, + _parent: TypedStack, ) -> KResult<'s, ()> { Ok(()) } } +#[derive(Debug, PartialEq)] +pub struct TypedStack { + current: C, +} +impl TypedStack +where + C: Clone, +{ + pub fn push(&self, next: N) -> TypedStack<(N, C)> { + TypedStack { + current: (next, self.current.clone()), + } + } +} +impl TypedStack<(C, P)> +where + C: Clone, + P: Clone, +{ + pub fn peek(&self) -> &C { + &self.current.0 + } + + pub fn pop(&self) -> TypedStack<(P)> { + TypedStack { + current: (self.current.clone().1), + } + } +} + pub trait KStream { fn is_eof(&self) -> KResult; fn seek(&self, position: u64) -> KResult<()>; @@ -276,25 +311,41 @@ impl<'a> KStream for BytesReader<'a> { } macro_rules! kf_max { - ($i: ident, $t: ty) => ( + ($i: ident, $t: ty) => { pub fn $i<'a>(first: Option<&'a $t>, second: &'a $t) -> Option<&'a $t> { - if second.is_nan() { first } - else if first.is_none() { Some(second) } - else { if first.unwrap() < second { Some(second) } else { first } } + if second.is_nan() { + first + } else if first.is_none() { + Some(second) + } else { + if first.unwrap() < second { + Some(second) + } else { + first + } + } } - ) + }; } kf_max!(kf32_max, f32); kf_max!(kf64_max, f64); macro_rules! kf_min { - ($i: ident, $t: ty) => ( + ($i: ident, $t: ty) => { pub fn $i<'a>(first: Option<&'a $t>, second: &'a $t) -> Option<&'a $t> { - if second.is_nan() { first } - else if first.is_none() { Some(second) } - else { if first.unwrap() < second { first } else { Some(second) } } + if second.is_nan() { + first + } else if first.is_none() { + Some(second) + } else { + if first.unwrap() < second { + first + } else { + Some(second) + } + } } - ) + }; } kf_min!(kf32_min, f32); kf_min!(kf64_min, f64); @@ -365,6 +416,19 @@ mod tests { let b: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9]; let reader = BytesReader::new(&b[..]); - assert_eq!(reader.read_bits_int(65).unwrap_err(), KError::ReadBitsTooLarge { requested: 65 }) + assert_eq!( + reader.read_bits_int(65).unwrap_err(), + KError::ReadBitsTooLarge { requested: 65 } + ) + } + + #[test] + fn stack_clone() { + let t = TypedStack { current: () }; + let t2: TypedStack<(u8, ())> = t.push(12); + let t3: TypedStack<(u16, (u8, ()))> = t2.push(14); + + assert_eq!(*t3.peek(), 14); + assert_eq!(*t3.pop().peek(), *t2.peek()); } } diff --git a/tests/grandparent_validation.rs b/tests/grandparent_validation.rs new file mode 100644 index 0000000..bc76e3e --- /dev/null +++ b/tests/grandparent_validation.rs @@ -0,0 +1,79 @@ +use kaitai::*; + +#[derive(Default, Debug)] +struct Grandparent<'s> { + value: &'s [u8], + parent: Option>, +} +impl<'r, 's: 'r> KStruct<'r, 's> for Grandparent<'s> { + type Root = Self; + type ParentStack = (KStructUnit); + + fn read( + &mut self, + _io: &'s S, + _root: Option<&'r Self::Root>, + _parent: TypedStack, + ) -> KResult<'s, ()> { + self.value = _io.read_bytes(1)?; + + let mut tmp = Parent::default(); + tmp.read(_io, Some(self), _parent.push(self)); + self.parent = Some(tmp); + Ok(()) + } +} + +#[derive(Default, Debug)] +struct Parent<'s> { + value: &'s [u8], + child: Option, +} +impl<'r, 's: 'r> KStruct<'r, 's> for Parent<'s> { + type Root = Grandparent<'s>; + type ParentStack = (&'r Grandparent<'s>, as KStruct<'r, 's>>::ParentStack); + + fn read( + &mut self, + _io: &'s S, + _root: Option<&'r Self::Root>, + _parent: TypedStack, + ) -> KResult<'s, ()> { + self.value = _io.read_bytes(1)?; + + let mut tmp = Child::default(); + tmp.read(_io, _root, _parent.push(self)); + self.child = Some(tmp); + + Ok(()) + } +} + +#[derive(Default, Debug)] +struct Child { + gp_value: u8, +} +impl<'r, 's: 'r> KStruct<'r, 's> for Child { + type Root = Grandparent<'s>; + type ParentStack = (&'r Parent<'s>, as KStruct<'r, 's>>::ParentStack); + + fn read( + &mut self, + _io: &'s S, + _root: Option<&'r Self::Root>, + _parent: TypedStack, + ) -> KResult<'s, ()> { + self.gp_value = _parent.pop().peek().value[0]; + + Ok(()) + } +} + +#[test] +fn basic_parse() { + let bytes = [0u8, 1]; + let r = BytesReader::new(&bytes); + + let mut gp = Grandparent::default(); + gp.read(&r, None, KStructUnit::parent_stack()); +} \ No newline at end of file diff --git a/tests/lifetime_validation.rs b/tests/lifetime_validation.rs index b59874c..f714994 100644 --- a/tests/lifetime_validation.rs +++ b/tests/lifetime_validation.rs @@ -1,52 +1,48 @@ //! Example using hand-coded structs to validate that the borrow checker //! will allow our code to actually run -use kaitai::{BytesReader, KError, KResult, KStream, KStruct, KStructUnit}; +use kaitai::{BytesReader, KError, KResult, KStream, KStruct, KStructUnit, TypedStack}; #[derive(Debug, PartialEq, Clone, Default)] -struct TestRootStruct<'a> { - pub bytes: &'a [u8], - pub child: Option>, +struct TestRootStruct<'s> { + pub bytes: &'s [u8], + pub child: Option>, } #[derive(Debug, PartialEq, Clone, Default)] -struct TestChildStruct<'a> { - pub bytes: &'a [u8], - pub root_bytes: &'a [u8], +struct TestChildStruct<'s> { + pub bytes: &'s [u8], + pub root_bytes: &'s [u8], } -impl<'a> KStruct<'a> for TestRootStruct<'a> { - type Parent = KStructUnit; - type Root = TestRootStruct<'a>; +impl<'r, 's: 'r> KStruct<'r, 's> for TestRootStruct<'s> { + type Root = Self; + type ParentStack = (KStructUnit); - fn read<'s: 'a, S: KStream>( + fn read( &mut self, _io: &'s S, - _root: Option<&Self::Root>, - _parent: Option<&Self::Parent>, + _root: Option<&'r Self::Root>, + _parent: TypedStack, ) -> KResult<'s, ()> { self.bytes = _io.read_bytes(1)?; - // TODO: `new` method in KStruct? let mut child = TestChildStruct::default(); - // Implementation note: because callers of `read` can't call us as - // `struct.read(_io, Some(struct), None)`, we have to use the `or` - // call below to give an immutable copy of ourselves to the child - child.read(_io, _root.or(Some(self)), Some(self))?; + child.read(_io, Some(self), _parent.push(self))?; self.child = Some(child); Ok(()) } } -impl<'a> KStruct<'a> for TestChildStruct<'a> { - type Parent = TestRootStruct<'a>; - type Root = TestRootStruct<'a>; +impl<'r, 's: 'r> KStruct<'r, 's> for TestChildStruct<'s> { + type Root = as KStruct<'r, 's>>::Root; + type ParentStack = (&'r TestRootStruct<'s>, as KStruct<'r, 's>>::ParentStack); - fn read<'s: 'a, S: KStream>( + fn read( &mut self, _io: &'s S, - _root: Option<&Self::Root>, - _parent: Option<&Self::Parent>, + _root: Option<&'r Self::Root>, + _parent: TypedStack, ) -> KResult<'s, ()> { self.bytes = _io.read_bytes(1).unwrap(); _root.map(|r| self.root_bytes = r.bytes).ok_or(KError::MissingRoot)?; @@ -61,7 +57,7 @@ fn basic_parse() { let mut reader = BytesReader::new(&bytes); let mut root = TestRootStruct::default(); - let res = root.read(&mut reader, None, None); + let res = root.read(&mut reader, None, KStructUnit::parent_stack()); assert!(res.is_ok()); assert_eq!([1], root.bytes);