aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--common/object/Cargo.toml3
-rw-r--r--common/object/src/buffer.rs12
-rw-r--r--common/object/src/lib.rs5
-rw-r--r--common/object/src/registry.rs27
-rw-r--r--common/object/src/tests.rs12
-rw-r--r--common/object/src/value.rs34
-rw-r--r--common/src/lib.rs27
-rw-r--r--common/src/routes.rs6
-rw-r--r--database/src/indices/key.rs26
-rw-r--r--database/src/indices/mod.rs11
-rw-r--r--database/src/indices/order.rs21
-rw-r--r--database/src/table.rs24
13 files changed, 158 insertions, 51 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 253ad09..e8f688a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1888,6 +1888,7 @@ name = "jellyobject"
version = "0.1.0"
dependencies = [
"bytemuck",
+ "log",
]
[[package]]
diff --git a/common/object/Cargo.toml b/common/object/Cargo.toml
index 04dc228..a5825ee 100644
--- a/common/object/Cargo.toml
+++ b/common/object/Cargo.toml
@@ -4,4 +4,5 @@ version = "0.1.0"
edition = "2024"
[dependencies]
-bytemuck = "1.24.0"
+bytemuck = { version = "1.24.0", features = ["extern_crate_std"] }
+log = "0.4.29"
diff --git a/common/object/src/buffer.rs b/common/object/src/buffer.rs
index 56b8caf..dbde833 100644
--- a/common/object/src/buffer.rs
+++ b/common/object/src/buffer.rs
@@ -5,6 +5,7 @@
*/
use crate::{Object, Tag, ValueStore};
+use bytemuck::try_cast_vec;
pub struct ObjectBuffer(pub Vec<u32>);
@@ -48,3 +49,14 @@ impl ObjectBuffer {
)
}
}
+
+impl From<Vec<u8>> for ObjectBuffer {
+ fn from(value: Vec<u8>) -> Self {
+ ObjectBuffer(try_cast_vec(value).unwrap_or_else(|(_, v)| {
+ v.into_iter()
+ .array_chunks()
+ .map(u32::from_ne_bytes)
+ .collect()
+ }))
+ }
+}
diff --git a/common/object/src/lib.rs b/common/object/src/lib.rs
index 831dee7..522b6c1 100644
--- a/common/object/src/lib.rs
+++ b/common/object/src/lib.rs
@@ -3,12 +3,15 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
+#![feature(iter_array_chunks)]
mod buffer;
+mod registry;
#[cfg(test)]
mod tests;
mod value;
pub use buffer::*;
+pub use registry::*;
pub use value::*;
use std::marker::PhantomData;
@@ -18,7 +21,7 @@ use std::marker::PhantomData;
pub struct Tag(pub u32);
pub struct TypedTag<T>(pub Tag, pub PhantomData<T>);
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
pub struct Object<'a> {
tags: &'a [u32],
offsets: &'a [u32],
diff --git a/common/object/src/registry.rs b/common/object/src/registry.rs
new file mode 100644
index 0000000..7148efd
--- /dev/null
+++ b/common/object/src/registry.rs
@@ -0,0 +1,27 @@
+/*
+ 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::Tag;
+use log::error;
+use std::{any::TypeId, collections::BTreeMap};
+
+#[derive(Default)]
+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) {
+ error!("Tag conflict: {:?} vs {:?}", info.name, other.name)
+ }
+ self.tags.insert(tag, info);
+ }
+}
+
+pub struct TagInfo {
+ pub name: &'static str,
+ pub r#type: Option<TypeId>,
+}
diff --git a/common/object/src/tests.rs b/common/object/src/tests.rs
index 35a29ba..143782c 100644
--- a/common/object/src/tests.rs
+++ b/common/object/src/tests.rs
@@ -37,3 +37,15 @@ fn read_multi_field() {
assert_eq!(friends.next(), Some("Charlie"));
assert_eq!(friends.next(), None);
}
+
+#[test]
+fn vec_align_test() {
+ let mut c = 0;
+ for _ in 0..10_000 {
+ let x = Vec::<u8>::with_capacity(16);
+ if x.as_ptr().align_offset(4) == 0 {
+ c += 1;
+ }
+ }
+ assert_eq!(c, 10_000, "correctly aligned vecs by system allocator")
+}
diff --git a/common/object/src/value.rs b/common/object/src/value.rs
index d77d53a..1b24e79 100644
--- a/common/object/src/value.rs
+++ b/common/object/src/value.rs
@@ -107,3 +107,37 @@ impl ValueStore for ObjectBuffer {
self.0.len() * 4
}
}
+impl<'a> Value<'a> for &'a [u8] {
+ const ALIGNED: bool = false;
+ fn load_unaligned(buf: &'a [u8]) -> Option<Self> {
+ Some(buf)
+ }
+}
+impl ValueStore for &[u8] {
+ fn is_aligned(&self) -> bool {
+ false
+ }
+ fn store_unaligned(&self, buf: &mut Vec<u8>) {
+ buf.extend(*self);
+ }
+ fn size(&self) -> usize {
+ self.len()
+ }
+}
+impl<'a> Value<'a> for &'a [u32] {
+ const ALIGNED: bool = true;
+ fn load_aligned(buf: &'a [u32]) -> Option<Self> {
+ Some(buf)
+ }
+}
+impl ValueStore for &[u32] {
+ fn is_aligned(&self) -> bool {
+ true
+ }
+ fn store_aligned(&self, buf: &mut Vec<u32>) {
+ buf.extend(*self);
+ }
+ fn size(&self) -> usize {
+ self.len() * 4
+ }
+}
diff --git a/common/src/lib.rs b/common/src/lib.rs
index 9ab0b91..1359c73 100644
--- a/common/src/lib.rs
+++ b/common/src/lib.rs
@@ -4,26 +4,39 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
#![feature(array_try_map)]
-pub mod api;
pub mod routes;
-pub mod user;
-use jellyobject::{Object, Tag, TypedTag};
+use jellyobject::{Object, Registry, Tag, TagInfo, TypedTag};
pub use jellystream_types as stream;
-use std::marker::PhantomData;
+use std::{any::TypeId, marker::PhantomData, sync::LazyLock};
+
+pub use jellyobject;
+
+pub static TAGREG: LazyLock<Registry> = LazyLock::new(|| {
+ let mut reg = Registry::default();
+ register_fields(&mut reg);
+ register_enums(&mut reg);
+ reg
+});
macro_rules! fields {
($($id:ident: $type:ty = $tag:literal $name:literal;)*) => {
$(pub const $id: TypedTag<$type> = TypedTag(Tag($tag), PhantomData);)*
+ fn register_fields(reg: &mut Registry) {
+ $(reg.add(Tag($tag), TagInfo { name: $name, r#type: Some(TypeId::of::<$type>()) });)*
+ }
};
}
macro_rules! enums {
($($id:ident = $tag:literal $name:literal;)*) => {
$(pub const $id: Tag = Tag($tag);)*
+ fn register_enums(reg: &mut Registry) {
+ $(reg.add(Tag($tag), TagInfo { name: $name, r#type: None });)*
+ }
};
}
fields! {
- // Tag counter: 32
+ // Tag counter: 36
NO_KIND: Tag = 1 "kind";
NO_TITLE: &str = 2 "title";
@@ -44,8 +57,8 @@ fields! {
NO_CREDIT: Object = 33 "credit"; // multi
CR_NODE: u64 = 34 "node";
- CR_KIND: Tag = 34 "kind";
- CR_JOB: &str = 34 "node";
+ CR_KIND: Tag = 35 "kind";
+ CR_ROLE: &str = 36 "role"; // multi
TR_KIND: Tag = 16 "kind";
TR_SOURCE: &str = 17 "source";
diff --git a/common/src/routes.rs b/common/src/routes.rs
index b11a622..48f975a 100644
--- a/common/src/routes.rs
+++ b/common/src/routes.rs
@@ -3,8 +3,6 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::api::NodeFilterSort;
-
pub fn u_home() -> String {
"/home".to_owned()
}
@@ -23,7 +21,7 @@ pub fn u_node_slug_player_time(node: &str, time: f64) -> String {
pub fn u_node_image(node: &str, slot: &str, size: usize) -> String {
format!("/n/{node}/image/{slot}?size={size}")
}
-pub fn u_node_slug_watched(node: &str, state: ApiWatchedState) -> String {
+pub fn u_node_slug_watched(node: &str, state: &str) -> String {
format!("/n/{node}/watched?state={state}")
}
pub fn u_node_slug_person_asset(node: &str, group: &str, index: usize, size: usize) -> String {
@@ -41,7 +39,7 @@ pub fn u_node_slug_progress(node: &str, time: f64) -> String {
pub fn u_items() -> String {
"/items".to_string()
}
-pub fn u_items_filter(page: usize, _filter: &NodeFilterSort) -> String {
+pub fn u_items_filter(page: usize) -> String {
// TODO
format!("/items?page={page}")
}
diff --git a/database/src/indices/key.rs b/database/src/indices/key.rs
index ab38d71..2790220 100644
--- a/database/src/indices/key.rs
+++ b/database/src/indices/key.rs
@@ -8,18 +8,18 @@ use crate::{
backends::{ReadTransaction, WriteTransaction},
indices::Index,
prefix_iterator::PrefixIterator,
- table::{RowNum, Table, TableNum},
+ table::{RowNum, TableNum},
};
use anyhow::Result;
+use jellycommon::jellyobject::{Object, Tag};
-pub struct KeyIndex<T> {
+pub struct KeyIndex {
id: TableNum,
- key: fn(&T) -> &[u8],
+ key: Vec<Tag>,
}
-impl<T: 'static> KeyIndex<T> {
- pub fn new(table: &mut Table<T>, id: TableNum, key: fn(&T) -> &[u8]) -> Self {
- table.indices.push(Box::new(Self { id, key }));
+impl KeyIndex {
+ pub fn new(id: TableNum, key: Vec<Tag>) -> Self {
Self { id, key }
}
pub fn key(&self, id: RowNum, key: &[u8]) -> Vec<u8> {
@@ -43,15 +43,15 @@ impl<T: 'static> KeyIndex<T> {
})
}
}
-impl<T: 'static> Index<T> for KeyIndex<T> {
- fn add(&self, db: &mut dyn WriteTransaction, id: RowNum, val: &T) -> Result<()> {
- db.set(&self.key(id, (self.key)(val)), &[])
+impl Index for KeyIndex {
+ fn add(&self, db: &mut dyn WriteTransaction, id: RowNum, val: Object) -> Result<()> {
+ // db.set(&self.key(id, (self.key)(val)), &[])
}
- fn remove(&self, db: &mut dyn WriteTransaction, id: RowNum, val: &T) -> Result<()> {
- db.del(&self.key(id, (self.key)(val)))
+ fn remove(&self, db: &mut dyn WriteTransaction, id: RowNum, val: Object) -> Result<()> {
+ // db.del(&self.key(id, (self.key)(val)))
}
- fn compare(&self, before: &T, after: &T) -> bool {
- (self.key)(before) == (self.key)(after)
+ fn compare(&self, before: Object, after: Object) -> bool {
+ // (self.key)(before) == (self.key)(after)
}
}
diff --git a/database/src/indices/mod.rs b/database/src/indices/mod.rs
index ab37589..ad3d00f 100644
--- a/database/src/indices/mod.rs
+++ b/database/src/indices/mod.rs
@@ -6,14 +6,15 @@
use crate::{backends::WriteTransaction, table::RowNum};
use anyhow::Result;
+use jellycommon::jellyobject::Object;
-pub mod order;
pub mod key;
+pub mod order;
-pub trait Index<T> {
- fn add(&self, db: &mut dyn WriteTransaction, row: RowNum, val: &T) -> Result<()>;
- fn remove(&self, db: &mut dyn WriteTransaction, row: RowNum, val: &T) -> Result<()>;
- fn compare(&self, before: &T, after: &T) -> bool {
+pub trait Index {
+ fn add(&self, db: &mut dyn WriteTransaction, row: RowNum, val: Object) -> Result<()>;
+ fn remove(&self, db: &mut dyn WriteTransaction, row: RowNum, val: Object) -> Result<()>;
+ fn compare(&self, before: Object, after: Object) -> bool {
let _ = (before, after);
true
}
diff --git a/database/src/indices/order.rs b/database/src/indices/order.rs
index 9163681..911ec37 100644
--- a/database/src/indices/order.rs
+++ b/database/src/indices/order.rs
@@ -7,34 +7,35 @@
use crate::{
backends::WriteTransaction,
indices::Index,
- table::{RowNum, Table, TableNum},
+ table::{RowNum, TableNum},
};
use anyhow::Result;
use bytemuck::{NoUninit, bytes_of};
+use jellycommon::jellyobject::{Object, Tag};
-pub struct OrderIndex<T> {
+#[derive(Clone)]
+pub struct OrderIndex {
id: TableNum,
- value: fn(&T) -> [u8; 8],
+ key: Vec<Tag>,
}
#[repr(C)]
#[derive(NoUninit, Clone, Copy)]
struct Key(TableNum, [u8; 8], RowNum);
-impl<T: 'static> OrderIndex<T> {
- pub fn new(table: &mut Table<T>, id: TableNum, value: fn(&T) -> [u8; 8]) -> Self {
- table.indices.push(Box::new(Self { id, value }));
+impl OrderIndex {
+ pub fn new(id: TableNum, value: fn(Object) -> [u8; 8]) -> Self {
Self { id, value }
}
}
-impl<T: 'static> Index<T> for OrderIndex<T> {
- fn add(&self, db: &mut dyn WriteTransaction, id: RowNum, val: &T) -> Result<()> {
+impl Index for OrderIndex {
+ fn add(&self, db: &mut dyn WriteTransaction, id: RowNum, val: Object) -> Result<()> {
db.set(bytes_of(&Key(self.id, (self.value)(val), id)), &[])
}
- fn remove(&self, db: &mut dyn WriteTransaction, id: RowNum, val: &T) -> Result<()> {
+ fn remove(&self, db: &mut dyn WriteTransaction, id: RowNum, val: Object) -> Result<()> {
db.del(bytes_of(&Key(self.id, (self.value)(val), id)))
}
- fn compare(&self, before: &T, after: &T) -> bool {
+ fn compare(&self, before: Object, after: Object) -> bool {
(self.value)(before) == (self.value)(after)
}
}
diff --git a/database/src/table.rs b/database/src/table.rs
index ba95f8e..42e03a3 100644
--- a/database/src/table.rs
+++ b/database/src/table.rs
@@ -4,21 +4,23 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
+use std::sync::Arc;
+
use crate::{
backends::{ReadTransaction, WriteTransaction},
indices::Index,
};
use anyhow::Result;
-use serde::{Serialize, de::DeserializeOwned};
+use jellycommon::jellyobject::{Object, ObjectBuffer};
pub type TableNum = u64;
pub type RowNum = u64;
-pub struct Table<T> {
+pub struct Table {
id: u32,
- pub(crate) indices: Vec<Box<dyn Index<T>>>,
+ pub(crate) indices: Vec<Box<dyn Index>>,
}
-impl<T: Serialize + DeserializeOwned> Table<T> {
+impl Table {
pub fn new(id: u32) -> Self {
Self {
id,
@@ -49,18 +51,20 @@ impl<T: Serialize + DeserializeOwned> Table<T> {
Ok(id_counter)
}
- pub fn get(&self, db: &dyn ReadTransaction, row: RowNum) -> Result<Option<T>> {
- Ok(db
- .get(&self.key(row))?
- .map(|v| serde_json::from_slice(&v))
- .transpose()?)
+ pub fn add_index<T: Index + Clone + 'static>(&mut self, index: T) -> T {
+ self.indices.push(Box::new(index.clone()));
+ index
+ }
+ pub fn get(&self, db: &dyn ReadTransaction, row: RowNum) -> Result<Option<ObjectBuffer>> {
+ Ok(db.get(&self.key(row))?.map(ObjectBuffer::from))
}
pub fn remove(&self, db: &mut dyn WriteTransaction, row: RowNum) -> Result<bool> {
let Some(entry) = self.get(db, row)? else {
return Ok(false);
};
+ let ob = entry.as_object();
for idx in &self.indices {
- idx.remove(db, row, &entry)?;
+ idx.remove(db, row, ob)?;
}
db.del(&self.key(row))?;
Ok(true)