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:V1:";
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("artifact metadata exceeds the schema preparation memory or work budget")]
67 Budget,
68
69 #[error("a Harmos metadata fragment exceeds its {FRAGMENT_LIMIT}-byte bound")]
70 Oversized,
71
72 #[error("a Harmos metadata fragment is invalid: {source}")]
73 Malformed {
74 #[source]
75 source: serde_json::Error,
76 },
77
78 #[error("a Harmos metadata fragment declares the unknown kind `{kind}`")]
79 UnknownKind { kind: String },
80}
81
82pub fn fragments<F: DeserializeOwned>(artifact: &[u8], owned: &[&str]) -> Result<Vec<F>, ScanError> {
89 fragments_with_settings(artifact, owned, &mut crate::config::SchemaSettings::default())
90}
91
92pub(crate) fn fragments_with_settings<F: DeserializeOwned>(
93 artifact: &[u8], owned: &[&str], settings: &mut crate::config::SchemaSettings,
94) -> Result<Vec<F>, ScanError> {
95 let family = &FAMILY[..FAMILY.len() - 1];
97 let mut prefix = [0; PREFIX.len()];
98 prefix[..family.len()].copy_from_slice(std::hint::black_box(family));
99 prefix[family.len()] = std::hint::black_box(b'V');
101 prefix[family.len() + 1] = std::hint::black_box(b'1');
102 prefix[family.len() + 2] = std::hint::black_box(b':');
103 let current = memmem::Finder::new(&prefix);
104 let mut found = Vec::new();
105 let mut cursor = 0;
106 let mut remaining_bytes = settings.preparation_bytes.get();
107 let mut remaining_work = settings.preparation_work.get();
108
109 while let Some(relative) = current.find(&artifact[cursor..]) {
110 let start = cursor + relative + PREFIX.len();
111 let bounded = &artifact[start..artifact.len().min(start + FRAGMENT_LIMIT + 1)];
112 let Some(length) = memchr(0, bounded) else {
113 return Err(ScanError::Oversized);
114 };
115 let encoded = &bounded[..length];
116 cursor = start + length + 1;
117
118 remaining_work = remaining_work.checked_sub(encoded.len()).ok_or(ScanError::Budget)?;
119 let charge =
120 encoded.len().checked_mul(16).and_then(|bytes| bytes.checked_add(4 * std::mem::size_of::<F>())).ok_or(ScanError::Budget)?;
121 remaining_bytes = remaining_bytes.checked_sub(charge).ok_or(ScanError::Budget)?;
122 let Kinded { kind } = serde_json::from_slice(encoded).map_err(|source| ScanError::Malformed { source })?;
123 if !KINDS.contains(&kind.as_str()) {
124 return Err(ScanError::UnknownKind { kind });
125 }
126 if !owned.contains(&kind.as_str()) {
127 continue;
128 }
129
130 found.push(serde_json::from_slice(encoded).map_err(|source| ScanError::Malformed { source })?);
131 }
132
133 settings.preparation_bytes = std::num::NonZeroUsize::new(remaining_bytes).ok_or(ScanError::Budget)?;
134 settings.preparation_work = std::num::NonZeroUsize::new(remaining_work).ok_or(ScanError::Budget)?;
135 Ok(found)
136}
137
138