aboutsummaryrefslogtreecommitdiff
path: root/common/object
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-01-16 04:21:06 +0100
committermetamuffin <metamuffin@disroot.org>2026-01-16 04:21:06 +0100
commit9b5d11a2a39e0030ce4eeab8905972f9472c7d27 (patch)
tree90f6e68f8595309b54bb014db509cd709234c9cc /common/object
parentc836b650eaf4ba33b1cfd2b475971b3ccc9f69b7 (diff)
downloadjellything-9b5d11a2a39e0030ce4eeab8905972f9472c7d27.tar
jellything-9b5d11a2a39e0030ce4eeab8905972f9472c7d27.tar.bz2
jellything-9b5d11a2a39e0030ce4eeab8905972f9472c7d27.tar.zst
object extend method
Diffstat (limited to 'common/object')
-rw-r--r--common/object/src/buffer.rs1
-rw-r--r--common/object/src/lib.rs100
-rw-r--r--common/object/src/registry.rs6
-rw-r--r--common/object/src/value.rs53
4 files changed, 152 insertions, 8 deletions
diff --git a/common/object/src/buffer.rs b/common/object/src/buffer.rs
index 2f9d066..f73b22e 100644
--- a/common/object/src/buffer.rs
+++ b/common/object/src/buffer.rs
@@ -7,6 +7,7 @@
use crate::{Object, Tag, ValueStore};
use bytemuck::try_cast_vec;
+#[derive(PartialEq, Eq, Hash)]
pub struct ObjectBuffer(pub Vec<u32>);
impl ObjectBuffer {
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> {
diff --git a/common/object/src/registry.rs b/common/object/src/registry.rs
index 4727600..39ed67a 100644
--- a/common/object/src/registry.rs
+++ b/common/object/src/registry.rs
@@ -32,6 +32,12 @@ impl Registry {
pub fn info(&self, tag: Tag) -> Option<&TagInfo> {
self.tags.get(&tag)
}
+ pub fn name(&self, tag: Tag) -> String {
+ match self.tags.get(&tag) {
+ Some(inf) => inf.name.to_string(),
+ None => format!("unknown_tag_{:04x}", tag.0),
+ }
+ }
}
pub struct TagInfo {
diff --git a/common/object/src/value.rs b/common/object/src/value.rs
index aad6101..83878d3 100644
--- a/common/object/src/value.rs
+++ b/common/object/src/value.rs
@@ -4,7 +4,7 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::{Object, ObjectBuffer};
+use crate::{Object, ObjectBuffer, Tag};
pub trait Value<'a>: ValueStore + Sized {
const ALIGNED: bool;
@@ -57,6 +57,23 @@ impl ValueStore for u32 {
4
}
}
+impl Value<'_> for Tag {
+ const ALIGNED: bool = true;
+ fn load_aligned(buf: &[u32]) -> Option<Self> {
+ buf.get(0).copied().map(u32::from_be).map(Tag)
+ }
+}
+impl ValueStore for Tag {
+ fn is_aligned(&self) -> bool {
+ true
+ }
+ fn store_aligned(&self, buf: &mut Vec<u32>) {
+ buf.push(self.0.to_be());
+ }
+ fn size(&self) -> usize {
+ 4
+ }
+}
impl Value<'_> for u64 {
const ALIGNED: bool = false;
fn load_aligned(buf: &[u32]) -> Option<Self> {
@@ -77,6 +94,40 @@ impl ValueStore for u64 {
8
}
}
+impl Value<'_> for f64 {
+ const ALIGNED: bool = false;
+ fn load_aligned(buf: &[u32]) -> Option<Self> {
+ u32::load_aligned(buf).map(|x| x as f64)
+ }
+}
+impl ValueStore for f64 {
+ fn is_aligned(&self) -> bool {
+ true
+ }
+ fn store_aligned(&self, buf: &mut Vec<u32>) {
+ (*self as u64).store_aligned(buf);
+ }
+ fn size(&self) -> usize {
+ 8
+ }
+}
+impl Value<'_> for i64 {
+ const ALIGNED: bool = false;
+ fn load_aligned(buf: &[u32]) -> Option<Self> {
+ u32::load_aligned(buf).map(|x| x as i64)
+ }
+}
+impl ValueStore for i64 {
+ fn is_aligned(&self) -> bool {
+ true
+ }
+ fn store_aligned(&self, buf: &mut Vec<u32>) {
+ (*self as u64).store_aligned(buf);
+ }
+ fn size(&self) -> usize {
+ 8
+ }
+}
impl<'a> Value<'a> for Object<'a> {
const ALIGNED: bool = true;
fn load_aligned(buf: &'a [u32]) -> Option<Self> {