Skip to main content

harmos_domain/
artifacts.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
17
18// Third Party Imports
19use memchr::{memchr, memmem};
20use serde::Deserialize;
21use serde::de::DeserializeOwned;
22// Local Imports
23
24/* === Constants === */
25
26const FAMILY: &[u8] = b"\0HARMOS:ARTIFACT:\0";
27pub const PREFIX: &[u8] = b"\0HARMOS:ARTIFACT:V6:";
28
29pub const FRAGMENT_LIMIT: usize = 64 * 1024;
30
31pub const KINDS: [&str; 17] = [
32    "capabilities",
33    "contracts",
34    "contributions",
35    "guest",
36    "listeners",
37    "observers",
38    "operations",
39    "plugin",
40    "publications",
41    "requires",
42    "restart",
43    "route",
44    "routes",
45    "schema",
46    "sidecar",
47    "stream",
48    "subscribe",
49];
50
51/* === Macros === */
52
53/* === Type Aliases === */
54
55/* === Structs === */
56
57#[derive(Deserialize)]
58struct Kinded {
59    kind: String,
60}
61
62/* === Enums === */
63
64#[derive(Debug, thiserror::Error)]
65pub enum ScanError {
66    #[error("the artifact uses another Harmos metadata format; rebuild it with the current SDK")]
67    IncompatiblePayload,
68
69    #[error("artifact metadata exceeds the schema preparation memory or work budget")]
70    Budget,
71
72    #[error("a Harmos metadata fragment exceeds its {FRAGMENT_LIMIT}-byte bound")]
73    Oversized,
74
75    #[error("a Harmos metadata fragment is invalid: {source}")]
76    Malformed {
77        #[source]
78        source: serde_json::Error,
79    },
80
81    #[error("a Harmos metadata fragment declares the unknown kind `{kind}`")]
82    UnknownKind { kind: String },
83}
84
85/* === Traits === */
86
87/* === Implementations === */
88
89/* === Public Functions === */
90
91pub fn fragments<F: DeserializeOwned>(artifact: &[u8], owned: &[&str]) -> Result<Vec<F>, ScanError> {
92    fragments_with_settings(artifact, owned, &mut crate::config::SchemaSettings::default())
93}
94
95pub(crate) fn fragments_with_settings<F: DeserializeOwned>(
96    artifact: &[u8], owned: &[&str], settings: &mut crate::config::SchemaSettings,
97) -> Result<Vec<F>, ScanError> {
98    // Keep Complete Search Markers Out Of Reader Binaries Themselves.
99    let family = &FAMILY[..FAMILY.len() - 1];
100    let retired = memmem::Finder::new(family);
101    // One Current Format: Any Other Versioned Fragment Is Refused, Never Interpreted.
102    for position in retired.find_iter(artifact) {
103        let version = &artifact[position + family.len()..];
104        if version.starts_with(b"V") && !version.starts_with(b"V6:") {
105            return Err(ScanError::IncompatiblePayload);
106        }
107    }
108    let mut prefix = [0; PREFIX.len()];
109    prefix[..family.len()].copy_from_slice(std::hint::black_box(family));
110    // A Terminator Separates The Family Literal From Adjacent Version Literals.
111    prefix[family.len()] = std::hint::black_box(b'V');
112    prefix[family.len() + 1] = std::hint::black_box(b'6');
113    prefix[family.len() + 2] = std::hint::black_box(b':');
114    let current = memmem::Finder::new(&prefix);
115    let mut found = Vec::new();
116    let mut cursor = 0;
117    let mut remaining_bytes = settings.preparation_bytes.get();
118    let mut remaining_work = settings.preparation_work.get();
119
120    while let Some(relative) = current.find(&artifact[cursor..]) {
121        let start = cursor + relative + PREFIX.len();
122        let bounded = &artifact[start..artifact.len().min(start + FRAGMENT_LIMIT + 1)];
123        let Some(length) = memchr(0, bounded) else {
124            return Err(ScanError::Oversized);
125        };
126        let encoded = &bounded[..length];
127        cursor = start + length + 1;
128
129        remaining_work = remaining_work.checked_sub(encoded.len()).ok_or(ScanError::Budget)?;
130        let charge =
131            encoded.len().checked_mul(16).and_then(|bytes| bytes.checked_add(4 * std::mem::size_of::<F>())).ok_or(ScanError::Budget)?;
132        remaining_bytes = remaining_bytes.checked_sub(charge).ok_or(ScanError::Budget)?;
133        let Kinded { kind } = serde_json::from_slice(encoded).map_err(|source| ScanError::Malformed { source })?;
134        if !KINDS.contains(&kind.as_str()) {
135            return Err(ScanError::UnknownKind { kind });
136        }
137        if !owned.contains(&kind.as_str()) {
138            continue;
139        }
140
141        found.push(serde_json::from_slice(encoded).map_err(|source| ScanError::Malformed { source })?);
142    }
143
144    settings.preparation_bytes = std::num::NonZeroUsize::new(remaining_bytes).ok_or(ScanError::Budget)?;
145    settings.preparation_work = std::num::NonZeroUsize::new(remaining_work).ok_or(ScanError::Budget)?;
146    Ok(found)
147}
148
149/* === Private Functions === */
150
151/* === Tests === */