/* 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 */ use crate::{Object, Tag, TypedTag, ValueMarker, value::ValueSer}; use bytemuck::{try_cast_slice, try_cast_vec}; use core::marker::Sized; use log::trace; use std::borrow::Cow; pub type OBB = ObjectBufferBuilder; #[derive(Default)] pub struct ObjectBufferBuilder(Vec<(Tag, u32, Vec)>); impl ObjectBufferBuilder { pub fn new() -> Self { Self::default() } pub fn push<'a, T: ValueMarker<'a> + ?Sized>(&mut self, tag: TypedTag, value: T::Inner) { let tyb = (T::Inner::TYPE as u32) << 2; if T::Inner::TYPE.is_aligned() { let mut buf = Vec::new(); value.store_aligned(&mut buf); self.0.push((tag.0, tyb, buf)); } else { let mut buf = Vec::new(); value.store_unaligned(&mut buf); let pad = pad_vec(&mut buf); self.0.push((tag.0, tyb | pad, vec_u8_to_u32(buf))); } } pub fn with<'a, T: ValueMarker<'a> + ?Sized>( mut self, tag: TypedTag, value: T::Inner, ) -> Self { self.push(tag, value); self } pub fn finish(mut self) -> Box { let mut tags = Vec::new(); let mut offsets = Vec::new(); let mut values = Vec::new(); self.0.sort_by_key(|(t, _, _)| t.0); for (tag, mut off, buf) in self.0 { tags.push(tag.0); off |= (values.len() as u32) << 5; offsets.push(off); values.extend(buf); } vec_to_ob( [tags.len() as u32] .into_iter() .chain(tags) .chain(offsets) .chain(values) .collect(), ) } } pub fn vec_to_ob(v: Vec) -> Box { //? safe way to do this? unsafe { std::mem::transmute(v.into_boxed_slice()) } } pub const fn slice_to_ob(v: &[u32]) -> &Object { //? safe way to do this? unsafe { std::mem::transmute(v) } } #[inline] pub(super) fn pad_vec(temp: &mut Vec) -> u32 { let mut pad = 0; while !temp.len().is_multiple_of(4) { pad += 1; temp.push(0); } pad } pub fn vec_u8_to_u32(value: Vec) -> Vec { 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 .iter() .copied() .array_chunks() .map(u32::from_ne_bytes) .collect(), ) }) }