diff options
| author | metamuffin <metamuffin@disroot.org> | 2026-01-06 02:44:35 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2026-01-06 02:44:35 +0100 |
| commit | ce5ed3f54e873fff9135313a4ed9fa6656caf741 (patch) | |
| tree | 80e94a83d636308cb8f3758b18b96c3ded0edcf8 | |
| parent | d543f6fe11a32dcead2310f1fb4c2abd303f5f8c (diff) | |
| download | jellything-ce5ed3f54e873fff9135313a4ed9fa6656caf741.tar jellything-ce5ed3f54e873fff9135313a4ed9fa6656caf741.tar.bz2 jellything-ce5ed3f54e873fff9135313a4ed9fa6656caf741.tar.zst | |
draft object serialization
| -rw-r--r-- | Cargo.lock | 7 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | common/object/Cargo.toml | 7 | ||||
| -rw-r--r-- | common/object/src/lib.rs | 132 | ||||
| -rw-r--r-- | common/src/lib.rs | 29 |
5 files changed, 176 insertions, 0 deletions
@@ -1913,6 +1913,13 @@ dependencies = [ ] [[package]] +name = "jellyobject" +version = "0.1.0" +dependencies = [ + "bytemuck", +] + +[[package]] name = "jellyremuxer" version = "0.1.0" dependencies = [ @@ -14,6 +14,7 @@ members = [ "stream/types", "ui", "remuxer", + "common/object", ] resolver = "3" diff --git a/common/object/Cargo.toml b/common/object/Cargo.toml new file mode 100644 index 0000000..04dc228 --- /dev/null +++ b/common/object/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "jellyobject" +version = "0.1.0" +edition = "2024" + +[dependencies] +bytemuck = "1.24.0" 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 + } +} diff --git a/common/src/lib.rs b/common/src/lib.rs index 81d6d15..769cfe2 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -53,6 +53,35 @@ pub enum NodeIDOrSlug { Slug(String), } +macro_rules! keys { + ($($id:ident = $tag:literal $name:literal;)*) => { + $(const $id: Tag = Tag($tag);)* + }; +} + +keys! { + N_KIND = 1 "kind"; + N_TITLE = 2 "title"; + N_PARENT = 3 "parent"; + N_TAGLINE = 4 "tagline"; + N_DESCRIPTION = 5 "description"; + N_RELEASEDATE = 6 "releasedate"; + N_INDEX = 7 "index"; + N_SEASON_INDEX = 8 "season_index"; + N_MEDIA = 9 "media"; + N_TAG = 10 "tag"; + N_RATINGS = 11 "ratings"; + N_PICTURES = 12 "pictures"; + N_IDENTIFIERS = 13 "identifiers"; + N_VISIBILITY = 14 "visibility"; + N_STORAGE_SIZE = 15 "storage_size"; + + LANG_NATIVE = 0xa001 "native"; + LANG_ENG = 0xa002 "eng"; + LANG_DEU = 0xa003 "deu"; + LANG_JPN = 0xa003 "jpn"; +} + #[derive(Debug, Clone, Deserialize, Serialize, Default)] pub struct Node { pub slug: String, |