harmos_domain/
artifacts.rs1use memchr::{memchr, memmem};
20use serde::Deserialize;
21use serde::de::DeserializeOwned;
22const 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#[derive(Deserialize)]
58struct Kinded {
59 kind: String,
60}
61
62#[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
85pub 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 let family = &FAMILY[..FAMILY.len() - 1];
100 let retired = memmem::Finder::new(family);
101 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 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