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 { name: Cow::Borrowed(name), tag, kind, cardinality }
195}
196
197const fn message(definition: Definition) -> Kind {
198    Kind::Message { definition }
199}
200
201fn variant(value: &Value) -> u32 {
202    match value {
203        Value::Null => NULL,
204        Value::Bool(_) => BOOL,
205        Value::Integer(_) => INTEGER,
206        Value::Unsigned(_) => UNSIGNED,
207        Value::Number(_) => NUMBER,
208        Value::Text(_) => TEXT,
209        Value::Datetime(_) => DATETIME,
210        Value::Array(_) => ARRAY,
211        Value::Object(_) => OBJECT,
212    }
213}
214
215fn depth_error() -> MessageError {
216    MessageError::borrowed("value nests deeper than its portable depth")
217}
218
219pub(crate) fn timestamp_parts(value: OffsetDateTime) -> (i64, u32) {
220    (value.unix_timestamp(), value.nanosecond())
221}
222
223pub(crate) fn from_parts(seconds: i64, nanos: u64) -> Result<OffsetDateTime, MessageError> {
224    let nanos = u32::try_from(nanos)
225        .ok()
226        .filter(|nanos| *nanos < 1_000_000_000)
227        .ok_or_else(|| MessageError::borrowed("timestamp nanoseconds out of range"))?;
228    OffsetDateTime::from_unix_timestamp(seconds)
229        .ok()
230        .and_then(|value| value.replace_nanosecond(nanos).ok())
231        .ok_or_else(|| MessageError::borrowed("timestamp out of range"))
232}
233
234pub(crate) fn write_timestamp(value: OffsetDateTime, bytes: &mut Vec<u8>) {
235    let (seconds, nanos) = timestamp_parts(value);
236    seconds.write(bytes);
237    nanos.write(bytes);
238}
239
240pub(crate) fn timestamp_size(value: OffsetDateTime) -> usize {
241    let (seconds, nanos) = timestamp_parts(value);
242    seconds.size().saturating_add(nanos.size())
243}
244
245pub(crate) fn read_timestamp(input: &mut Reader<'_>) -> Result<OffsetDateTime, MessageError> {
246    let seconds = i64::read(input)?;
247    let nanos = u32::read(input)?;
248    from_parts(seconds, u64::from(nanos))
249}
250
251pub(crate) fn write_entries(entries: &BTreeMap<String, Value>, bytes: &mut Vec<u8>) {
252    entries.len().write(bytes);
253    for (key, value) in entries {
254        key.write(bytes);
255        value.write(bytes);
256    }
257}
258
259pub(crate) fn entries_size(entries: &BTreeMap<String, Value>) -> usize {
260    entries.iter().fold(entries.len().size(), |size, (key, value)| size.saturating_add(key.size()).saturating_add(value.size()))
261}
262
263pub(crate) fn entries_memory(input: &mut Reader<'_>, depth: usize) -> Result<usize, MessageError> {
264    input.nested(|input| {
265        let count: usize = input.value()?;
266        input.claim(count)?;
267        let mut owned = btree_map_memory::<String, Value>(count);
268        for _ in 0..count {
269            let key = String::memory(input)?.saturating_sub(std::mem::size_of::<String>());
270            let value = memory_at(input, depth)?.saturating_sub(std::mem::size_of::<Value>());
271            owned = decoding_add(owned, decoding_add(key, value)?)?;
272        }
273        Ok(owned)
274    })
275}
276
277pub(crate) fn read_entries(input: &mut Reader<'_>, depth: usize) -> Result<BTreeMap<String, Value>, MessageError> {
278    input.nested(|input| {
279        let count: usize = input.value()?;
280        input.claim(count)?;
281        let mut entries = BTreeMap::new();
282        for _ in 0..count {
283            let key = String::read(input)?;
284            let value = read_at(input, depth)?;
285            if entries.insert(key, value).is_some() {
286                return Err(MessageError::borrowed("duplicate value object key"));
287            }
288        }
289        Ok(entries)
290    })
291}
292
293fn memory_at(input: &mut Reader<'_>, depth: usize) -> Result<usize, MessageError> {
294    input.nested(|input| {
295        let owned = std::mem::size_of::<Value>();
296        let allocation = match u32::read(input)? {
297            NULL => 0,
298            BOOL => {
299                bool::read(input)?;
300                0
301            }
302            INTEGER => {
303                i64::read(input)?;
304                0
305            }
306            UNSIGNED => {
307                u64::read(input)?;
308                0
309            }
310            NUMBER => {
311                f64::read(input)?;
312                0
313            }
314            TEXT => String::memory(input)?.saturating_sub(std::mem::size_of::<String>()),
315            DATETIME => {
316                read_timestamp(input)?;
317                0
318            }
319            ARRAY => {
320                let depth = depth.checked_sub(1).ok_or_else(depth_error)?;
321                let count: usize = input.value()?;
322                input.claim(count)?;
323                let mut items = decoding_multiply(count, std::mem::size_of::<Value>())?;
324                for _ in 0..count {
325                    let item = memory_at(input, depth)?;
326                    items = decoding_add(items, item.saturating_sub(std::mem::size_of::<Value>()))?;
327                }
328                items
329            }
330            OBJECT => {
331                let depth = depth.checked_sub(1).ok_or_else(depth_error)?;
332                entries_memory(input, depth)?
333            }
334            _ => return Err(MessageError::borrowed("unknown value variant")),
335        };
336        decoding_add(owned, allocation)
337    })
338}
339
340fn read_at(input: &mut Reader<'_>, depth: usize) -> Result<Value, MessageError> {
341    input.nested(|input| {
342        Ok(match u32::read(input)? {
343            NULL => Value::Null,
344            BOOL => Value::Bool(bool::read(input)?),
345            INTEGER => Value::Integer(i64::read(input)?),
346            UNSIGNED => Value::Unsigned(u64::read(input)?),
347            NUMBER => Value::Number(f64::read(input)?),
348            TEXT => Value::Text(String::read(input)?),
349            DATETIME => Value::Datetime(read_timestamp(input)?),
350            ARRAY => {
351                let depth = depth.checked_sub(1).ok_or_else(depth_error)?;
352                let count: usize = input.value()?;
353                input.claim(count)?;
354                let mut items = crate::message::decoding_vector(count)?;
355                for _ in 0..count {
356                    items.push(read_at(input, depth)?);
357                }
358                Value::Array(items)
359            }
360            OBJECT => {
361                let depth = depth.checked_sub(1).ok_or_else(depth_error)?;
362                Value::Object(read_entries(input, depth)?)
363            }
364            _ => return Err(MessageError::borrowed("unknown value variant")),
365        })
366    })
367}
368
369/* === Tests === */