diff options
Diffstat (limited to 'common/object/src/lib.rs')
| -rw-r--r-- | common/object/src/lib.rs | 100 |
1 files changed, 93 insertions, 7 deletions
diff --git a/common/object/src/lib.rs b/common/object/src/lib.rs index 02b2293..290cf91 100644 --- a/common/object/src/lib.rs +++ b/common/object/src/lib.rs @@ -13,26 +13,45 @@ mod registry; mod tests; mod value; pub use buffer::*; +pub use path::*; pub use registry::*; pub use value::*; -pub use path::*; -use std::marker::PhantomData; +use std::{collections::HashSet, hash::Hash, marker::PhantomData}; #[repr(transparent)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Tag(pub u32); -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct TypedTag<T>(pub Tag, pub PhantomData<T>); -#[derive(Debug, Clone, Copy)] +impl<T> Clone for TypedTag<T> { + fn clone(&self) -> Self { + Self(self.0, PhantomData) + } +} +impl<T> Copy for TypedTag<T> {} + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub struct Object<'a> { tags: &'a [u32], offsets: &'a [u32], values: &'a [u32], } + +impl<'a> Default for Object<'a> { + fn default() -> Self { + Self::EMPTY + } +} impl<'a> Object<'a> { + pub const EMPTY: Object<'static> = Object { + offsets: &[], + tags: &[], + values: &[], + }; + pub fn load(buf: &'a [u32]) -> Option<Self> { let nf = *buf.get(0)? as usize; if buf.len() < 1 + nf * 2 { @@ -44,6 +63,14 @@ impl<'a> Object<'a> { values: &buf[1 + nf + nf..], }) } + pub fn dump(&self) -> ObjectBuffer { + let mut out = Vec::new(); + out.push(self.tags.len() as u32); + out.extend(self.tags); + out.extend(self.offsets); + out.extend(self.values); + ObjectBuffer(out) + } 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); @@ -52,6 +79,9 @@ impl<'a> Object<'a> { .is_some_and(|&x| x == tag.0) .then_some(first) } + pub fn has(&self, tag: Tag) -> bool { + self.find_field(tag).is_some() + } #[inline] fn offset(&self, i: usize) -> usize { self.offsets @@ -119,9 +149,55 @@ impl<'a> Object<'a> { ty: PhantomData, } } + #[must_use] + pub fn extend<T: Value<'a> + Hash + Eq + PartialEq>( + &self, + tag: TypedTag<T>, + values: impl IntoIterator<Item = T>, + ) -> ObjectBuffer { + self.insert_multi( + tag, + &self + .iter(tag) + .chain(values) + .collect::<HashSet<_>>() + .into_iter() + .collect::<Vec<_>>(), + ) + } + #[must_use] + pub fn extend_object( + &self, + tag: TypedTag<Object<'static>>, + ident: Tag, + values: impl IntoIterator<Item = ObjectBuffer>, + ) -> ObjectBuffer { + let ident = TypedTag(ident, PhantomData::<&[u8]>); + let mut new_vals = Vec::new(); + for ob in self.iter(tag) { + new_vals.push(ob.dump()); + } + let mut any_new = false; + for val in values { + if new_vals + .iter() + .all(|rhs| rhs.as_object().get(ident) != val.as_object().get(ident)) + { + any_new = false; + new_vals.push(val); + } + } + if any_new { + self.insert_multi(TypedTag(tag.0, PhantomData), &new_vals) + } else { + self.dump() + } + } + #[must_use] pub fn insert<T: ValueStore>(&self, tag: TypedTag<T>, value: T) -> ObjectBuffer { self.insert_multi(tag, &[value]) } + #[must_use] pub fn insert_multi<T: ValueStore>(&self, tag: TypedTag<T>, values: &[T]) -> ObjectBuffer { let prefix = self.tags.partition_point(|&x| x < tag.0.0); let suffix = self.tags.partition_point(|&x| x <= tag.0.0); @@ -163,13 +239,23 @@ impl<'a> Object<'a> { let values_insert_size = buf.len() - values_new; let values_cut_size = values_suffix - values_prefix; let suffix_offset = (values_insert_size as i32 - values_cut_size as i32) * 4; - buf[suffix_offs..suffix_end] - .iter_mut() - .for_each(|e| *e = e.strict_add_signed(suffix_offset)); + if suffix_offset != 0 { + buf[suffix_offs..suffix_end] + .iter_mut() + .for_each(|e| *e = e.strict_add_signed(suffix_offset)); + } buf.extend(&self.values[values_suffix..]); ObjectBuffer(buf) } + #[must_use] + pub fn update( + &self, + tag: TypedTag<Object<'static>>, + update: impl FnOnce(Object<'a>) -> ObjectBuffer, + ) -> ObjectBuffer { + self.insert(tag, update(self.get(tag).unwrap_or_default()).as_object()) + } } pub struct KeysIter<'a> { |