diff options
Diffstat (limited to 'common/object')
| -rw-r--r-- | common/object/src/buffer.rs | 5 | ||||
| -rw-r--r-- | common/object/src/debug.rs | 59 | ||||
| -rw-r--r-- | common/object/src/inspect.rs | 59 | ||||
| -rw-r--r-- | common/object/src/json.rs | 34 | ||||
| -rw-r--r-- | common/object/src/lib.rs | 64 | ||||
| -rw-r--r-- | common/object/src/registry.rs | 42 | ||||
| -rw-r--r-- | common/object/src/tests.rs | 34 | ||||
| -rw-r--r-- | common/object/src/value.rs | 78 |
8 files changed, 195 insertions, 180 deletions
diff --git a/common/object/src/buffer.rs b/common/object/src/buffer.rs index 05557e4..829cade 100644 --- a/common/object/src/buffer.rs +++ b/common/object/src/buffer.rs @@ -30,8 +30,9 @@ impl ObjectBuffer { let mut temp = Vec::new(); for (tag, val) in fields { tags.push(tag.0); - let off = (values.len() as u32) << 2; - if val.is_aligned() { + let ty = val.get_type(); + let off = (values.len() as u32) << 5 | (ty as u32) << 2; + if ty.is_aligned() { offsets.push(off); val.store_aligned(&mut values); } else { diff --git a/common/object/src/debug.rs b/common/object/src/debug.rs new file mode 100644 index 0000000..15812b1 --- /dev/null +++ b/common/object/src/debug.rs @@ -0,0 +1,59 @@ +/* + 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) 2026 metamuffin <metamuffin.org> +*/ + +use crate::{Object, ObjectBuffer, Tag, TypedTag, ValueType}; +use std::{any::type_name, fmt::Debug}; + +impl Debug for ObjectBuffer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_object().fmt(f) + } +} + +impl Debug for Object<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut s = f.debug_struct("Object"); + let mut nonexhaustive = false; + for (i, tag) in self.keys().enumerate() { + let kbytes = tag.0.to_be_bytes(); + let k = str::from_utf8(&kbytes).unwrap(); + let ty = self.offset_type(i); + let sz = self.size(i); + match (ty, sz) { + (ValueType::String, _) => s.field(k, &self.get_typed::<&str>(i).unwrap()), + (ValueType::Binary, _) => s.field(k, &self.get_typed::<&[u8]>(i).unwrap()), + (ValueType::Object, _) => s.field(k, &self.get_typed::<Object>(i).unwrap()), + (ValueType::UInt, 4) => s.field(k, &self.get_typed::<u32>(i).unwrap()), + (ValueType::UInt, 8) => s.field(k, &self.get_typed::<u64>(i).unwrap()), + (ValueType::Int, 8) => s.field(k, &self.get_typed::<i64>(i).unwrap()), + _ => { + nonexhaustive = true; + &mut s + } + }; + } + if nonexhaustive { + s.finish_non_exhaustive() + } else { + s.finish() + } + } +} + +impl<T> Debug for TypedTag<T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple(&format!("TypedTag<{}>", type_name::<T>())) + .field(&self.0) + .finish() + } +} +impl Debug for Tag { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let bytes = self.0.to_be_bytes(); + let name = str::from_utf8(&bytes).unwrap(); + f.debug_tuple("Tag").field(&name).finish() + } +} diff --git a/common/object/src/inspect.rs b/common/object/src/inspect.rs deleted file mode 100644 index cfab5c3..0000000 --- a/common/object/src/inspect.rs +++ /dev/null @@ -1,59 +0,0 @@ -/* - 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) 2026 metamuffin <metamuffin.org> -*/ - -use crate::{Object, Registry, Tag, TypedTag, types::*}; -use std::{any::type_name, fmt::Debug}; - -pub struct Inspector<'a, T>(pub &'a Registry, pub T); - -impl Debug for Inspector<'_, Object<'_>> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let mut s = f.debug_struct("Object"); - let mut nonexhaustive = false; - for (i, k) in self.1.keys().enumerate() { - let Some(info) = self.0.info(k) else { - nonexhaustive = true; - continue; - }; - let Some(ty) = info.r#type else { - nonexhaustive = true; - continue; - }; - match ty { - x if x == STR => s.field(info.name, &self.1.get_typed::<&str>(i).unwrap()), - x if x == BINARY => s.field(info.name, &self.1.get_typed::<&[u8]>(i).unwrap()), - x if x == OBJECT => s.field(info.name, &self.1.get_typed::<Object>(i).unwrap()), - x if x == U32 => s.field(info.name, &self.1.get_typed::<u32>(i).unwrap()), - x if x == U64 => s.field(info.name, &self.1.get_typed::<u64>(i).unwrap()), - x if x == I64 => s.field(info.name, &self.1.get_typed::<i64>(i).unwrap()), - _ => { - nonexhaustive = true; - &mut s - } - }; - } - if nonexhaustive { - s.finish_non_exhaustive() - } else { - s.finish() - } - } -} - -impl<T> Debug for Inspector<'_, TypedTag<T>> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let name = self.0.info(self.1.0).map(|t| t.name).unwrap_or("unknown"); - f.debug_tuple(&format!("TypedTag<{}>", type_name::<T>())) - .field(&name) - .finish() - } -} -impl Debug for Inspector<'_, Tag> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let name = self.0.info(self.1).map(|t| t.name).unwrap_or("unknown"); - f.debug_tuple("Tag").field(&name).finish() - } -} diff --git a/common/object/src/json.rs b/common/object/src/json.rs index 0aa4cbd..cbb8bda 100644 --- a/common/object/src/json.rs +++ b/common/object/src/json.rs @@ -4,36 +4,30 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::{Object, Registry, types::*}; +use crate::{Object, ValueType}; use serde_json::{Map, Value}; -pub fn object_to_json(reg: &Registry, ob: Object<'_>) -> Value { +pub fn object_to_json(ob: Object<'_>) -> Value { let mut o = Map::new(); let mut nonexhaustive = false; for (i, tag) in ob.keys().enumerate() { - let Some(info) = reg.info(tag) else { - nonexhaustive = true; - continue; - }; - let Some(ty) = info.r#type else { - nonexhaustive = true; - continue; - }; - let key = info.name.to_string(); - - let val = match ty { - x if x == STR => ob.get_typed::<&str>(i).unwrap().to_string().into(), - x if x == OBJECT => object_to_json(reg, ob.get_typed::<Object>(i).unwrap()), - x if x == U32 => ob.get_typed::<u32>(i).unwrap().into(), - x if x == U64 => ob.get_typed::<u64>(i).unwrap().into(), - x if x == I64 => ob.get_typed::<i64>(i).unwrap().into(), - x if x == F64 => ob.get_typed::<f64>(i).unwrap().into(), + let kbytes = tag.0.to_be_bytes(); + let k = str::from_utf8(&kbytes).unwrap().to_string(); + let ty = ob.offset_type(i); + let sz = ob.size(i); + let val = match (ty, sz) { + (ValueType::String, _) => ob.get_typed::<&str>(i).unwrap().to_string().into(), + (ValueType::Object, _) => object_to_json(ob.get_typed::<Object>(i).unwrap()), + (ValueType::UInt, 4) => ob.get_typed::<u32>(i).unwrap().into(), + (ValueType::UInt, 8) => ob.get_typed::<u64>(i).unwrap().into(), + (ValueType::Int, 8) => ob.get_typed::<i64>(i).unwrap().into(), + (ValueType::Float, 8) => ob.get_typed::<f64>(i).unwrap().into(), _ => { nonexhaustive = true; continue; } }; - multi_insert(&mut o, key, val); + multi_insert(&mut o, k, val); } if nonexhaustive { o.insert("_nonexhaustive".to_owned(), Value::Bool(true)); diff --git a/common/object/src/lib.rs b/common/object/src/lib.rs index 771dd94..bd00639 100644 --- a/common/object/src/lib.rs +++ b/common/object/src/lib.rs @@ -6,7 +6,7 @@ #![feature(iter_array_chunks)] mod buffer; -pub mod inspect; +pub mod debug; #[cfg(feature = "json")] pub mod json; mod path; @@ -20,14 +20,30 @@ pub use path::*; pub use registry::*; pub use value::*; -use std::{collections::BTreeSet, hash::Hash, marker::PhantomData}; +use std::{collections::BTreeSet, fmt::Display, hash::Hash, marker::PhantomData}; #[repr(transparent)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Tag(pub u32); -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +impl Tag { + pub const fn new(fourcc: &[u8; 4]) -> Self { + Self(u32::from_be_bytes(*fourcc)) + } +} +impl Display for Tag { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(str::from_utf8(&self.0.to_be_bytes()).unwrap()) + } +} + +#[derive(PartialEq, Eq, PartialOrd, Ord)] pub struct TypedTag<T>(pub Tag, pub PhantomData<T>); +impl<T> Display for TypedTag<T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} impl<T> Clone for TypedTag<T> { fn clone(&self) -> Self { @@ -36,7 +52,7 @@ impl<T> Clone for TypedTag<T> { } impl<T> Copy for TypedTag<T> {} -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Clone, Copy, Hash, PartialEq, Eq)] pub struct Object<'a> { tags: &'a [u32], offsets: &'a [u32], @@ -89,33 +105,50 @@ impl<'a> Object<'a> { fn offset(&self, i: usize) -> usize { self.offsets .get(i) - .map(|&v| v >> 2) + .map(|&v| v >> 5) .unwrap_or(self.values.len() as u32) as usize } + fn offset_type(&self, i: usize) -> ValueType { + let raw = (self.offsets[i] >> 2) & 0b111; + ValueType::from_num(raw) + } + fn size(&self, i: usize) -> u32 { + let start_raw = self.offsets[i]; + let end_raw = self + .offsets + .get(i + 1) + .copied() + .unwrap_or((self.values.len() as u32) << 5); + + let u32_len = (end_raw >> 5) - (start_raw >> 5); + let padding = start_raw & 0b11; + u32_len * 4 - padding + } fn get_aligned(&self, index: usize) -> Option<&'a [u32]> { let start_raw = self.offsets[index]; let end_raw = self .offsets .get(index + 1) .copied() - .unwrap_or((self.values.len() as u32) << 2); + .unwrap_or((self.values.len() as u32) << 5); - let start = start_raw >> 2; - let end = end_raw >> 2; + let start = start_raw >> 5; + let end = end_raw >> 5; Some(&self.values[start as usize..end as usize]) } + fn get_unaligned(&self, index: usize) -> Option<&'a [u8]> { let start_raw = self.offsets[index]; let end_raw = self .offsets .get(index + 1) .copied() - .unwrap_or((self.values.len() as u32) << 2); + .unwrap_or((self.values.len() as u32) << 5); - let start = (start_raw >> 2) * 4; + let start = (start_raw >> 5) * 4; let padding = start_raw & 0b11; - let end = (end_raw >> 2) * 4 - padding; + let end = (end_raw >> 5) * 4 - padding; let values_u8: &[u8] = bytemuck::cast_slice(self.values); Some(&values_u8[start as usize..end as usize]) @@ -228,8 +261,9 @@ impl<'a> Object<'a> { let mut temp = Vec::new(); let values_new = buf.len(); for (i, val) in values.iter().enumerate() { - let off = (buf.len() as u32 - values_start) << 2; - if val.is_aligned() { + let ty = val.get_type(); + let off = (buf.len() as u32 - values_start) << 5 | (ty as u32) << 2; + if ty.is_aligned() { buf[new_offs + i] = off; val.store_aligned(&mut buf); } else { @@ -242,7 +276,7 @@ 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; + let suffix_offset = (values_insert_size as i32 - values_cut_size as i32) << 5; if suffix_offset != 0 { buf[suffix_offs..suffix_end] .iter_mut() diff --git a/common/object/src/registry.rs b/common/object/src/registry.rs index 5f1f36e..af028e8 100644 --- a/common/object/src/registry.rs +++ b/common/object/src/registry.rs @@ -4,8 +4,7 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::Tag; -use std::{any::TypeId, collections::BTreeMap}; +use std::any::TypeId; pub mod types { use crate::Object; @@ -20,31 +19,6 @@ pub mod types { pub const F64: TypeId = TypeId::of::<f64>(); } -#[derive(Default, Clone)] -pub struct Registry { - tags: BTreeMap<Tag, TagInfo>, -} -impl Registry { - pub fn add(&mut self, tag: Tag, info: TagInfo) { - if let Some(other) = self.tags.get(&tag) { - panic!( - "Conflicting names for tag {}: {:?} vs {:?}", - tag.0, info.name, other.name - ) - } - self.tags.insert(tag, info); - } - pub fn info(&self, tag: Tag) -> Option<&TagInfo> { - self.tags.get(&tag) - } - pub fn name(&self, tag: Tag) -> &str { - match self.tags.get(&tag) { - Some(inf) => inf.name, - None => "unknown", - } - } -} - #[derive(Clone)] pub struct TagInfo { pub name: &'static str, @@ -53,19 +27,13 @@ pub struct TagInfo { #[macro_export] macro_rules! fields { - ($($id:ident: $type:ty = $tag:literal $name:literal;)*) => { - $(pub const $id: $crate::TypedTag<$type> = $crate::TypedTag($crate::Tag($tag), std::marker::PhantomData);)* - pub(crate) fn register_fields(reg: &mut $crate::Registry) { - $(reg.add($crate::Tag($tag), $crate::TagInfo { name: $name, r#type: Some(std::any::TypeId::of::<$type>()) });)* - } + ($($id:ident: $type:ty = $tag:literal;)*) => { + $(pub const $id: $crate::TypedTag<$type> = $crate::TypedTag($crate::Tag::new($tag), std::marker::PhantomData);)* }; } #[macro_export] macro_rules! enums { - ($($id:ident = $tag:literal $name:literal;)*) => { - $(pub const $id: $crate::Tag = $crate::Tag($tag);)* - pub(crate) fn register_enums(reg: &mut $crate::Registry) { - $(reg.add($crate::Tag($tag), $crate::TagInfo { name: $name, r#type: None });)* - } + ($($id:ident = $tag:literal;)*) => { + $(pub const $id: $crate::Tag = $crate::Tag::new($tag);)* }; } diff --git a/common/object/src/tests.rs b/common/object/src/tests.rs index 616ac69..73f7b6f 100644 --- a/common/object/src/tests.rs +++ b/common/object/src/tests.rs @@ -4,19 +4,13 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::{Object, ObjectBuffer, Registry, fields, inspect::Inspector}; -use std::sync::LazyLock; +use crate::{Object, ObjectBuffer, fields}; -pub static TAGREG: LazyLock<Registry> = LazyLock::new(|| { - let mut reg = Registry::default(); - register_fields(&mut reg); - reg -}); fields! { - NAME: &str = 15 "name"; - AGE: u32 = 13 "age"; - FRIEND: &str = 54321 "friend"; - STUFF: Object = 3 "stuff"; + NAME: &str = b"name"; + AGE: u32 = b"age1"; + FRIEND: &str = b"frnd"; + STUFF: Object = b"stff"; } fn test_object() -> ObjectBuffer { @@ -86,21 +80,9 @@ fn insert_empty() { } #[test] -fn inspect_object() { - let bob = test_object(); - eprintln!("{:#?}", Inspector(&TAGREG, bob.as_object())); - // panic!() -} - -#[test] -fn inspect_tag() { - assert_eq!( - format!("{:?}", Inspector(&TAGREG, FRIEND)), - "TypedTag<&str>(\"friend\")" - ); +fn debug() { assert_eq!( - format!("{:?}", Inspector(&TAGREG, AGE)), - "TypedTag<u32>(\"age\")" + format!("{:?}", test_object()), + "Object { age1: 35, frnd: \"Alice\", frnd: \"Charlie\", name: \"Bob\" }" ); - assert_eq!(format!("{:?}", Inspector(&TAGREG, NAME.0)), "Tag(\"name\")"); } diff --git a/common/object/src/value.rs b/common/object/src/value.rs index 733e3a0..eb9de7c 100644 --- a/common/object/src/value.rs +++ b/common/object/src/value.rs @@ -17,8 +17,44 @@ pub trait Value<'a>: ValueStore + Sized { unimplemented!() } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ValueType { + Binary, + String, + Object, + UInt, + Int, + Tag, + Float, + Bool, +} + +impl ValueType { + #[inline] + pub fn is_aligned(self) -> bool { + match self { + ValueType::Binary | ValueType::String => false, + _ => true, + } + } + pub fn from_num(n: u32) -> Self { + match n { + 0 => Self::Binary, + 1 => Self::String, + 2 => Self::Object, + 3 => Self::UInt, + 4 => Self::Int, + 5 => Self::Tag, + 6 => Self::Float, + 7 => Self::Bool, + _ => unreachable!(), + } + } +} + pub trait ValueStore { - fn is_aligned(&self) -> bool; + fn get_type(&self) -> ValueType; fn store_aligned(&self, _buf: &mut Vec<u32>) {} fn store_unaligned(&self, _buf: &mut Vec<u8>) {} fn size(&self) -> usize; @@ -30,8 +66,8 @@ impl<'a> Value<'a> for &'a str { } } impl ValueStore for &str { - fn is_aligned(&self) -> bool { - false + fn get_type(&self) -> ValueType { + ValueType::String } fn store_unaligned(&self, buf: &mut Vec<u8>) { buf.extend(self.as_bytes()); @@ -47,8 +83,8 @@ impl Value<'_> for u32 { } } impl ValueStore for u32 { - fn is_aligned(&self) -> bool { - true + fn get_type(&self) -> ValueType { + ValueType::UInt } fn store_aligned(&self, buf: &mut Vec<u32>) { buf.push(self.to_be()); @@ -64,8 +100,8 @@ impl Value<'_> for Tag { } } impl ValueStore for Tag { - fn is_aligned(&self) -> bool { - true + fn get_type(&self) -> ValueType { + ValueType::String } fn store_aligned(&self, buf: &mut Vec<u32>) { buf.push(self.0.to_be()); @@ -83,8 +119,8 @@ impl Value<'_> for u64 { } } impl ValueStore for u64 { - fn is_aligned(&self) -> bool { - true + fn get_type(&self) -> ValueType { + ValueType::UInt } fn store_aligned(&self, buf: &mut Vec<u32>) { buf.push(((self >> 32) as u32).to_be()); @@ -108,8 +144,8 @@ impl Value<'_> for f64 { } } impl ValueStore for f64 { - fn is_aligned(&self) -> bool { - true + fn get_type(&self) -> ValueType { + ValueType::Float } fn store_aligned(&self, buf: &mut Vec<u32>) { let b = self.to_be_bytes(); @@ -127,8 +163,8 @@ impl Value<'_> for i64 { } } impl ValueStore for i64 { - fn is_aligned(&self) -> bool { - true + fn get_type(&self) -> ValueType { + ValueType::Int } fn store_aligned(&self, buf: &mut Vec<u32>) { (*self as u64).store_aligned(buf); @@ -144,8 +180,8 @@ impl<'a> Value<'a> for Object<'a> { } } impl ValueStore for Object<'_> { - fn is_aligned(&self) -> bool { - true + fn get_type(&self) -> ValueType { + ValueType::Object } fn store_aligned(&self, buf: &mut Vec<u32>) { buf.push(self.tags.len() as u32); @@ -158,8 +194,8 @@ impl ValueStore for Object<'_> { } } impl ValueStore for ObjectBuffer { - fn is_aligned(&self) -> bool { - true + fn get_type(&self) -> ValueType { + ValueType::Object } fn store_aligned(&self, buf: &mut Vec<u32>) { buf.extend(&self.0); @@ -175,8 +211,8 @@ impl<'a> Value<'a> for &'a [u8] { } } impl ValueStore for &[u8] { - fn is_aligned(&self) -> bool { - false + fn get_type(&self) -> ValueType { + ValueType::Binary } fn store_unaligned(&self, buf: &mut Vec<u8>) { buf.extend(*self); @@ -192,8 +228,8 @@ impl<'a> Value<'a> for () { } } impl ValueStore for () { - fn is_aligned(&self) -> bool { - true + fn get_type(&self) -> ValueType { + ValueType::Binary } fn store_aligned(&self, _buf: &mut Vec<u32>) {} fn size(&self) -> usize { |