Skip to main content
KeyOS API Reference

server/
rkyv_utils.rs

1// SPDX-FileCopyrightText: 2025 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4//! rkyv serialization plumbing and `with` helpers shared across the crate.
5
6use core::cell::RefCell;
7use std::time::{Duration, SystemTime, UNIX_EPOCH};
8
9use rkyv::{
10    ser::{
11        allocator::{Arena, ArenaHandle},
12        writer::Buffer as RkyvBuffer,
13        Positional, Serializer, Writer,
14    },
15    with::{ArchiveWith, DeserializeWith, SerializeWith},
16    Archive, Archived, Deserialize, Place, Portable, Serialize,
17};
18
19pub type XousDeserializer = rkyv::rancor::Strategy<(), rkyv::rancor::Error>;
20pub type XousValidator<'a> = rkyv::api::low::LowValidator<'a, rkyv::rancor::Error>;
21pub type XousSerializer<'a, 'b> = rkyv::rancor::Strategy<
22    rkyv::ser::Serializer<rkyv::ser::writer::Buffer<'b>, ArenaHandle<'a>, ()>,
23    rkyv::rancor::Error,
24>;
25pub(crate) type SizeOfSerializer<'a> =
26    rkyv::rancor::Strategy<rkyv::ser::Serializer<SizeOfWriter, ArenaHandle<'a>, ()>, rkyv::rancor::Error>;
27
28thread_local! {
29    static ALLOC_ARENA: RefCell<Arena> = RefCell::new(Arena::new());
30}
31
32fn with_arena<R>(f: impl FnOnce(ArenaHandle<'_>) -> R) -> R {
33    ALLOC_ARENA.with_borrow_mut(|alloc| f(alloc.acquire()))
34}
35
36/// Serialized byte length of `src`, computed without writing it.
37pub(crate) fn serialized_size<T>(src: &T) -> core::result::Result<usize, rkyv::rancor::Error>
38where
39    T: for<'a> rkyv::Serialize<SizeOfSerializer<'a>>,
40{
41    with_arena(|arena| {
42        let mut serializer = Serializer::new(SizeOfWriter::new(), arena, ());
43        rkyv::api::serialize_using(src, &mut serializer)?;
44        Ok(serializer.pos())
45    })
46}
47
48/// Serialize `src` into `dst`, returning the number of bytes written.
49pub(crate) fn serialize_into<T>(dst: &mut [u8], src: &T) -> core::result::Result<usize, rkyv::rancor::Error>
50where
51    T: for<'a, 'b> rkyv::Serialize<XousSerializer<'a, 'b>>,
52{
53    with_arena(|arena| {
54        let mut serializer = Serializer::new(RkyvBuffer::from(dst), arena, ());
55        rkyv::api::serialize_using(src, &mut serializer)?;
56        Ok(serializer.pos())
57    })
58}
59
60/// Validate and deserialize archived `bytes` into an owned value.
61pub(crate) fn decode<T>(bytes: &[u8]) -> core::result::Result<T, rkyv::rancor::Error>
62where
63    T: rkyv::Archive,
64    T::Archived:
65        Portable + for<'a> rkyv::bytecheck::CheckBytes<XousValidator<'a>> + Deserialize<T, XousDeserializer>,
66{
67    let archived = rkyv::api::low::access::<T::Archived, rkyv::rancor::Error>(bytes)?;
68    rkyv::api::low::deserialize::<T, rkyv::rancor::Error>(archived)
69}
70
71/// A writer that only counts the size of the serialized data without actually writing it.
72/// Used to determine buffer size requirements before allocating memory.
73#[derive(Debug, Default)]
74pub struct SizeOfWriter {
75    pos: usize,
76}
77
78impl SizeOfWriter {
79    pub(crate) fn new() -> Self { Self { pos: 0 } }
80}
81
82impl Positional for SizeOfWriter {
83    fn pos(&self) -> usize { self.pos }
84}
85
86impl rkyv::rancor::Fallible for SizeOfWriter {
87    type Error = rkyv::rancor::Error;
88}
89
90impl Writer<rkyv::rancor::Error> for SizeOfWriter {
91    fn write(&mut self, bytes: &[u8]) -> core::result::Result<(), rkyv::rancor::Error> {
92        self.pos += bytes.len();
93        Ok(())
94    }
95}
96
97/// A custom UnixTimestamp implementation that doesn't have an error.
98/// making it compatible with infallible error types
99pub struct WithUnixTimestamp;
100
101impl ArchiveWith<SystemTime> for WithUnixTimestamp {
102    type Archived = Archived<Duration>;
103    type Resolver = <Duration as Archive>::Resolver;
104
105    #[inline]
106    fn resolve_with(field: &SystemTime, resolver: Self::Resolver, out: Place<Self::Archived>) {
107        let duration = field.duration_since(UNIX_EPOCH).unwrap_or_default();
108        Archive::resolve(&duration, resolver, out);
109    }
110}
111
112impl<S> SerializeWith<SystemTime, S> for WithUnixTimestamp
113where
114    S: rkyv::rancor::Fallible + ?Sized,
115{
116    fn serialize_with(field: &SystemTime, s: &mut S) -> Result<Self::Resolver, S::Error> {
117        let duration = field.duration_since(UNIX_EPOCH).unwrap_or_default();
118        duration.serialize(s)
119    }
120}
121
122impl<D> DeserializeWith<Archived<Duration>, SystemTime, D> for WithUnixTimestamp
123where
124    D: rkyv::rancor::Fallible + ?Sized,
125{
126    fn deserialize_with(field: &Archived<Duration>, _: &mut D) -> Result<SystemTime, D::Error> {
127        Ok(UNIX_EPOCH + Duration::from(*field))
128    }
129}
130
131pub struct WithAppId;
132
133impl ArchiveWith<xous::AppId> for WithAppId {
134    type Archived = Archived<[u32; 4]>;
135    type Resolver = <[u32; 4] as Archive>::Resolver;
136
137    #[inline]
138    fn resolve_with(field: &xous::AppId, resolver: Self::Resolver, out: Place<Self::Archived>) {
139        let words: [u32; 4] = field.into();
140        Archive::resolve(&words, resolver, out);
141    }
142}
143
144impl<S> SerializeWith<xous::AppId, S> for WithAppId
145where
146    S: rkyv::rancor::Fallible + ?Sized,
147{
148    fn serialize_with(field: &xous::AppId, s: &mut S) -> Result<Self::Resolver, S::Error> {
149        let words: [u32; 4] = field.into();
150        words.serialize(s)
151    }
152}
153
154impl<D> DeserializeWith<Archived<[u32; 4]>, xous::AppId, D> for WithAppId
155where
156    D: rkyv::rancor::Fallible + ?Sized,
157{
158    fn deserialize_with(field: &Archived<[u32; 4]>, d: &mut D) -> Result<xous::AppId, D::Error> {
159        let words = field.deserialize(d)?;
160        Ok(words.into())
161    }
162}