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