Skip to main content

harmos_domain/value/
codec.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10/* === Modules === */
11
12/* === Re-Exports === */
13
14/* === Imports === */
15
16// Standard Library Imports
17use std::borrow::Cow;
18use std::collections::BTreeMap;
19// Third Party Imports
20use time::OffsetDateTime;
21// Local Imports
22use super::Value;
23use crate::message::{PostcardReader as Reader, PostcardValue, decoding_add, decoding_multiply};
24use crate::schema::declaration::{Definition, Field, Kind};
25use crate::schema::{Cardinality, Described};
26use crate::{MessageError, btree_map_memory};
27
28/* === Constants === */
29
30pub const VALUE_DEPTH: usize = 4;
31
32// Postcard Variant Ordinals Are Part Of The Value Contract
33const NULL: u32 = 0;
34const BOOL: u32 = 1;
35const INTEGER: u32 = 2;
36const UNSIGNED: u32 = 3;
37const NUMBER: u32 = 4;
38const TEXT: u32 = 5;
39const DATETIME: u32 = 6;
40const ARRAY: u32 = 7;
41const OBJECT: u32 = 8;
42
43pub(crate) const TIMESTAMP_DEFINITION: Definition = Timestamp::DEFINITION;
44pub(crate) const VALUE_FIELDS: &[Field] = Value4::FIELDS;
45pub(crate) const ENTRY_DEFINITION: Definition = Entry4::DEFINITION;
46
47/* === Macros === */
48
49macro_rules! shape {
50    ($name:ident, $fields:expr) => {
51        pub(crate) struct $name;
52        impl Described for $name {
53            const NAME: &'static str = concat!(module_path!(), "::", stringify!($name));
54            const FIELDS: &'static [Field] = $fields;
55            const MARKER: &'static [u8] = &crate::schema::declaration::encode::<
56                { crate::schema::declaration::encoded_len(Self::NAME, Self::FIELDS) },
57            >(Self::NAME, Self::FIELDS);
58        }
59    };
60}
61
62macro_rules! level {
63    ($level:ident, $entry:ident, $array:ident, $object:ident, $inner:ident) => {
64        shape!($array, &[field("items", 1, message($inner::DEFINITION), Cardinality::Repeated)]);
65        shape!(
66            $entry,
67            &[field("key", 1, Kind::String, Cardinality::Required), field("value", 2, message($inner::DEFINITION), Cardinality::Required),]
68        );
69        shape!($object, &[field("entries", 1, message($entry::DEFINITION), Cardinality::Repeated)]);
70        shape!(
71            $level,
72            &[
73                field("null", 1, message(<()>::DEFINITION), Cardinality::Variant),
74                field("bool", 2, message(<bool>::DEFINITION), Cardinality::Variant),
75                field("integer", 3, message(<i64>::DEFINITION), Cardinality::Variant),
76                field("unsigned", 4, message(<u64>::DEFINITION), Cardinality::Variant),
77                field("number", 5, message(<f64>::DEFINITION), Cardinality::Variant),
78                field("text", 6, message(String::DEFINITION), Cardinality::Variant),
79                field("datetime", 7, message(Timestamp::DEFINITION), Cardinality::Variant),
80                field("array", 8, message($array::DEFINITION), Cardinality::Variant),
81                field("object", 9, message($object::DEFINITION), Cardinality::Variant),
82            ]
83        );
84    };
85}
86
87shape!(Timestamp, &[field("seconds", 1, Kind::I64, Cardinality::Required), field("nanos", 2, Kind::U32, Cardinality::Required),]);
88shape!(
89    Value0,
90    &[
91        field("null", 1, message(<()>::DEFINITION), Cardinality::Variant),
92        field("bool", 2, message(<bool as Described>::DEFINITION), Cardinality::Variant,),
93        field("integer", 3, message(<i64 as Described>::DEFINITION), Cardinality::Variant,),
94        field("unsigned", 4, message(<u64 as Described>::DEFINITION), Cardinality::Variant,),
95        field("number", 5, message(<f64 as Described>::DEFINITION), Cardinality::Variant,),
96        field("text", 6, message(<String as Described>::DEFINITION), Cardinality::Variant,),
97        field("datetime", 7, message(Timestamp::DEFINITION), Cardinality::Variant,),
98    ]
99);
100level!(Value1, Entry1, Array1, Object1, Value0);
101level!(Value2, Entry2, Array2, Object2, Value1);
102level!(Value3, Entry3, Array3, Object3, Value2);
103level!(Value4, Entry4, Array4, Object4, Value3);
104
105/* === Type Aliases === */
106
107/* === Structs === */
108
109/* === Enums === */
110
111/* === Traits === */
112
113/* === Implementations === */
114
115impl Described for Value {
116    const NAME: &'static str = "harmos::Value";
117    const FIELDS: &'static [Field] = VALUE_FIELDS;
118    const MARKER: &'static [u8] = &crate::schema::declaration::encode::<
119        { crate::schema::declaration::encoded_len(Self::NAME, Self::FIELDS) },
120    >(Self::NAME, Self::FIELDS);
121}
122
123impl PostcardValue for Value {
124    fn write(&self, bytes: &mut Vec<u8>) {
125        match self {
126            Self::Null => NULL.write(bytes),
127            Self::Bool(value) => {
128                BOOL.write(bytes);
129                value.write(bytes);
130            }
131            Self::Integer(value) => {
132                INTEGER.write(bytes);
133                value.write(bytes);
134            }
135            Self::Unsigned(value) => {
136                UNSIGNED.write(bytes);
137                value.write(bytes);
138            }
139            Self::Number(value) => {
140                NUMBER.write(bytes);
141                value.write(bytes);
142            }
143            Self::Text(value) => {
144                TEXT.write(bytes);
145                value.write(bytes);
146            }
147            Self::Datetime(value) => {
148                DATETIME.write(bytes);
149                write_timestamp(*value, bytes);
150            }
151            Self::Array(values) => {
152                ARRAY.write(bytes);
153                values.len().write(bytes);
154                for value in values {
155                    value.write(bytes);
156                }
157            }
158            Self::Object(entries) => {
159                OBJECT.write(bytes);
160                write_entries(entries, bytes);
161            }
162        }
163    }
164
165    fn size(&self) -> usize {
166        let payload = match self {
167            Self::Null => 0,
168            Self::Bool(value) => value.size(),
169            Self::Integer(value) => value.size(),
170            Self::Unsigned(value) => value.size(),
171            Self::Number(value) => value.size(),
172            Self::Text(value) => value.size(),
173            Self::Datetime(value) => timestamp_size(*value),
174            Self::Array(values) => values.iter().fold(values.len().size(), |size, value| size.saturating_add(value.size())),
175            Self::Object(entries) => entries_size(entries),
176        };
177        variant(self).size().saturating_add(payload)
178    }
179
180    fn memory(input: &mut Reader<'_>) -> Result<usize, MessageError> {
181        memory_at(input, VALUE_DEPTH)
182    }
183
184    fn read(input: &mut Reader<'_>) -> Result<Self, MessageError> {
185        read_at(input, VALUE_DEPTH)
186    }
187}
188
189/* === Public Functions === */
190
191/* === Private Functions === */
192
193const fn field(name: &'static str, tag: u32, kind: Kind, cardinality: Cardinality) -> Field {
194    Field {
195        name: Cow::Borrowed(name),
196        tag,
197        kind,
198        cardinality,
199    }
200}
201
202const fn message(definition: Definition) -> Kind {
203    Kind::Message { definition }
204}
205
206fn variant(value: &Value) -> u32 {
207    match value {
208        Value::Null => NULL,
209        Value::Bool(_) => BOOL,
210        Value::Integer(_) => INTEGER,
211        Value::Unsigned(_) => UNSIGNED,
212        Value::Number(_) => NUMBER,
213        Value::Text(_) => TEXT,
214        Value::Datetime(_) => DATETIME,
215        Value::Array(_) => ARRAY,
216        Value::Object(_) => OBJECT,
217    }
218}
219
220fn depth_error() -> MessageError {
221    MessageError::borrowed("value nests deeper than its portable depth")
222}
223
224pub(crate) fn timestamp_parts(value: OffsetDateTime) -> (i64, u32) {
225    (value.unix_timestamp(), value.nanosecond())
226}
227
228pub(crate) fn from_parts(seconds: i64, nanos: u64) -> Result<OffsetDateTime, MessageError> {
229    let nanos = u32::try_from(nanos)
230        .ok()
231        .filter(|nanos| *nanos < 1_000_000_000)
232        .ok_or_else(|| MessageError::borrowed("timestamp nanoseconds out of range"))?;
233    OffsetDateTime::from_unix_timestamp(seconds)
234        .ok()
235        .and_then(|value| value.replace_nanosecond(nanos).ok())
236        .ok_or_else(|| MessageError::borrowed("timestamp out of range"))
237}
238
239pub(crate) fn write_timestamp(value: OffsetDateTime, bytes: &mut Vec<u8>) {
240    let (seconds, nanos) = timestamp_parts(value);
241    seconds.write(bytes);
242    nanos.write(bytes);
243}
244
245pub(crate) fn timestamp_size(value: OffsetDateTime) -> usize {
246    let (seconds, nanos) = timestamp_parts(value);
247    seconds.size().saturating_add(nanos.size())
248}
249
250pub(crate) fn read_timestamp(input: &mut Reader<'_>) -> Result<OffsetDateTime, MessageError> {
251    let seconds = i64::read(input)?;
252    let nanos = u32::read(input)?;
253    from_parts(seconds, u64::from(nanos))
254}
255
256pub(crate) fn write_entries(entries: &BTreeMap<String, Value>, bytes: &mut Vec<u8>) {
257    entries.len().write(bytes);
258    for (key, value) in entries {
259        key.write(bytes);
260        value.write(bytes);
261    }
262}
263
264pub(crate) fn entries_size(entries: &BTreeMap<String, Value>) -> usize {
265    entries.iter().fold(entries.len().size(), |size, (key, value)| size.saturating_add(key.size()).saturating_add(value.size()))
266}
267
268pub(crate) fn entries_memory(input: &mut Reader<'_>, depth: usize) -> Result<usize, MessageError> {
269    input.nested(|input| {
270        let count: usize = input.value()?;
271        input.claim(count)?;
272        let mut owned = btree_map_memory::<String, Value>(count);
273        for _ in 0..count {
274            let key = String::memory(input)?.saturating_sub(std::mem::size_of::<String>());
275            let value = memory_at(input, depth)?.saturating_sub(std::mem::size_of::<Value>());
276            owned = decoding_add(owned, decoding_add(key, value)?)?;
277        }
278        Ok(owned)
279    })
280}
281
282pub(crate) fn read_entries(input: &mut Reader<'_>, depth: usize) -> Result<BTreeMap<String, Value>, MessageError> {
283    input.nested(|input| {
284        let count: usize = input.value()?;
285        input.claim(count)?;
286        let mut entries = BTreeMap::new();
287        for _ in 0..count {
288            let key = String::read(input)?;
289            let value = read_at(input, depth)?;
290            if entries.insert(key, value).is_some() {
291                return Err(MessageError::borrowed("duplicate value object key"));
292            }
293        }
294        Ok(entries)
295    })
296}
297
298fn memory_at(input: &mut Reader<'_>, depth: usize) -> Result<usize, MessageError> {
299    input.nested(|input| {
300        let owned = std::mem::size_of::<Value>();
301        let allocation = match u32::read(input)? {
302            NULL => 0,
303            BOOL => {
304                bool::read(input)?;
305                0
306            }
307            INTEGER => {
308                i64::read(input)?;
309                0
310            }
311            UNSIGNED => {
312                u64::read(input)?;
313                0
314            }
315            NUMBER => {
316                f64::read(input)?;
317                0
318            }
319            TEXT => String::memory(input)?.saturating_sub(std::mem::size_of::<String>()),
320            DATETIME => {
321                read_timestamp(input)?;
322                0
323            }
324            ARRAY => {
325                let depth = depth.checked_sub(1).ok_or_else(depth_error)?;
326                let count: usize = input.value()?;
327                input.claim(count)?;
328                let mut items = decoding_multiply(count, std::mem::size_of::<Value>())?;
329                for _ in 0..count {
330                    let item = memory_at(input, depth)?;
331                    items = decoding_add(items, item.saturating_sub(std::mem::size_of::<Value>()))?;
332                }
333                items
334            }
335            OBJECT => {
336                let depth = depth.checked_sub(1).ok_or_else(depth_error)?;
337                entries_memory(input, depth)?
338            }
339            _ => return Err(MessageError::borrowed("unknown value variant")),
340        };
341        decoding_add(owned, allocation)
342    })
343}
344
345fn read_at(input: &mut Reader<'_>, depth: usize) -> Result<Value, MessageError> {
346    input.nested(|input| {
347        Ok(match u32::read(input)? {
348            NULL => Value::Null,
349            BOOL => Value::Bool(bool::read(input)?),
350            INTEGER => Value::Integer(i64::read(input)?),
351            UNSIGNED => Value::Unsigned(u64::read(input)?),
352            NUMBER => Value::Number(f64::read(input)?),
353            TEXT => Value::Text(String::read(input)?),
354            DATETIME => Value::Datetime(read_timestamp(input)?),
355            ARRAY => {
356                let depth = depth.checked_sub(1).ok_or_else(depth_error)?;
357                let count: usize = input.value()?;
358                input.claim(count)?;
359                let mut items = crate::message::decoding_vector(count)?;
360                for _ in 0..count {
361                    items.push(read_at(input, depth)?);
362                }
363                Value::Array(items)
364            }
365            OBJECT => {
366                let depth = depth.checked_sub(1).ok_or_else(depth_error)?;
367                Value::Object(read_entries(input, depth)?)
368            }
369            _ => return Err(MessageError::borrowed("unknown value variant")),
370        })
371    })
372}
373
374/* === Tests === */