aboutsummaryrefslogtreecommitdiff
path: root/common/object/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'common/object/src/lib.rs')
-rw-r--r--common/object/src/lib.rs67
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
+ }
}