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