Skip to main content

harmos_domain/schema/
payload.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::sync::Arc;
18// Third Party Imports
19// Local Imports
20use super::{Cardinality, graph};
21use crate::MessageError;
22use crate::config::SchemaSettings;
23use crate::message::PostcardReader;
24
25/* === Constants === */
26
27/* === Macros === */
28
29/* === Type Aliases === */
30
31/* === Structs === */
32
33#[derive(Clone, Debug)]
34pub struct Validator {
35    root: usize,
36    definitions: Arc<[Box<[Checked]>]>,
37}
38#[derive(Debug)]
39struct Checked {
40    name: Box<str>,
41    tag: u32,
42    cardinality: Cardinality,
43    kind: Wire,
44}
45
46/* === Enums === */
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum PathElement<'schema> {
50    Field { name: &'schema str, tag: u32 },
51    Index(usize),
52}
53
54#[derive(Debug, Clone, Copy, PartialEq)]
55pub enum Scalar<'payload> {
56    Bool(bool),
57    U32(u32),
58    U64(u64),
59    I32(i32),
60    I64(i64),
61    F32(f32),
62    F64(f64),
63    String(&'payload str),
64    Bytes(&'payload [u8]),
65}
66
67#[derive(Debug, Clone, Copy, PartialEq)]
68pub enum Event<'payload> {
69    BeginMessage,
70    EndMessage,
71    Optional { present: bool },
72    BeginRepeated { length: usize },
73    EndRepeated,
74    Variant { index: u32 },
75    Scalar(Scalar<'payload>),
76}
77
78#[derive(Debug, thiserror::Error)]
79pub enum VisitError<E> {
80    #[error(transparent)]
81    Payload(#[from] PayloadError),
82
83    #[error("Postcard visitor failed: {0}")]
84    Consumer(#[source] E),
85}
86
87#[derive(Debug)]
88enum Wire {
89    Bool,
90    U32,
91    U64,
92    I32,
93    I64,
94    F32,
95    F64,
96    String,
97    Bytes,
98    Message(usize),
99}
100#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
101#[error(transparent)]
102pub struct PayloadError(#[from] MessageError);
103
104/* === Traits === */
105
106/* === Implementations === */
107
108impl Validator {
109    // Reuse The Checked Definitions For Another Root Without Recompiling Them.
110    pub fn at(&self, root: usize) -> Result<Self, graph::Error> {
111        if root >= self.definitions.len() {
112            return Err(graph::Error::Reference { definition: root });
113        }
114        Ok(Self { root, definitions: Arc::clone(&self.definitions) })
115    }
116    pub fn construction_memory_bytes(schema: &graph::Schema, settings: &SchemaSettings) -> Result<usize, graph::Error> {
117        schema
118            .preparation(settings)?
119            .bytes
120            .checked_add(graph_compilation_bytes(schema)?)
121            .and_then(|bytes| bytes.checked_add(2 * std::mem::size_of::<usize>()))
122            .and_then(|bytes| {
123                schema
124                    .definitions
125                    .len()
126                    .checked_mul(std::mem::size_of::<Box<[Checked]>>())
127                    .and_then(|temporary| bytes.checked_add(temporary))
128            })
129            .ok_or(graph::Error::Budget)
130    }
131    pub fn validate(&self, bytes: &[u8]) -> Result<(), PayloadError> {
132        match self.visit(bytes, |_, _| Ok::<(), std::convert::Infallible>(())) {
133            Ok(()) => Ok(()),
134            Err(VisitError::Payload(error)) => Err(error),
135            Err(VisitError::Consumer(error)) => match error {},
136        }
137    }
138
139    pub fn visit<'schema, 'payload, E>(
140        &'schema self, bytes: &'payload [u8], mut visitor: impl FnMut(&[PathElement<'schema>], Event<'payload>) -> Result<(), E>,
141    ) -> Result<(), VisitError<E>> {
142        let mut input = PostcardReader::new(bytes);
143        let mut path = Vec::new();
144        let mut failure = None;
145        let result = self.read(self.root, &mut input, &mut path, &mut |path, event| {
146            visitor(path, event).map_err(|error| {
147                failure = Some(error);
148                MessageError::borrowed("Postcard consumer failed")
149            })
150        });
151        if let Some(error) = failure {
152            return Err(VisitError::Consumer(error));
153        }
154        result.map_err(PayloadError::from)?;
155        input.finish().map_err(PayloadError::from)?;
156        Ok(())
157    }
158    pub fn from_schema(schema: &graph::Schema, settings: &SchemaSettings) -> Result<Self, graph::Error> {
159        schema.prepare(settings)?;
160        if Self::construction_memory_bytes(schema, settings)? > settings.preparation_bytes.get() {
161            return Err(graph::Error::Budget);
162        }
163        let definitions = schema
164            .definitions
165            .iter()
166            .map(|fields| {
167                fields
168                    .iter()
169                    .map(|field| Checked {
170                        name: field.name.as_ref().into(),
171                        tag: field.tag,
172                        cardinality: field.cardinality,
173                        kind: match field.kind {
174                            graph::Kind::Bool => Wire::Bool,
175                            graph::Kind::U32 => Wire::U32,
176                            graph::Kind::U64 => Wire::U64,
177                            graph::Kind::I32 => Wire::I32,
178                            graph::Kind::I64 => Wire::I64,
179                            graph::Kind::F32 => Wire::F32,
180                            graph::Kind::F64 => Wire::F64,
181                            graph::Kind::String => Wire::String,
182                            graph::Kind::Bytes => Wire::Bytes,
183                            graph::Kind::Message { definition } => Wire::Message(definition),
184                        },
185                    })
186                    .collect::<Box<[_]>>()
187            })
188            .collect::<Box<[_]>>();
189        Ok(Self { root: schema.root, definitions: definitions.into() })
190    }
191    fn read<'schema, 'payload>(
192        &'schema self, definition: usize, input: &mut PostcardReader<'payload>, path: &mut Vec<PathElement<'schema>>,
193        visitor: &mut impl FnMut(&[PathElement<'schema>], Event<'payload>) -> Result<(), MessageError>,
194    ) -> Result<(), MessageError> {
195        input.nested(|input| {
196            visitor(path, Event::BeginMessage)?;
197            if self.definitions[definition].first().is_some_and(|field| field.cardinality == Cardinality::Variant) {
198                let index: u32 = input.value()?;
199                let field = self.definitions[definition]
200                    .get(index as usize)
201                    .ok_or_else(|| MessageError::borrowed("unknown Postcard enum variant"))?;
202                let Wire::Message(payload) = &field.kind else { unreachable!("validated enum schema") };
203                path.push(PathElement::Field { name: &field.name, tag: field.tag });
204                visitor(path, Event::Variant { index })?;
205                visitor(path, Event::BeginMessage)?;
206                self.read_fields(*payload, input, path, visitor)?;
207                visitor(path, Event::EndMessage)?;
208                path.pop();
209            } else {
210                self.read_fields(definition, input, path, visitor)?;
211            }
212            visitor(path, Event::EndMessage)
213        })
214    }
215    fn read_fields<'schema, 'payload>(
216        &'schema self, definition: usize, input: &mut PostcardReader<'payload>, path: &mut Vec<PathElement<'schema>>,
217        visitor: &mut impl FnMut(&[PathElement<'schema>], Event<'payload>) -> Result<(), MessageError>,
218    ) -> Result<(), MessageError> {
219        for field in &self.definitions[definition] {
220            path.push(PathElement::Field { name: &field.name, tag: field.tag });
221            match field.cardinality {
222                Cardinality::Required | Cardinality::Variant => field.kind.read(self, input, path, visitor)?,
223                Cardinality::Optional => input.nested(|input| {
224                    let present = input.value::<bool>()?;
225                    visitor(path, Event::Optional { present })?;
226                    if present {
227                        field.kind.read(self, input, path, visitor)?;
228                    }
229                    Ok(())
230                })?,
231                Cardinality::Repeated => input.nested(|input| {
232                    let count: usize = input.value()?;
233                    input.claim(count)?;
234                    visitor(path, Event::BeginRepeated { length: count })?;
235                    for index in 0..count {
236                        path.push(PathElement::Index(index));
237                        field.kind.read(self, input, path, visitor)?;
238                        path.pop();
239                    }
240                    visitor(path, Event::EndRepeated)
241                })?,
242            }
243            path.pop();
244        }
245        Ok(())
246    }
247}
248impl Wire {
249    fn read<'schema, 'payload>(
250        &'schema self, schema: &'schema Validator, input: &mut PostcardReader<'payload>, path: &mut Vec<PathElement<'schema>>,
251        visitor: &mut impl FnMut(&[PathElement<'schema>], Event<'payload>) -> Result<(), MessageError>,
252    ) -> Result<(), MessageError> {
253        let scalar = match self {
254            Self::Bool => Scalar::Bool(input.value()?),
255            Self::U32 => Scalar::U32(input.value()?),
256            Self::U64 => Scalar::U64(input.value()?),
257            Self::I32 => Scalar::I32(input.value()?),
258            Self::I64 => Scalar::I64(input.value()?),
259            Self::F32 => Scalar::F32(input.value()?),
260            Self::F64 => Scalar::F64(input.value()?),
261            Self::String => Scalar::String(input.value()?),
262            Self::Bytes => Scalar::Bytes(input.nested(|input| {
263                let count = input.value()?;
264                input.take(count)
265            })?),
266            Self::Message(definition) => return schema.read(*definition, input, path, visitor),
267        };
268        visitor(path, Event::Scalar(scalar))
269    }
270}
271impl crate::MemorySize for Validator {
272    fn memory_size(&self) -> usize {
273        let mut bytes = std::mem::size_of::<Self>()
274            .saturating_add(2 * std::mem::size_of::<usize>())
275            .saturating_add(self.definitions.len().saturating_mul(std::mem::size_of::<Box<[Checked]>>()));
276        for fields in self.definitions.iter() {
277            bytes = bytes.saturating_add(fields.len().saturating_mul(std::mem::size_of::<Checked>()));
278            for field in fields {
279                bytes = bytes.saturating_add(field.name.len());
280            }
281        }
282        bytes
283    }
284}
285
286/* === Public Functions === */
287
288/* === Private Functions === */
289
290fn graph_compilation_bytes(schema: &graph::Schema) -> Result<usize, graph::Error> {
291    let mut bytes = std::mem::size_of::<Validator>()
292        .checked_add(schema.definitions.len().checked_mul(std::mem::size_of::<Box<[Checked]>>()).ok_or(graph::Error::Budget)?)
293        .ok_or(graph::Error::Budget)?;
294    for fields in &schema.definitions {
295        bytes = bytes
296            .checked_add(fields.len().checked_mul(std::mem::size_of::<Checked>()).ok_or(graph::Error::Budget)?)
297            .ok_or(graph::Error::Budget)?;
298        for field in fields {
299            bytes = bytes.checked_add(field.name.len()).ok_or(graph::Error::Budget)?;
300        }
301    }
302    Ok(bytes)
303}
304
305/* === Tests === */