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.rs100
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> {