harmos_domain/schema/graph/
codec.rs1use std::borrow::Cow;
18use std::mem::size_of;
19use super::{Field, Kind, Schema};
23use crate::config::SchemaSettings;
24use crate::message::{MessageError, PostcardReader, PostcardValue, decoding_vector};
25use crate::schema::declaration::{self, Definition};
26use crate::schema::{Cardinality, Described, MAX_NAME_BYTES};
27
28macro_rules! shape {
33 ($ty:ty, $name:literal, $fields:expr) => {
34 impl Described for $ty {
35 const NAME: &'static str = $name;
36 const FIELDS: &'static [Field<Definition>] = $fields;
37 const MARKER: &'static [u8] =
38 &declaration::encode::<{ declaration::encoded_len(Self::NAME, Self::FIELDS) }>(Self::NAME, Self::FIELDS);
39 }
40 };
41}
42
43shape!(
44 Schema,
45 "harmos::schema::Graph",
46 &[
47 described("root", 1, declaration::Kind::U64, Cardinality::Required),
48 described("definitions", 2, declaration::Kind::Message { definition: DefinitionShape::DEFINITION }, Cardinality::Repeated),
49 ]
50);
51shape!(
52 DefinitionShape,
53 "harmos::schema::Definition",
54 &[described("fields", 1, declaration::Kind::Message { definition: FieldShape::DEFINITION }, Cardinality::Repeated),]
55);
56shape!(
57 FieldShape,
58 "harmos::schema::Field",
59 &[
60 described("name", 1, declaration::Kind::String, Cardinality::Required),
61 described("tag", 2, declaration::Kind::U32, Cardinality::Required),
62 described("kind", 3, declaration::Kind::U32, Cardinality::Required),
63 described("cardinality", 4, declaration::Kind::U32, Cardinality::Required),
64 described("definition", 5, declaration::Kind::U64, Cardinality::Optional),
65 ]
66);
67
68type Result<T> = std::result::Result<T, MessageError>;
71
72struct DefinitionShape;
75struct FieldShape;
76
77struct Node<'a> {
78 name: &'a str,
79 tag: u32,
80 kind: u32,
81 cardinality: u32,
82 definition: Option<usize>,
83}
84
85struct Budget {
86 bytes: usize,
87 work: usize,
88 settings: SchemaSettings,
89}
90
91impl Budget {
98 fn new() -> Self {
99 Self {
100 bytes: size_of::<Schema>(),
101 work: 0,
102 settings: SchemaSettings::default(),
103 }
104 }
105
106 fn charge(&mut self, bytes: usize, work: usize) -> Result<()> {
107 self.bytes = self.bytes.checked_add(bytes).ok_or_else(invalid)?;
108 self.work = self.work.checked_add(work).ok_or_else(invalid)?;
109 if self.bytes > self.settings.preparation_bytes.get() || self.work > self.settings.preparation_work.get() {
110 return Err(invalid());
111 }
112 Ok(())
113 }
114
115 fn definition(&mut self, fields: usize) -> Result<()> {
116 let bits = usize::BITS as usize - fields.leading_zeros() as usize;
117 let work = fields.checked_mul(2 * bits + 3).ok_or_else(invalid)?;
118 self.charge(4 * size_of::<Vec<Field>>() + size_of::<usize>() + size_of::<bool>(), work + 1)
119 }
120
121 fn field(&mut self, node: &Node<'_>) -> Result<()> {
122 node.check()?;
123 self.charge(4 * size_of::<Field>() + 2 * node.name.len() + size_of::<&str>() + size_of::<u32>(), node.name.len() + 1)
124 }
125}
126
127impl Node<'_> {
128 fn check(&self) -> Result<()> {
129 if self.name.is_empty()
130 || self.name.len() > MAX_NAME_BYTES
131 || self.name.chars().any(char::is_control)
132 || self.tag == 0
133 || self.tag > 536_870_911
134 || (19_000..=19_999).contains(&self.tag)
135 || self.kind > 9
136 || self.cardinality > 3
137 || (self.kind == 9) != self.definition.is_some()
138 {
139 return Err(invalid());
140 }
141 Ok(())
142 }
143
144 fn owned(self) -> Result<Field> {
145 self.check()?;
146 let kind = match self.kind {
147 0 => Kind::Bool,
148 1 => Kind::U32,
149 2 => Kind::U64,
150 3 => Kind::I32,
151 4 => Kind::I64,
152 5 => Kind::F32,
153 6 => Kind::F64,
154 7 => Kind::String,
155 8 => Kind::Bytes,
156 9 => Kind::Message {
157 definition: self.definition.ok_or_else(invalid)?,
158 },
159 _ => return Err(invalid()),
160 };
161 let cardinality = match self.cardinality {
162 0 => Cardinality::Required,
163 1 => Cardinality::Optional,
164 2 => Cardinality::Repeated,
165 3 => Cardinality::Variant,
166 _ => return Err(invalid()),
167 };
168 Ok(Field {
169 name: self.name.to_owned().into(),
170 tag: self.tag,
171 kind,
172 cardinality,
173 })
174 }
175}
176
177impl<'a> From<&'a Field> for Node<'a> {
178 fn from(field: &'a Field) -> Self {
179 let (kind, definition) = match field.kind {
180 Kind::Bool => (0, None),
181 Kind::U32 => (1, None),
182 Kind::U64 => (2, None),
183 Kind::I32 => (3, None),
184 Kind::I64 => (4, None),
185 Kind::F32 => (5, None),
186 Kind::F64 => (6, None),
187 Kind::String => (7, None),
188 Kind::Bytes => (8, None),
189 Kind::Message { definition } => (9, Some(definition)),
190 };
191 let cardinality = match field.cardinality {
192 Cardinality::Required => 0,
193 Cardinality::Optional => 1,
194 Cardinality::Repeated => 2,
195 Cardinality::Variant => 3,
196 };
197 Self {
198 name: &field.name,
199 tag: field.tag,
200 kind,
201 cardinality,
202 definition,
203 }
204 }
205}
206
207impl PostcardValue for Schema {
208 fn write(&self, bytes: &mut Vec<u8>) {
209 self.root.write(bytes);
210 self.definitions.len().write(bytes);
211 for fields in &self.definitions {
212 fields.len().write(bytes);
213 for field in fields {
214 let node = Node::from(field);
215 node.name.len().write(bytes);
216 bytes.extend_from_slice(node.name.as_bytes());
217 node.tag.write(bytes);
218 node.kind.write(bytes);
219 node.cardinality.write(bytes);
220 match node.definition {
221 None => 0u32.write(bytes),
222 Some(definition) => {
223 1u32.write(bytes);
224 definition.write(bytes);
225 }
226 }
227 }
228 }
229 }
230
231 fn size(&self) -> usize {
232 let mut size = self.root.size().saturating_add(self.definitions.len().size());
233 for fields in &self.definitions {
234 size = size.saturating_add(fields.len().size());
235 for field in fields {
236 let node = Node::from(field);
237 let reference = match node.definition {
238 None => 1,
239 Some(index) => 1usize.saturating_add(index.size()),
240 };
241 size = size
242 .saturating_add(node.name.len().size())
243 .saturating_add(node.name.len())
244 .saturating_add(node.tag.size())
245 .saturating_add(node.kind.size())
246 .saturating_add(node.cardinality.size())
247 .saturating_add(reference);
248 }
249 }
250 size
251 }
252
253 fn memory(input: &mut PostcardReader<'_>) -> Result<usize> {
254 input.nested(|input| {
255 let root = input.value::<usize>()?;
256 let count = input.value::<usize>()?;
257 if root >= count {
258 return Err(invalid());
259 }
260 input.claim(count)?;
261 let mut budget = Budget::new();
262 for _ in 0..count {
263 let fields = input.value::<usize>()?;
264 input.claim(fields)?;
265 budget.definition(fields)?;
266 for _ in 0..fields {
267 let node = postcard_node(input)?;
268 if node.definition.is_some_and(|reference| reference >= count) {
269 return Err(invalid());
270 }
271 budget.field(&node)?;
272 }
273 }
274 Ok(budget.bytes)
275 })
276 }
277
278 fn read(input: &mut PostcardReader<'_>) -> Result<Self> {
279 Self::memory(&mut input.clone())?;
280 input.nested(|input| {
281 let root = input.value()?;
282 let count = input.value::<usize>()?;
283 input.claim(count)?;
284 let mut definitions = decoding_vector(count)?;
285 for _ in 0..count {
286 let count = input.value::<usize>()?;
287 input.claim(count)?;
288 let mut fields = decoding_vector(count)?;
289 for _ in 0..count {
290 fields.push(postcard_node(input)?.owned()?);
291 }
292 definitions.push(fields);
293 }
294 let schema = Self { root, definitions };
295 schema.prepare(&SchemaSettings::default()).map_err(|_| invalid())?;
296 Ok(schema)
297 })
298 }
299}
300
301const fn described(name: &'static str, tag: u32, kind: declaration::Kind, cardinality: Cardinality) -> Field<Definition> {
306 Field {
307 name: Cow::Borrowed(name),
308 tag,
309 kind,
310 cardinality,
311 }
312}
313
314fn postcard_node<'a>(input: &mut PostcardReader<'a>) -> Result<Node<'a>> {
315 let length = input.value::<usize>()?;
316 if length > MAX_NAME_BYTES {
317 return Err(invalid());
318 }
319 let name = std::str::from_utf8(input.take(length)?).map_err(|_| invalid())?;
320 let tag = input.value()?;
321 let kind = input.value()?;
322 let cardinality = input.value()?;
323 let definition = match input.value::<u32>()? {
324 0 => None,
325 1 => Some(input.value()?),
326 _ => return Err(invalid()),
327 };
328 Ok(Node { name, tag, kind, cardinality, definition })
329}
330
331fn invalid() -> MessageError {
332 MessageError::borrowed("invalid finite schema graph or preparation allowance")
333}
334
335