aboutsummaryrefslogtreecommitdiff
path: root/common/object/src/lib.rs
blob: 90ddef5d5c828caa200ed75c1eada7d7c02d2975 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
    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>
*/

mod value;
pub use value::*;

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)?)
        }
    }
}