Skip to main content

harmos_domain/message/
mod.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10/* === Modules === */
11
12pub(crate) mod allocation;
13pub mod postcard;
14
15/* === Re-Exports === */
16
17// Codec Internals For This Crate; Generated Code Reaches Them Through `__private`.
18pub(crate) use allocation::{add as decoding_add, multiply as decoding_multiply, vector as decoding_vector};
19pub(crate) use postcard::{Reader as PostcardReader, Value as PostcardValue};
20
21/* === Imports === */
22
23// Standard Library Imports
24use std::borrow::Cow;
25use std::collections::BTreeMap;
26// Third Party Imports
27
28// Local Imports
29
30/* === Constants === */
31
32/* === Macros === */
33
34macro_rules! inline_memory {
35    ($($ty:ty),* $(,)?) => {$ (
36        impl MemorySize for $ty {
37            const INLINE_ONLY: bool = true;
38
39            fn memory_size(&self) -> usize {
40                std::mem::size_of::<Self>()
41            }
42        }
43    )*};
44}
45
46inline_memory!((), bool, u8, u32, u64, i32, i64, f32, f64);
47
48#[macro_export]
49macro_rules! postcard_product {
50    ($ty:ty { $($field:ident: $field_ty:ty),* $(,)? } $(, validate = $validate:expr)?) => {
51        #[allow(unused_variables)]
52        impl $crate::__private::PostcardValue for $ty {
53            fn write(&self, bytes: &mut ::std::vec::Vec<u8>) {
54                $(<$field_ty as $crate::__private::PostcardValue>::write(&self.$field, bytes);)*
55            }
56            fn size(&self) -> usize {
57                0usize$(.saturating_add(<$field_ty as $crate::__private::PostcardValue>::size(&self.$field)))*
58            }
59            fn memory(input: &mut $crate::__private::PostcardReader<'_>) -> ::std::result::Result<usize, $crate::MessageError> {
60                input.nested(|input| {
61                    let memory = std::mem::size_of::<Self>();
62                    $(let memory = $crate::__private::decoding_add(memory,
63                        <$field_ty as $crate::__private::PostcardValue>::memory(input)?.saturating_sub(std::mem::size_of::<$field_ty>()))?;)*
64                    Ok(memory)
65                })
66            }
67            fn read(input: &mut $crate::__private::PostcardReader<'_>) -> ::std::result::Result<Self, $crate::MessageError> {
68                input.nested(|input| {
69                    let value = Self { $($field: <$field_ty as $crate::__private::PostcardValue>::read(input)?),* };
70                    $(($validate)(&value)?;)?
71                    Ok(value)
72                })
73            }
74        }
75    };
76}
77pub(crate) use postcard_product;
78
79/* === Type Aliases === */
80
81/* === Structs === */
82
83#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
84#[error("invalid message: {0}")]
85pub struct MessageError(Cow<'static, str>);
86
87/* === Enums === */
88
89/* === Traits === */
90
91pub trait Message: Sized + Send + Sync {
92    fn encode_to_vec(&self) -> Vec<u8>;
93    fn encode_into(&self, bytes: &mut Vec<u8>) {
94        bytes.extend_from_slice(&self.encode_to_vec());
95    }
96    fn encoded_len(&self) -> usize;
97    fn decode(bytes: &[u8]) -> Result<Self, MessageError>;
98    fn decoding_memory(_bytes: &[u8]) -> Result<usize, MessageError> {
99        Err(MessageError::borrowed("decoding memory bound is unsupported"))
100    }
101}
102
103pub trait MemorySize: Sized {
104    /// True only when every value occupies exactly `size_of::<Self>()` bytes,
105    /// with no separately owned allocations or additional accounting charge.
106    const INLINE_ONLY: bool = false;
107
108    fn memory_size(&self) -> usize;
109}
110
111// A Value That Crosses A Process Boundary: Every Operation Request, Response
112// And Stream Item. Transport Codecs Derive From The Postcard Value And Its Shape.
113pub trait Portable: Message + MemorySize + crate::schema::Described + Send + Sync + 'static {}
114
115pub trait Storable: Message + MemorySize {
116    fn encoded_size(&self) -> usize {
117        self.encoded_len()
118    }
119}
120
121/* === Implementations === */
122
123impl<T> Portable for T where T: Message + MemorySize + crate::schema::Described + Send + Sync + 'static {}
124
125impl<T: PostcardValue> Message for T {
126    fn encode_to_vec(&self) -> Vec<u8> {
127        let mut bytes = Vec::with_capacity(self.size());
128        self.write(&mut bytes);
129        bytes
130    }
131    fn encode_into(&self, bytes: &mut Vec<u8>) {
132        self.write(bytes);
133    }
134    fn encoded_len(&self) -> usize {
135        self.size()
136    }
137    fn decoding_memory(bytes: &[u8]) -> Result<usize, MessageError> {
138        let mut input = PostcardReader::new(bytes);
139        let memory = Self::memory(&mut input)?;
140        input.finish()?;
141        Ok(memory)
142    }
143    fn decode(bytes: &[u8]) -> Result<Self, MessageError> {
144        <Self as Message>::decoding_memory(bytes)?;
145        let mut input = PostcardReader::new(bytes);
146        let value = Self::read(&mut input)?;
147        input.finish()?;
148        Ok(value)
149    }
150}
151
152impl<T: Message + MemorySize> Storable for T {}
153
154impl MemorySize for String {
155    fn memory_size(&self) -> usize {
156        std::mem::size_of::<Self>().saturating_add(self.capacity())
157    }
158}
159
160impl<T: MemorySize> MemorySize for Vec<T> {
161    fn memory_size(&self) -> usize {
162        let mut bytes = std::mem::size_of::<Self>().saturating_add(self.capacity().saturating_mul(std::mem::size_of::<T>()));
163        if T::INLINE_ONLY {
164            return bytes;
165        }
166
167        for value in self {
168            let owned = value.memory_size();
169            if owned == usize::MAX {
170                return usize::MAX;
171            }
172            bytes = bytes.saturating_add(owned.saturating_sub(std::mem::size_of::<T>()));
173        }
174        bytes
175    }
176}
177
178impl<T: MemorySize> MemorySize for Option<T> {
179    const INLINE_ONLY: bool = T::INLINE_ONLY;
180
181    fn memory_size(&self) -> usize {
182        let Some(value) = self else {
183            return std::mem::size_of::<Self>();
184        };
185        let owned = value.memory_size();
186        if owned == usize::MAX {
187            return usize::MAX;
188        }
189
190        std::mem::size_of::<Self>().saturating_add(owned.saturating_sub(std::mem::size_of::<T>()))
191    }
192}
193
194// The Heap Pointee Owns Its Full Resident Size Beside The Inline Pointer
195impl<T: MemorySize> MemorySize for Box<T> {
196    fn memory_size(&self) -> usize {
197        let owned = (**self).memory_size();
198        if owned == usize::MAX {
199            return usize::MAX;
200        }
201
202        std::mem::size_of::<Self>().saturating_add(owned)
203    }
204}
205
206impl<K: MemorySize, V: MemorySize> MemorySize for BTreeMap<K, V> {
207    fn memory_size(&self) -> usize {
208        let mut bytes = btree_map_memory::<K, V>(self.len());
209        for (key, value) in self {
210            let key_bytes = key.memory_size();
211            let value_bytes = value.memory_size();
212            if key_bytes == usize::MAX || value_bytes == usize::MAX {
213                return usize::MAX;
214            }
215            bytes = bytes
216                .saturating_add(key_bytes.saturating_sub(std::mem::size_of::<K>()))
217                .saturating_add(value_bytes.saturating_sub(std::mem::size_of::<V>()));
218        }
219        bytes
220    }
221}
222
223impl MessageError {
224    pub fn new(reason: impl Into<String>) -> Self {
225        Self(Cow::Owned(reason.into()))
226    }
227
228    pub const fn borrowed(reason: &'static str) -> Self {
229        Self(Cow::Borrowed(reason))
230    }
231}
232
233/* === Public Functions === */
234
235pub fn btree_map_memory<K, V>(entries: usize) -> usize {
236    let alignment = std::mem::align_of::<K>().max(std::mem::align_of::<V>()).max(std::mem::align_of::<usize>());
237    let node = std::mem::size_of::<K>()
238        .saturating_add(std::mem::size_of::<V>())
239        .saturating_mul(11)
240        .saturating_add(13usize.saturating_mul(std::mem::size_of::<usize>()))
241        .saturating_add(2 * std::mem::size_of::<u16>())
242        .saturating_add(5usize.saturating_mul(alignment));
243    std::mem::size_of::<BTreeMap<K, V>>().saturating_add(entries.saturating_add(1).saturating_mul(node))
244}
245
246/* === Private Functions === */
247
248/* === Tests === */
249
250#[cfg(test)]
251mod tests {
252    use super::MemorySize;
253
254    #[test]
255    fn vector_allocations() {
256        use std::sync::atomic::{AtomicUsize, Ordering};
257
258        static VISITS: AtomicUsize = AtomicUsize::new(0);
259        struct Inline(u8);
260        impl MemorySize for Inline {
261            const INLINE_ONLY: bool = true;
262
263            fn memory_size(&self) -> usize {
264                VISITS.fetch_add(1, Ordering::Relaxed);
265                std::mem::size_of_val(&self.0)
266            }
267        }
268
269        let mut values = Vec::with_capacity(128);
270        values.push(Some(Inline(7)));
271        let allocation = values.capacity() * std::mem::size_of::<Option<Inline>>();
272        assert_eq!(values.memory_size(), std::mem::size_of_val(&values) + allocation);
273        assert_eq!(VISITS.load(Ordering::Relaxed), 0);
274
275        let mut text = String::with_capacity(256);
276        text.push_str("nested");
277        let text_allocation = text.capacity();
278        let mut nested = Vec::with_capacity(8);
279        nested.push(Some(text));
280        let allocation = nested.capacity() * std::mem::size_of::<Option<String>>();
281        let expected = std::mem::size_of_val(&nested) + allocation + text_allocation;
282        assert_eq!(nested.memory_size(), expected);
283    }
284
285    #[test]
286    fn wrappers_saturate() {
287        struct Overflow;
288        impl MemorySize for Overflow {
289            fn memory_size(&self) -> usize {
290                usize::MAX
291            }
292        }
293
294        assert_eq!(Some(Overflow).memory_size(), usize::MAX);
295        assert_eq!(vec![Overflow].memory_size(), usize::MAX);
296        assert_eq!(Box::new(Overflow).memory_size(), usize::MAX);
297        assert_eq!(None::<Overflow>.memory_size(), std::mem::size_of::<Option<Overflow>>());
298    }
299}