aboutsummaryrefslogtreecommitdiff
path: root/common/object
diff options
context:
space:
mode:
Diffstat (limited to 'common/object')
-rw-r--r--common/object/src/buffer.rs5
-rw-r--r--common/object/src/debug.rs59
-rw-r--r--common/object/src/inspect.rs59
-rw-r--r--common/object/src/json.rs34
-rw-r--r--common/object/src/lib.rs64
-rw-r--r--common/object/src/registry.rs42
-rw-r--r--common/object/src/tests.rs34
-rw-r--r--common/object/src/value.rs78
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 {