Skip to main content

harmos_domain/schema/
declaration.rs

1/* ***
2 *
3 * Harmos
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * ***
8 */
9
10// Static Shared Type Declarations And Inert Metadata
11
12/* === Modules === */
13
14/* === Re-Exports === */
15
16/* === Imports === */
17
18// Standard Library Imports
19use std::any::TypeId;
20use std::collections::HashMap;
21use std::fmt;
22use std::mem::size_of;
23// Third Party Imports
24use serde::{Deserialize, Serialize};
25// Local Imports
26use super::graph::{self, Error, Schema};
27use super::{Cardinality, MAX_NAME_BYTES};
28use crate::config::SchemaSettings;
29use crate::inventory::{digits, number, quoted, quoted_len, write};
30
31/* === Constants === */
32
33const HEADER: &[u8] = b"{\"kind\":\"schema\",\"name\":";
34const FIELDS: &[u8] = b",\"fields\":[";
35const NAME: &[u8] = b"{\"name\":";
36const TAG: &[u8] = b",\"tag\":";
37const KIND: &[u8] = b",\"kind\":{\"type\":";
38const REFERENCE: &[u8] = b",\"definition\":";
39const CARDINALITY: &[u8] = b"},\"cardinality\":";
40
41/* === Macros === */
42
43/* === Type Aliases === */
44
45pub type Field<R = Definition> = graph::Field<R>;
46pub type Kind<R = Definition> = graph::Kind<R>;
47
48/* === Structs === */
49
50#[derive(Clone, Copy)]
51pub struct Definition {
52    pub name: &'static str,
53
54    pub default_variant: Option<usize>,
55
56    pub identity: fn() -> TypeId,
57    pub fields: fn() -> &'static [Field<Definition>],
58    pub marker: fn() -> &'static [u8],
59}
60
61#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
62#[serde(from = "Fragment", into = "Fragment")]
63pub struct Named {
64    pub name: String,
65    pub fields: Vec<Field<String>>,
66}
67
68// Bounded Static Storage For Names Of Generic Message Shapes.
69pub struct Name {
70    bytes: [u8; MAX_NAME_BYTES],
71    length: usize,
72}
73
74struct Plan {
75    declarations: Vec<Definition>,
76    indices: HashMap<&'static str, usize>,
77    remaining_bytes: usize,
78    remaining_work: usize,
79}
80
81/* === Enums === */
82
83#[derive(Serialize, Deserialize)]
84#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
85enum Fragment {
86    Schema { name: String, fields: Vec<Field<String>> },
87}
88
89/* === Traits === */
90
91/* === Implementations === */
92
93impl PartialEq for Definition {
94    fn eq(&self, other: &Self) -> bool {
95        self.name == other.name && (self.identity)() == (other.identity)()
96    }
97}
98impl Eq for Definition {}
99
100impl Name {
101    pub const fn new(owner: &str, arguments: &[&str]) -> Self {
102        let mut name = Self { bytes: [0; MAX_NAME_BYTES], length: 0 };
103        name.push(owner);
104        if !arguments.is_empty() {
105            name.push("<");
106            let mut index = 0;
107            while index < arguments.len() {
108                if index > 0 {
109                    name.push(",");
110                }
111                name.push(arguments[index]);
112                index += 1;
113            }
114            name.push(">");
115        }
116        name
117    }
118
119    pub const fn as_str(&self) -> &str {
120        match std::str::from_utf8(self.bytes.split_at(self.length).0) {
121            Ok(name) => name,
122            Err(_) => panic!("schema names originate from valid UTF-8"),
123        }
124    }
125
126    const fn push(&mut self, part: &str) {
127        assert!(part.len() <= MAX_NAME_BYTES - self.length, "schema name exceeds its bound");
128        let mut index = 0;
129        while index < part.len() {
130            self.bytes[self.length] = part.as_bytes()[index];
131            self.length += 1;
132            index += 1;
133        }
134    }
135}
136
137impl Definition {
138    pub const fn of<T: super::Described + ?Sized>() -> Self {
139        Self { name: T::NAME, default_variant: T::DEFAULT_VARIANT, identity: TypeId::of::<T>, fields: fields::<T>, marker: marker::<T> }
140    }
141}
142
143impl Named {
144    pub fn decode<'a>(fragments: impl IntoIterator<Item = &'a [u8]>, settings: &SchemaSettings) -> Result<Vec<Self>, Error> {
145        let mut budget = Plan::new(settings);
146        let mut declarations = Vec::new();
147        for encoded in fragments {
148            if encoded.len() > crate::FRAGMENT_LIMIT {
149                return Err(Error::Budget);
150            }
151            // Flat JSON Input Bounds Parser Allocations Before Decoding Each Type.
152            budget.charge(encoded.len().checked_mul(8).ok_or(Error::Budget)? + 4 * size_of::<Self>(), encoded.len() + 1)?;
153            let declaration: Self = serde_json::from_slice(encoded)
154                .map_err(|_| Error::Field { field: "schema fragment".into(), reason: "invalid schema metadata" })?;
155            declarations.push(declaration);
156        }
157        Ok(declarations)
158    }
159}
160
161impl From<Fragment> for Named {
162    fn from(fragment: Fragment) -> Self {
163        let Fragment::Schema { name, fields } = fragment;
164        Self { name, fields }
165    }
166}
167impl From<Named> for Fragment {
168    fn from(named: Named) -> Self {
169        Self::Schema { name: named.name, fields: named.fields }
170    }
171}
172
173impl fmt::Debug for Definition {
174    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
175        formatter.debug_struct("Definition").field("name", &self.name).finish_non_exhaustive()
176    }
177}
178
179impl Schema {
180    // Resolve Inert Named Metadata Without Executing Artifact Code.
181    pub fn from_named(root: &str, declarations: &[Named], settings: &SchemaSettings) -> Result<Self, Error> {
182        resolve_named(&[("", root)], declarations, settings, false)
183    }
184
185    // Resolve Multiple Contract Directions From One Inert Type Dictionary.
186    pub fn from_named_roots(roots: &[(&str, &str)], declarations: &[Named], settings: &SchemaSettings) -> Result<Self, Error> {
187        resolve_named(roots, declarations, settings, true)
188    }
189
190    pub fn from_declaration(root: Definition, settings: &SchemaSettings) -> Result<Self, Error> {
191        let mut plan = Plan::new(settings);
192        plan.insert(root)?;
193        let schema = Self { root: 0, definitions: plan.compile()? };
194        schema.prepare(settings)?;
195        Ok(schema)
196    }
197
198    // Share Each Message Definition Across A Set Of Contract Directions.
199    pub fn from_declarations(roots: &[(&str, Definition)], settings: &SchemaSettings) -> Result<Self, Error> {
200        let mut plan = Plan::new(settings);
201        plan.charge(4 * size_of::<Vec<Field>>() + size_of::<Self>(), 1)?;
202        let mut fields = Vec::new();
203        for (index, (name, declaration)) in roots.iter().enumerate() {
204            plan.charge(4 * size_of::<Field<usize>>() + name.len(), name.len() + 1)?;
205            let tag = u32::try_from(index + 1).map_err(|_| Error::Budget)?;
206            fields.push(Field {
207                name: (*name).to_owned().into(),
208                tag,
209                kind: Kind::Message { definition: plan.insert(*declaration)? },
210                cardinality: Cardinality::Required,
211            });
212        }
213        let mut definitions = plan.compile()?;
214        let root = definitions.len();
215        definitions.push(fields);
216        let schema = Self { root, definitions };
217        schema.prepare(settings)?;
218        Ok(schema)
219    }
220}
221
222impl Plan {
223    fn compile(mut self) -> Result<Vec<Vec<Field<usize>>>, Error> {
224        let mut definitions = Vec::new();
225        let mut index = 0;
226        while index < self.declarations.len() {
227            let declaration = self.declarations[index];
228            let mut fields = Vec::new();
229            for field in (declaration.fields)() {
230                self.charge(4 * size_of::<Field<usize>>() + field.name.len(), field.name.len() + 1)?;
231                let kind = field.kind.try_map(|definition| self.insert(definition))?;
232                fields.push(Field { name: field.name.to_string().into(), tag: field.tag, kind, cardinality: field.cardinality });
233            }
234            definitions.push(fields);
235            index += 1;
236        }
237        Ok(definitions)
238    }
239    fn new(settings: &SchemaSettings) -> Self {
240        Self {
241            declarations: Vec::new(),
242            indices: HashMap::new(),
243            remaining_bytes: settings.preparation_bytes.get(),
244            remaining_work: settings.preparation_work.get(),
245        }
246    }
247    fn charge(&mut self, bytes: usize, work: usize) -> Result<(), Error> {
248        self.remaining_bytes = self.remaining_bytes.checked_sub(bytes).ok_or(Error::Budget)?;
249        self.remaining_work = self.remaining_work.checked_sub(work).ok_or(Error::Budget)?;
250        Ok(())
251    }
252    fn insert(&mut self, definition: Definition) -> Result<usize, Error> {
253        self.charge(0, definition.name.len() + 1)?;
254        if definition.name.is_empty() || definition.name.len() > MAX_NAME_BYTES || definition.name.chars().any(char::is_control) {
255            return Err(Error::Field { field: super::diagnostic_name(definition.name), reason: "invalid schema definition name" });
256        }
257        if let Some(index) = self.indices.get(definition.name).copied() {
258            let original = self.declarations[index];
259            if (original.identity)() != (definition.identity)() {
260                return Err(Error::Field { field: definition.name.into(), reason: "schema definition name has multiple owners" });
261            }
262            return Ok(index);
263        }
264        self.charge(4 * (size_of::<Definition>() + size_of::<Vec<Field>>() + size_of::<(&str, usize)>() + 1), 1)?;
265        let index = self.declarations.len();
266        self.declarations.push(definition);
267        self.indices.insert(definition.name, index);
268        Ok(index)
269    }
270}
271
272/* === Public Functions === */
273
274pub fn markers(root: Definition, settings: &SchemaSettings) -> Result<Vec<&'static [u8]>, Error> {
275    markers_many([root], settings)
276}
277
278pub fn markers_many(roots: impl IntoIterator<Item = Definition>, settings: &SchemaSettings) -> Result<Vec<&'static [u8]>, Error> {
279    let mut plan = Plan::new(settings);
280    for root in roots {
281        plan.insert(root)?;
282    }
283    let mut index = 0;
284    while index < plan.declarations.len() {
285        let declaration = plan.declarations[index];
286        for field in (declaration.fields)() {
287            plan.charge(0, 1)?;
288            if let Kind::Message { definition } = field.kind {
289                plan.insert(definition)?;
290            }
291        }
292        index += 1;
293    }
294    let output_bytes = plan.declarations.len().checked_mul(std::mem::size_of::<&[u8]>()).ok_or(Error::Budget)?;
295    plan.charge(output_bytes, 0)?;
296    Ok(plan.declarations.iter().map(|definition| (definition.marker)()).collect())
297}
298
299pub const fn encoded_len(name: &str, fields: &[Field<Definition>]) -> usize {
300    let mut length = crate::PREFIX.len() + HEADER.len() + quoted_len(name) + FIELDS.len() + 3;
301    let mut index = 0;
302    while index < fields.len() {
303        let field = &fields[index];
304        length += NAME.len()
305            + quoted_len(field_name(field))
306            + TAG.len()
307            + digits(field.tag as u64)
308            + KIND.len()
309            + quoted_len(kind_name(&field.kind))
310            + CARDINALITY.len()
311            + quoted_len(cardinality_name(field.cardinality))
312            + 1;
313        if let Kind::Message { definition } = &field.kind {
314            length += REFERENCE.len() + quoted_len(definition.name);
315        }
316        if index > 0 {
317            length += 1;
318        }
319        index += 1;
320    }
321    assert!(length - crate::PREFIX.len() - 1 <= crate::FRAGMENT_LIMIT);
322    length
323}
324
325pub const fn encode<const N: usize>(name: &str, fields: &[Field<Definition>]) -> [u8; N] {
326    assert!(N == encoded_len(name, fields));
327    encode_buffer(name, fields)
328}
329
330// Encode A Small Generic Shape Into Explicitly Bounded Static Storage.
331// The Caller Retains Only The `encoded_len` Prefix Of The Result.
332pub const fn encode_buffer<const N: usize>(name: &str, fields: &[Field<Definition>]) -> [u8; N] {
333    assert!(N >= encoded_len(name, fields), "schema marker exceeds its static storage");
334    let mut output = [0; N];
335    let mut cursor = 0;
336    write(&mut output, &mut cursor, crate::PREFIX);
337    write(&mut output, &mut cursor, HEADER);
338    quoted(&mut output, &mut cursor, name);
339    write(&mut output, &mut cursor, FIELDS);
340    let mut index = 0;
341    while index < fields.len() {
342        let field = &fields[index];
343        if index > 0 {
344            write(&mut output, &mut cursor, b",");
345        }
346        write(&mut output, &mut cursor, NAME);
347        quoted(&mut output, &mut cursor, field_name(field));
348        write(&mut output, &mut cursor, TAG);
349        number(&mut output, &mut cursor, field.tag as u64);
350        write(&mut output, &mut cursor, KIND);
351        quoted(&mut output, &mut cursor, kind_name(&field.kind));
352        if let Kind::Message { definition } = &field.kind {
353            write(&mut output, &mut cursor, REFERENCE);
354            quoted(&mut output, &mut cursor, definition.name);
355        }
356        write(&mut output, &mut cursor, CARDINALITY);
357        quoted(&mut output, &mut cursor, cardinality_name(field.cardinality));
358        write(&mut output, &mut cursor, b"}");
359        index += 1;
360    }
361    write(&mut output, &mut cursor, b"]}\0");
362    assert!(cursor == encoded_len(name, fields));
363    output
364}
365
366/* === Private Functions === */
367
368fn fields<T: super::Described + ?Sized>() -> &'static [Field<Definition>] {
369    T::FIELDS
370}
371
372fn marker<T: super::Described + ?Sized>() -> &'static [u8] {
373    T::MARKER
374}
375
376fn resolve_named(roots: &[(&str, &str)], declarations: &[Named], settings: &SchemaSettings, composite: bool) -> Result<Schema, Error> {
377    let mut budget = Plan::new(settings);
378    let mut dictionary = HashMap::new();
379    for declaration in declarations {
380        let name = declaration.name.as_str();
381        budget.charge(4 * size_of::<(&str, &Named)>() + name.len(), name.len() + 1)?;
382        if name.is_empty() || name.len() > MAX_NAME_BYTES || name.chars().any(char::is_control) {
383            return Err(Error::Field { field: super::diagnostic_name(name), reason: "invalid schema definition name" });
384        }
385        // Charge The Already Decoded Input Alongside Output And Lookup Scratch.
386        budget.charge(declaration.fields.capacity() * size_of::<Field<String>>(), 1)?;
387        for field in &declaration.fields {
388            budget.charge(field.name.len(), field.name.len() + 1)?;
389            if let Kind::Message { definition } = &field.kind {
390                budget.charge(definition.len(), definition.len() + 1)?;
391            }
392        }
393        if let Some(original) = dictionary.insert(name, declaration) {
394            budget.charge(0, declaration.fields.len() + 1)?;
395            if original != declaration {
396                return Err(Error::Field { field: name.into(), reason: "schema definition name has conflicting metadata" });
397            }
398        }
399    }
400    let missing = |name: &str| Error::Field { field: super::diagnostic_name(name), reason: "schema definition is absent" };
401    let mut names = Vec::new();
402    let mut indices = HashMap::new();
403    let mut directions = Vec::new();
404    budget.charge(4 * size_of::<Vec<Field>>() + size_of::<Schema>(), 1)?;
405    for (index, (direction, root)) in roots.iter().enumerate() {
406        let declaration = dictionary.get(root).ok_or_else(|| missing(root))?;
407        let name = declaration.name.as_str();
408        let definition = if let Some(index) = indices.get(name) {
409            *index
410        } else {
411            budget.charge(4 * (size_of::<&str>() + size_of::<(&str, usize)>()), name.len() + 1)?;
412            let index = names.len();
413            names.push(name);
414            indices.insert(name, index);
415            index
416        };
417        if composite {
418            budget.charge(4 * size_of::<Field<usize>>() + direction.len(), direction.len() + 1)?;
419            directions.push(Field {
420                name: (*direction).to_owned().into(),
421                tag: u32::try_from(index + 1).map_err(|_| Error::Budget)?,
422                kind: Kind::Message { definition },
423                cardinality: Cardinality::Required,
424            });
425        }
426    }
427    let mut definitions = Vec::new();
428    let mut index = 0;
429    while index < names.len() {
430        let declaration = dictionary[names[index]];
431        let mut fields = Vec::new();
432        budget.charge(4 * (size_of::<&str>() + size_of::<(&str, usize)>() + size_of::<Vec<Field>>()), 1)?;
433        for field in &declaration.fields {
434            budget.charge(4 * size_of::<Field<usize>>() + field.name.len(), field.name.len() + 1)?;
435            let kind = field.kind.clone().try_map(|name| {
436                let declaration = dictionary.get(name.as_str()).ok_or_else(|| missing(&name))?;
437                let name = declaration.name.as_str();
438                if let Some(index) = indices.get(name) {
439                    return Ok(*index);
440                }
441                budget.charge(4 * (size_of::<&str>() + size_of::<(&str, usize)>()), name.len() + 1)?;
442                let index = names.len();
443                indices.insert(name, index);
444                names.push(name);
445                Ok(index)
446            })?;
447            fields.push(Field { name: field.name.clone(), tag: field.tag, kind, cardinality: field.cardinality });
448        }
449        definitions.push(fields);
450        index += 1;
451    }
452    let root = if composite {
453        let root = definitions.len();
454        definitions.push(directions);
455        root
456    } else {
457        0
458    };
459    let schema = Schema { root, definitions };
460    schema.prepare(settings)?;
461    Ok(schema)
462}
463
464const fn field_name(field: &Field<Definition>) -> &str {
465    match &field.name {
466        std::borrow::Cow::Borrowed(name) => name,
467        std::borrow::Cow::Owned(_) => panic!("constant schema names must be borrowed"),
468    }
469}
470const fn kind_name(kind: &Kind<Definition>) -> &'static str {
471    match kind {
472        Kind::Bool => "bool",
473        Kind::U32 => "u32",
474        Kind::U64 => "u64",
475        Kind::I32 => "i32",
476        Kind::I64 => "i64",
477        Kind::F32 => "f32",
478        Kind::F64 => "f64",
479        Kind::String => "string",
480        Kind::Bytes => "bytes",
481        Kind::Message { .. } => "message",
482    }
483}
484const fn cardinality_name(cardinality: Cardinality) -> &'static str {
485    match cardinality {
486        Cardinality::Required => "required",
487        Cardinality::Optional => "optional",
488        Cardinality::Repeated => "repeated",
489        Cardinality::Variant => "variant",
490    }
491}
492
493/* === Tests === */