Skip to main content

harmos_domain/schema/graph/
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::mem::size_of;
19// Third Party Imports
20
21// Local Imports
22use super::{Field, Kind, Schema};
23use crate::config::SchemaSettings;
24use crate::message::{MessageError, PostcardReader, PostcardValue, decoding_vector};
25use crate::schema::declaration::{self, Definition};
26use crate::schema::{Cardinality, Described, MAX_NAME_BYTES};
27
28/* === Constants === */
29
30/* === Macros === */
31
32macro_rules! shape {
33    ($ty:ty, $name:literal, $fields:expr) => {
34        impl Described for $ty {
35            const NAME: &'static str = $name;
36            const FIELDS: &'static [Field<Definition>] = $fields;
37            const MARKER: &'static [u8] =
38                &declaration::encode::<{ declaration::encoded_len(Self::NAME, Self::FIELDS) }>(Self::NAME, Self::FIELDS);
39        }
40    };
41}
42
43shape!(
44    Schema,
45    "harmos::schema::Graph",
46    &[
47        described("root", 1, declaration::Kind::U64, Cardinality::Required),
48        described("definitions", 2, declaration::Kind::Message { definition: DefinitionShape::DEFINITION }, Cardinality::Repeated),
49    ]
50);
51shape!(
52    DefinitionShape,
53    "harmos::schema::Definition",
54    &[described("fields", 1, declaration::Kind::Message { definition: FieldShape::DEFINITION }, Cardinality::Repeated),]
55);
56shape!(
57    FieldShape,
58    "harmos::schema::Field",
59    &[
60        described("name", 1, declaration::Kind::String, Cardinality::Required),
61        described("tag", 2, declaration::Kind::U32, Cardinality::Required),
62        described("kind", 3, declaration::Kind::U32, Cardinality::Required),
63        described("cardinality", 4, declaration::Kind::U32, Cardinality::Required),
64        described("definition", 5, declaration::Kind::U64, Cardinality::Optional),
65    ]
66);
67
68/* === Type Aliases === */
69
70type Result<T> = std::result::Result<T, MessageError>;
71
72/* === Structs === */
73
74struct DefinitionShape;
75struct FieldShape;
76
77struct Node<'a> {
78    name: &'a str,
79    tag: u32,
80    kind: u32,
81    cardinality: u32,
82    definition: Option<usize>,
83}
84
85struct Budget {
86    bytes: usize,
87    work: usize,
88    settings: SchemaSettings,
89}
90
91/* === Enums === */
92
93/* === Traits === */
94
95/* === Implementations === */
96
97impl Budget {
98    fn new() -> Self {
99        Self {
100            bytes: size_of::<Schema>(),
101            work: 0,
102            settings: SchemaSettings::default(),
103        }
104    }
105
106    fn charge(&mut self, bytes: usize, work: usize) -> Result<()> {
107        self.bytes = self.bytes.checked_add(bytes).ok_or_else(invalid)?;
108        self.work = self.work.checked_add(work).ok_or_else(invalid)?;
109        if self.bytes > self.settings.preparation_bytes.get() || self.work > self.settings.preparation_work.get() {
110            return Err(invalid());
111        }
112        Ok(())
113    }
114
115    fn definition(&mut self, fields: usize) -> Result<()> {
116        let bits = usize::BITS as usize - fields.leading_zeros() as usize;
117        let work = fields.checked_mul(2 * bits + 3).ok_or_else(invalid)?;
118        self.charge(4 * size_of::<Vec<Field>>() + size_of::<usize>() + size_of::<bool>(), work + 1)
119    }
120
121    fn field(&mut self, node: &Node<'_>) -> Result<()> {
122        node.check()?;
123        self.charge(4 * size_of::<Field>() + 2 * node.name.len() + size_of::<&str>() + size_of::<u32>(), node.name.len() + 1)
124    }
125}
126
127impl Node<'_> {
128    fn check(&self) -> Result<()> {
129        if self.name.is_empty()
130            || self.name.len() > MAX_NAME_BYTES
131            || self.name.chars().any(char::is_control)
132            || self.tag == 0
133            || self.tag > 536_870_911
134            || (19_000..=19_999).contains(&self.tag)
135            || self.kind > 9
136            || self.cardinality > 3
137            || (self.kind == 9) != self.definition.is_some()
138        {
139            return Err(invalid());
140        }
141        Ok(())
142    }
143
144    fn owned(self) -> Result<Field> {
145        self.check()?;
146        let kind = match self.kind {
147            0 => Kind::Bool,
148            1 => Kind::U32,
149            2 => Kind::U64,
150            3 => Kind::I32,
151            4 => Kind::I64,
152            5 => Kind::F32,
153            6 => Kind::F64,
154            7 => Kind::String,
155            8 => Kind::Bytes,
156            9 => Kind::Message {
157                definition: self.definition.ok_or_else(invalid)?,
158            },
159            _ => return Err(invalid()),
160        };
161        let cardinality = match self.cardinality {
162            0 => Cardinality::Required,
163            1 => Cardinality::Optional,
164            2 => Cardinality::Repeated,
165            3 => Cardinality::Variant,
166            _ => return Err(invalid()),
167        };
168        Ok(Field {
169            name: self.name.to_owned().into(),
170            tag: self.tag,
171            kind,
172            cardinality,
173        })
174    }
175}
176
177impl<'a> From<&'a Field> for Node<'a> {
178    fn from(field: &'a Field) -> Self {
179        let (kind, definition) = match field.kind {
180            Kind::Bool => (0, None),
181            Kind::U32 => (1, None),
182            Kind::U64 => (2, None),
183            Kind::I32 => (3, None),
184            Kind::I64 => (4, None),
185            Kind::F32 => (5, None),
186            Kind::F64 => (6, None),
187            Kind::String => (7, None),
188            Kind::Bytes => (8, None),
189            Kind::Message { definition } => (9, Some(definition)),
190        };
191        let cardinality = match field.cardinality {
192            Cardinality::Required => 0,
193            Cardinality::Optional => 1,
194            Cardinality::Repeated => 2,
195            Cardinality::Variant => 3,
196        };
197        Self {
198            name: &field.name,
199            tag: field.tag,
200            kind,
201            cardinality,
202            definition,
203        }
204    }
205}
206
207impl PostcardValue for Schema {
208    fn write(&self, bytes: &mut Vec<u8>) {
209        self.root.write(bytes);
210        self.definitions.len().write(bytes);
211        for fields in &self.definitions {
212            fields.len().write(bytes);
213            for field in fields {
214                let node = Node::from(field);
215                node.name.len().write(bytes);
216                bytes.extend_from_slice(node.name.as_bytes());
217                node.tag.write(bytes);
218                node.kind.write(bytes);
219                node.cardinality.write(bytes);
220                match node.definition {
221                    None => 0u32.write(bytes),
222                    Some(definition) => {
223                        1u32.write(bytes);
224                        definition.write(bytes);
225                    }
226                }
227            }
228        }
229    }
230
231    fn size(&self) -> usize {
232        let mut size = self.root.size().saturating_add(self.definitions.len().size());
233        for fields in &self.definitions {
234            size = size.saturating_add(fields.len().size());
235            for field in fields {
236                let node = Node::from(field);
237                let reference = match node.definition {
238                    None => 1,
239                    Some(index) => 1usize.saturating_add(index.size()),
240                };
241                size = size
242                    .saturating_add(node.name.len().size())
243                    .saturating_add(node.name.len())
244                    .saturating_add(node.tag.size())
245                    .saturating_add(node.kind.size())
246                    .saturating_add(node.cardinality.size())
247                    .saturating_add(reference);
248            }
249        }
250        size
251    }
252
253    fn memory(input: &mut PostcardReader<'_>) -> Result<usize> {
254        input.nested(|input| {
255            let root = input.value::<usize>()?;
256            let count = input.value::<usize>()?;
257            if root >= count {
258                return Err(invalid());
259            }
260            input.claim(count)?;
261            let mut budget = Budget::new();
262            for _ in 0..count {
263                let fields = input.value::<usize>()?;
264                input.claim(fields)?;
265                budget.definition(fields)?;
266                for _ in 0..fields {
267                    let node = postcard_node(input)?;
268                    if node.definition.is_some_and(|reference| reference >= count) {
269                        return Err(invalid());
270                    }
271                    budget.field(&node)?;
272                }
273            }
274            Ok(budget.bytes)
275        })
276    }
277
278    fn read(input: &mut PostcardReader<'_>) -> Result<Self> {
279        Self::memory(&mut input.clone())?;
280        input.nested(|input| {
281            let root = input.value()?;
282            let count = input.value::<usize>()?;
283            input.claim(count)?;
284            let mut definitions = decoding_vector(count)?;
285            for _ in 0..count {
286                let count = input.value::<usize>()?;
287                input.claim(count)?;
288                let mut fields = decoding_vector(count)?;
289                for _ in 0..count {
290                    fields.push(postcard_node(input)?.owned()?);
291                }
292                definitions.push(fields);
293            }
294            let schema = Self { root, definitions };
295            schema.prepare(&SchemaSettings::default()).map_err(|_| invalid())?;
296            Ok(schema)
297        })
298    }
299}
300
301/* === Public Functions === */
302
303/* === Private Functions === */
304
305const fn described(name: &'static str, tag: u32, kind: declaration::Kind, cardinality: Cardinality) -> Field<Definition> {
306    Field {
307        name: Cow::Borrowed(name),
308        tag,
309        kind,
310        cardinality,
311    }
312}
313
314fn postcard_node<'a>(input: &mut PostcardReader<'a>) -> Result<Node<'a>> {
315    let length = input.value::<usize>()?;
316    if length > MAX_NAME_BYTES {
317        return Err(invalid());
318    }
319    let name = std::str::from_utf8(input.take(length)?).map_err(|_| invalid())?;
320    let tag = input.value()?;
321    let kind = input.value()?;
322    let cardinality = input.value()?;
323    let definition = match input.value::<u32>()? {
324        0 => None,
325        1 => Some(input.value()?),
326        _ => return Err(invalid()),
327    };
328    Ok(Node { name, tag, kind, cardinality, definition })
329}
330
331fn invalid() -> MessageError {
332    MessageError::borrowed("invalid finite schema graph or preparation allowance")
333}
334
335/* === Tests === */