aboutsummaryrefslogtreecommitdiff
path: root/common/object/src/buffer.rs
blob: 05557e4de035d80746800be5615dfa0d2b9087bd (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
84
85
86
87
88
89
90
91
92
93
94
95
/*
    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, Tag, ValueStore};
use bytemuck::{try_cast_slice, try_cast_vec};
use log::trace;
use std::borrow::Cow;

#[derive(PartialEq, Eq, Hash)]
pub struct ObjectBuffer(pub Vec<u32>);

impl ObjectBuffer {
    pub fn empty() -> Self {
        Self(vec![0])
    }
    pub fn as_object<'a>(&'a self) -> Object<'a> {
        Object::load(&self.0).unwrap()
    }
    pub fn to_bytes(self) -> Vec<u8> {
        self.0.into_iter().flat_map(u32::to_le_bytes).collect()
    }
    pub fn new(fields: &mut [(Tag, &dyn ValueStore)]) -> ObjectBuffer {
        let mut tags = Vec::new();
        let mut offsets = Vec::new();
        let mut values = Vec::new();
        fields.sort_by_key(|(t, _)| t.0);
        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() {
                offsets.push(off);
                val.store_aligned(&mut values);
            } else {
                temp.clear();
                val.store_unaligned(&mut temp);
                let pad = pad_vec(&mut temp);
                offsets.push(off | pad);
                values.extend(&*slice_u8_to_u32(&temp));
            }
        }
        ObjectBuffer(
            [tags.len() as u32]
                .into_iter()
                .chain(tags)
                .chain(offsets)
                .chain(values)
                .collect(),
        )
    }
}

impl From<Vec<u8>> for ObjectBuffer {
    fn from(value: Vec<u8>) -> Self {
        ObjectBuffer(vec_u8_to_u32(value))
    }
}

#[inline]
pub(super) fn pad_vec(temp: &mut Vec<u8>) -> u32 {
    let mut pad = 0;
    while temp.len() % 4 != 0 {
        pad += 1;
        temp.push(0);
    }
    pad
}

pub fn vec_u8_to_u32(value: Vec<u8>) -> Vec<u32> {
    try_cast_vec(value).unwrap_or_else(|(_, v)| {
        trace!("encountered unalined vec");
        v.into_iter()
            .array_chunks()
            .map(u32::from_ne_bytes)
            .collect()
    })
}
pub fn slice_u8_to_u32<'a>(value: &'a [u8]) -> Cow<'a, [u32]> {
    try_cast_slice(value)
        .map(Cow::Borrowed)
        .unwrap_or_else(|_| {
            trace!("encountered unalined slice");
            Cow::Owned(
                value
                    .into_iter()
                    .copied()
                    .array_chunks()
                    .map(u32::from_ne_bytes)
                    .collect(),
            )
        })
}