diff options
| author | metamuffin <metamuffin@disroot.org> | 2026-01-16 04:21:06 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2026-01-16 04:21:06 +0100 |
| commit | 9b5d11a2a39e0030ce4eeab8905972f9472c7d27 (patch) | |
| tree | 90f6e68f8595309b54bb014db509cd709234c9cc /common/object | |
| parent | c836b650eaf4ba33b1cfd2b475971b3ccc9f69b7 (diff) | |
| download | jellything-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.rs | 1 | ||||
| -rw-r--r-- | common/object/src/lib.rs | 100 | ||||
| -rw-r--r-- | common/object/src/registry.rs | 6 | ||||
| -rw-r--r-- | common/object/src/value.rs | 53 |
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> { |