diff options
Diffstat (limited to 'common/object/src')
| -rw-r--r-- | common/object/src/lib.rs | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/common/object/src/lib.rs b/common/object/src/lib.rs new file mode 100644 index 0000000..33d38d1 --- /dev/null +++ b/common/object/src/lib.rs @@ -0,0 +1,132 @@ +/* + This file is part of jellything (https://codeberg.org/metamuffin/jellything) + which is licensed under the GNU Affero General Public License (version 3); see /COPYING. + Copyright (C) 2025 metamuffin <metamuffin.org> +*/ + +use std::marker::PhantomData; + +#[repr(transparent)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +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() + } +} + +pub struct Object<'a> { + tags: &'a [u32], + offsets: &'a [u32], + values: &'a [u32], +} +impl<'a> Object<'a> { + pub fn load(buf: &'a [u32]) -> Option<Self> { + let nf = *buf.get(0)? as usize; + Some(Self { + tags: &buf[1..1 + nf], + offsets: &buf[1 + nf..1 + nf + nf], + values: &buf[1 + nf + nf..], + }) + } + pub fn get_aligned(&self, tag: Tag) -> Option<&[u32]> { + let index = self.tags.binary_search(&tag.0).ok()?; + let start_raw = *self.offsets.get(index)?; + let end_raw = self + .offsets + .get(index) + .copied() + .unwrap_or((self.values.len() as u32) << 2); + + let start = start_raw >> 2; + let end = end_raw >> 2; + + Some(&self.values[start as usize..end as usize]) + } + pub fn get_unaligned(&self, tag: Tag) -> Option<&[u8]> { + let index = self.tags.binary_search(&tag.0).ok()?; + let start_raw = *self.offsets.get(index)?; + let end_raw = self + .offsets + .get(index) + .copied() + .unwrap_or((self.values.len() as u32) << 2); + + let start = (start_raw >> 2) * 4; + let padding = start_raw & 0b11; + let end = (end_raw >> 2) * 4 - padding; + + let values_u8: &[u8] = bytemuck::cast_slice(self.values); + Some(&values_u8[start as usize..end as usize]) + } + pub fn get_str(&self, tag: Tag) -> Option<&str> { + self.get_unaligned(tag).and_then(|b| str::from_utf8(b).ok()) + } + + pub fn get<'b: 'a, T: Value<'b>>(&'b self, tag: TypedTag<T>) -> Option<T> { + if T::ALIGNED { + T::load_aligned(self.get_aligned(tag.0)?) + } else { + T::load_unaligned(self.get_unaligned(tag.0)?) + } + } +} + +pub trait Value<'a>: Sized { + const ALIGNED: bool; + fn load_aligned(buf: &'a [u32]) -> Option<Self> { + let _ = buf; + None + } + fn load_unaligned(buf: &'a [u8]) -> Option<Self> { + let _ = buf; + None + } + fn store(&self, buf: &mut Vec<u8>); + fn size(&self) -> usize; +} +impl<'a> Value<'a> for &'a str { + const ALIGNED: bool = false; + fn load_unaligned(buf: &'a [u8]) -> Option<Self> { + str::from_utf8(buf).ok() + } + fn store(&self, buf: &mut Vec<u8>) { + buf.extend(self.as_bytes()); + } + fn size(&self) -> usize { + self.len() + } +} +impl Value<'_> for u32 { + const ALIGNED: bool = false; + fn load_aligned(buf: &[u32]) -> Option<Self> { + buf.get(0).copied() + } + fn store(&self, buf: &mut Vec<u8>) { + buf.extend(self.to_ne_bytes()); + } + fn size(&self) -> usize { + 4 + } +} +impl Value<'_> for u64 { + const ALIGNED: bool = false; + fn load_aligned(buf: &[u32]) -> Option<Self> { + let hi = *buf.get(0)? as u64; + let lo = *buf.get(1)? as u64; + Some(hi << 32 | lo) + } + fn store(&self, buf: &mut Vec<u8>) { + buf.extend(self.to_ne_bytes()); + } + fn size(&self) -> usize { + 8 + } +} |