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 { bytes: size_of::<Schema>(), work: 0, settings: SchemaSettings::default() }
100 }
101
102 fn charge(&mut self, bytes: usize, work: usize) -> Result<()> {
103 self.bytes = self.bytes.checked_add(bytes).ok_or_else(invalid)?;
104 self.work = self.work.checked_add(work).ok_or_else(invalid)?;
105 if self.bytes > self.settings.preparation_bytes.get() || self.work > self.settings.preparation_work.get() {
106 return Err(invalid());
107 }
108 Ok(())
109 }
110
111 fn definition(&mut self, fields: usize) -> Result<()> {
112 let bits = usize::BITS as usize - fields.leading_zeros() as usize;
113 let work = fields.checked_mul(2 * bits + 3).ok_or_else(invalid)?;
114 self.charge(4 * size_of::<Vec<Field>>() + size_of::<usize>() + size_of::<bool>(), work + 1)
115 }
116
117 fn field(&mut self, node: &Node<'_>) -> Result<()> {
118 node.check()?;
119 self.charge(4 * size_of::<Field>() + 2 * node.name.len() + size_of::<&str>() + size_of::<u32>(), node.name.len() + 1)
120 }
121}
122
123impl Node<'_> {
124 fn check(&self) -> Result<()> {
125 if self.name.is_empty()
126 || self.name.len() > MAX_NAME_BYTES
127 || self.name.chars().any(char::is_control)
128 || self.tag == 0
129 || self.tag > 536_870_911
130 || (19_000..=19_999).contains(&self.tag)
131 || self.kind > 9
132 || self.cardinality > 3
133 || (self.kind == 9) != self.definition.is_some()
134 {
135 return Err(invalid());
136 }
137 Ok(())
138 }
139
140 fn owned(self) -> Result<Field> {
141 self.check()?;
142 let kind = match self.kind {
143 0 => Kind::Bool,
144 1 => Kind::U32,
145 2 => Kind::U64,
146 3 => Kind::I32,
147 4 => Kind::I64,
148 5 => Kind::F32,
149 6 => Kind::F64,
150 7 => Kind::String,
151 8 => Kind::Bytes,
152 9 => Kind::Message { definition: self.definition.ok_or_else(invalid)? },
153 _ => return Err(invalid()),
154 };
155 let cardinality = match self.cardinality {
156 0 => Cardinality::Required,
157 1 => Cardinality::Optional,
158 2 => Cardinality::Repeated,
159 3 => Cardinality::Variant,
160 _ => return Err(invalid()),
161 };
162 Ok(Field { name: self.name.to_owned().into(), tag: self.tag, kind, cardinality })
163 }
164}
165
166impl<'a> From<&'a Field> for Node<'a> {
167 fn from(field: &'a Field) -> Self {
168 let (kind, definition) = match field.kind {
169 Kind::Bool => (0, None),
170 Kind::U32 => (1, None),
171 Kind::U64 => (2, None),
172 Kind::I32 => (3, None),
173 Kind::I64 => (4, None),
174 Kind::F32 => (5, None),
175 Kind::F64 => (6, None),
176 Kind::String => (7, None),
177 Kind::Bytes => (8, None),
178 Kind::Message { definition } => (9, Some(definition)),
179 };
180 let cardinality = match field.cardinality {
181 Cardinality::Required => 0,
182 Cardinality::Optional => 1,
183 Cardinality::Repeated => 2,
184 Cardinality::Variant => 3,
185 };
186 Self { name: &field.name, tag: field.tag, kind, cardinality, definition }
187 }
188}
189
190impl PostcardValue for Schema {
191 fn write(&self, bytes: &mut Vec<u8>) {
192 self.root.write(bytes);
193 self.definitions.len().write(bytes);
194 for fields in &self.definitions {
195 fields.len().write(bytes);
196 for field in fields {
197 let node = Node::from(field);
198 node.name.len().write(bytes);
199 bytes.extend_from_slice(node.name.as_bytes());
200 node.tag.write(bytes);
201 node.kind.write(bytes);
202 node.cardinality.write(bytes);
203 match node.definition {
204 None => 0u32.write(bytes),
205 Some(definition) => {
206 1u32.write(bytes);
207 definition.write(bytes);
208 }
209 }
210 }
211 }
212 }
213
214 fn size(&self) -> usize {
215 let mut size = self.root.size().saturating_add(self.definitions.len().size());
216 for fields in &self.definitions {
217 size = size.saturating_add(fields.len().size());
218 for field in fields {
219 let node = Node::from(field);
220 let reference = match node.definition {
221 None => 1,
222 Some(index) => 1usize.saturating_add(index.size()),
223 };
224 size = size
225 .saturating_add(node.name.len().size())
226 .saturating_add(node.name.len())
227 .saturating_add(node.tag.size())
228 .saturating_add(node.kind.size())
229 .saturating_add(node.cardinality.size())
230 .saturating_add(reference);
231 }
232 }
233 size
234 }
235
236 fn memory(input: &mut PostcardReader<'_>) -> Result<usize> {
237 input.nested(|input| {
238 let root = input.value::<usize>()?;
239 let count = input.value::<usize>()?;
240 if root >= count {
241 return Err(invalid());
242 }
243 input.claim(count)?;
244 let mut budget = Budget::new();
245 for _ in 0..count {
246 let fields = input.value::<usize>()?;
247 input.claim(fields)?;
248 budget.definition(fields)?;
249 for _ in 0..fields {
250 let node = postcard_node(input)?;
251 if node.definition.is_some_and(|reference| reference >= count) {
252 return Err(invalid());
253 }
254 budget.field(&node)?;
255 }
256 }
257 Ok(budget.bytes)
258 })
259 }
260
261 fn read(input: &mut PostcardReader<'_>) -> Result<Self> {
262 Self::memory(&mut input.clone())?;
263 input.nested(|input| {
264 let root = input.value()?;
265 let count = input.value::<usize>()?;
266 input.claim(count)?;
267 let mut definitions = decoding_vector(count)?;
268 for _ in 0..count {
269 let count = input.value::<usize>()?;
270 input.claim(count)?;
271 let mut fields = decoding_vector(count)?;
272 for _ in 0..count {
273 fields.push(postcard_node(input)?.owned()?);
274 }
275 definitions.push(fields);
276 }
277 let schema = Self { root, definitions };
278 schema.prepare(&SchemaSettings::default()).map_err(|_| invalid())?;
279 Ok(schema)
280 })
281 }
282}
283
284const fn described(name: &'static str, tag: u32, kind: declaration::Kind, cardinality: Cardinality) -> Field<Definition> {
289 Field { name: Cow::Borrowed(name), tag, kind, cardinality }
290}
291
292fn postcard_node<'a>(input: &mut PostcardReader<'a>) -> Result<Node<'a>> {
293 let length = input.value::<usize>()?;
294 if length > MAX_NAME_BYTES {
295 return Err(invalid());
296 }
297 let name = std::str::from_utf8(input.take(length)?).map_err(|_| invalid())?;
298 let tag = input.value()?;
299 let kind = input.value()?;
300 let cardinality = input.value()?;
301 let definition = match input.value::<u32>()? {
302 0 => None,
303 1 => Some(input.value()?),
304 _ => return Err(invalid()),
305 };
306 Ok(Node { name, tag, kind, cardinality, definition })
307}
308
309fn invalid() -> MessageError {
310 MessageError::borrowed("invalid finite schema graph or preparation allowance")
311}
312
313