diff options
Diffstat (limited to 'common/object/src/lib.rs')
| -rw-r--r-- | common/object/src/lib.rs | 67 |
1 files changed, 48 insertions, 19 deletions
diff --git a/common/object/src/lib.rs b/common/object/src/lib.rs index 9f9e0be..831dee7 100644 --- a/common/object/src/lib.rs +++ b/common/object/src/lib.rs @@ -4,7 +4,11 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ +mod buffer; +#[cfg(test)] +mod tests; mod value; +pub use buffer::*; pub use value::*; use std::marker::PhantomData; @@ -14,17 +18,7 @@ use std::marker::PhantomData; pub struct Tag(pub u32); pub struct TypedTag<T>(pub Tag, pub PhantomData<T>); -pub struct ObjectBuffer(pub Vec<u32>); - -impl ObjectBuffer { - pub fn new() -> Self { - Self(vec![0]) - } - pub fn as_object<'a>(&'a self) -> Object<'a> { - Object::load(&self.0).unwrap() - } -} - +#[derive(Debug)] pub struct Object<'a> { tags: &'a [u32], offsets: &'a [u32], @@ -33,25 +27,28 @@ pub struct Object<'a> { impl<'a> Object<'a> { pub fn load(buf: &'a [u32]) -> Option<Self> { let nf = *buf.get(0)? as usize; + if buf.len() < 1 + nf * 2 { + return None; + } Some(Self { tags: &buf[1..1 + nf], offsets: &buf[1 + nf..1 + nf + nf], values: &buf[1 + nf + nf..], }) } - fn find_field(&self, tag: Tag) -> Option<usize> { + pub fn find_field(&self, tag: Tag) -> Option<usize> { // using partition as binary search for the first field (instead of regular binary_search that returns any) - let first = self.tags.partition_point(|&x| x >= tag.0); + let first = self.tags.partition_point(|&x| x < tag.0); self.tags .get(first) .is_some_and(|&x| x == tag.0) .then_some(first) } fn get_aligned(&self, index: usize) -> Option<&[u32]> { - let start_raw = *self.offsets.get(index)?; + let start_raw = self.offsets[index]; let end_raw = self .offsets - .get(index) + .get(index + 1) .copied() .unwrap_or((self.values.len() as u32) << 2); @@ -61,10 +58,10 @@ impl<'a> Object<'a> { Some(&self.values[start as usize..end as usize]) } fn get_unaligned(&self, index: usize) -> Option<&[u8]> { - let start_raw = *self.offsets.get(index)?; + let start_raw = self.offsets[index]; let end_raw = self .offsets - .get(index) + .get(index + 1) .copied() .unwrap_or((self.values.len() as u32) << 2); @@ -75,12 +72,44 @@ impl<'a> Object<'a> { let values_u8: &[u8] = bytemuck::cast_slice(self.values); Some(&values_u8[start as usize..end as usize]) } - pub fn get<'b: 'a, T: Value<'b>>(&'b self, tag: TypedTag<T>) -> Option<T> { - let index = self.find_field(tag.0)?; + #[inline] + pub fn get_typed<'b: 'a, T: Value<'b>>(&'b self, index: usize) -> Option<T> { if T::ALIGNED { T::load_aligned(self.get_aligned(index)?) } else { T::load_unaligned(self.get_unaligned(index)?) } } + pub fn get<'b: 'a, T: Value<'b>>(&'b self, tag: TypedTag<T>) -> Option<T> { + self.get_typed(self.find_field(tag.0)?) + } + pub fn iter<'b: 'a, T>(&'b self, tag: TypedTag<T>) -> FieldIter<'b, T> { + FieldIter { + object: self, + index: self.tags.partition_point(|&x| x < tag.0.0), + tag: tag.0.0, + ty: PhantomData, + } + } +} + +pub struct FieldIter<'a, T> { + object: &'a Object<'a>, + index: usize, + tag: u32, + ty: PhantomData<T>, +} +impl<'a, T: Value<'a>> Iterator for FieldIter<'a, T> { + type Item = T; + fn next(&mut self) -> Option<Self::Item> { + if self.index >= self.object.tags.len() { + return None; + } + if self.object.tags[self.index] != self.tag { + return None; + } + let val = self.object.get_typed(self.index); + self.index += 1; + val + } } |