1use std::collections::HashMap;
9
10use crate::error::{Error, Result};
11use crate::layout::slot_size_align;
12use crate::schema::{Schema, Type};
13
14const MAX_RESOLVE_DEPTH: u32 = 128;
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum NumKind {
28 U8,
29 U16,
30 U32,
31 U64,
32 I8,
33 I16,
34 I32,
35 I64,
36 F32,
37 F64,
38}
39
40pub fn num_kind(ty: &Type) -> Option<NumKind> {
41 Some(match ty {
42 Type::U8 => NumKind::U8,
43 Type::U16 => NumKind::U16,
44 Type::U32 => NumKind::U32,
45 Type::U64 => NumKind::U64,
46 Type::I8 => NumKind::I8,
47 Type::I16 => NumKind::I16,
48 Type::I32 => NumKind::I32,
49 Type::I64 => NumKind::I64,
50 Type::F32 => NumKind::F32,
51 Type::F64 => NumKind::F64,
52 _ => return None,
53 })
54}
55
56fn widenable(from: NumKind, to: NumKind) -> bool {
59 use NumKind::*;
60 if from == to {
61 return true;
62 }
63 matches!(
64 (from, to),
65 (U8, U16)
66 | (U8, U32)
67 | (U8, U64)
68 | (U16, U32)
69 | (U16, U64)
70 | (U32, U64)
71 | (I8, I16)
72 | (I8, I32)
73 | (I8, I64)
74 | (I16, I32)
75 | (I16, I64)
76 | (I32, I64)
77 | (F32, F64)
78 )
79}
80
81#[derive(Clone, Debug)]
83pub enum Load {
84 Bool,
85 Num {
86 from: NumKind,
87 to: NumKind,
88 },
89 Enum,
90 Str,
91 Bytes,
92 Struct(usize),
95 List(Box<ElemPlan>),
96 Map(Box<MapPlan>),
97 Union(Box<UnionPlan>),
98}
99
100#[derive(Clone, Debug)]
104pub struct UnionPlan {
105 pub variants: Vec<VariantPlan>,
106}
107
108#[derive(Clone, Debug)]
109pub struct VariantPlan {
110 pub load: Load,
111 pub payload_off: u32,
112}
113
114#[derive(Clone, Debug)]
118pub struct MapPlan {
119 pub key: Load,
120 pub value: Load,
121 pub stride: u32,
122 pub align: u32,
123 pub key_off: u32,
124 pub value_off: u32,
125}
126
127#[derive(Clone, Debug)]
128pub struct ElemPlan {
129 pub load: Load,
130 pub stride: u32,
132 pub align: u32,
133 pub struct_inline: bool,
138}
139
140#[derive(Clone, Debug)]
141pub enum FieldSource {
142 Absent,
144 Slot {
146 offset: u32,
148 presence_byte: u32,
151 presence_mask: u8,
152 load: Load,
153 },
154 Packed {
158 writer_pos: u32,
160 load: Load,
161 },
162}
163
164#[derive(Clone, Debug)]
165pub struct FieldPlan {
166 pub id: u16,
167 pub source: FieldSource,
168}
169
170#[derive(Clone, Debug)]
171pub struct StructPlan {
172 pub reader_type: u16,
174 pub writer_type: u16,
176 pub writer_packed: bool,
178 pub fields: Vec<FieldPlan>,
180}
181
182#[derive(Clone, Debug)]
183pub struct Resolver {
184 writer: Schema,
185 reader: Schema,
186 plans: Vec<StructPlan>,
187 root_plan: usize,
188}
189
190impl Resolver {
191 pub fn new(writer: &Schema, reader: &Schema) -> Result<Resolver> {
196 let mut b = PlanBuilder {
197 writer,
198 reader,
199 map: HashMap::new(),
200 plans: Vec::new(),
201 };
202 let root_plan = b.pair(writer.root_index(), reader.root_index(), 0)?;
203 Ok(Resolver {
204 writer: writer.clone(),
205 reader: reader.clone(),
206 plans: b.plans,
207 root_plan,
208 })
209 }
210
211 pub fn identity(schema: &Schema) -> Result<Resolver> {
213 Resolver::new(schema, schema)
214 }
215
216 pub fn writer_id(&self) -> u128 {
217 self.writer.id()
218 }
219
220 pub fn writer_schema(&self) -> &Schema {
221 &self.writer
222 }
223
224 pub fn reader_schema(&self) -> &Schema {
225 &self.reader
226 }
227
228 pub(crate) fn plan(&self, index: usize) -> &StructPlan {
229 &self.plans[index]
230 }
231
232 pub(crate) fn root_plan_index(&self) -> usize {
233 self.root_plan
234 }
235}
236
237struct PlanBuilder<'a> {
238 writer: &'a Schema,
239 reader: &'a Schema,
240 map: HashMap<(u16, u16), usize>,
241 plans: Vec<StructPlan>,
242}
243
244impl<'a> PlanBuilder<'a> {
245 fn pair(&mut self, writer_idx: u16, reader_idx: u16, depth: u32) -> Result<usize> {
246 if depth > MAX_RESOLVE_DEPTH {
247 return Err(Error::DepthLimitExceeded);
248 }
249 if let Some(&i) = self.map.get(&(writer_idx, reader_idx)) {
250 return Ok(i);
251 }
252 let plan_idx = self.plans.len();
254 let writer_packed = self.writer.struct_def_unchecked(writer_idx).is_packed();
255 self.plans.push(StructPlan {
256 reader_type: reader_idx,
257 writer_type: writer_idx,
258 writer_packed,
259 fields: Vec::new(),
260 });
261 self.map.insert((writer_idx, reader_idx), plan_idx);
262
263 let ws = self.writer.struct_def_unchecked(writer_idx);
264 let rs = self.reader.struct_def_unchecked(reader_idx);
265 let mut fields = Vec::with_capacity(rs.fields.len());
266 for rf in &rs.fields {
267 let source = match ws.fields.binary_search_by_key(&rf.id, |f| f.id) {
268 Err(_) => FieldSource::Absent,
269 Ok(wpos) => {
270 let wf = &ws.fields[wpos];
271 let load = self
272 .compat(&wf.ty, &rf.ty, depth + 1)
273 .map_err(|e| match e {
274 Error::Incompatible(msg) => Error::Incompatible(format!(
275 "field {} (id {}): {msg}",
276 rf.name, rf.id
277 )),
278 other => other,
279 })?;
280 if writer_packed {
281 FieldSource::Packed {
282 writer_pos: wpos as u32,
283 load,
284 }
285 } else {
286 let wlay = self.writer.layout_unchecked(writer_idx).as_fixed();
287 FieldSource::Slot {
288 offset: wlay.slots[wpos],
289 presence_byte: if ws.is_dense() { 0 } else { wpos as u32 / 8 },
290 presence_mask: if ws.is_dense() { 0 } else { 1 << (wpos % 8) },
291 load,
292 }
293 }
294 }
295 };
296 fields.push(FieldPlan { id: rf.id, source });
297 }
298 self.plans[plan_idx].fields = fields;
299 Ok(plan_idx)
300 }
301
302 fn compat(&mut self, w: &Type, r: &Type, depth: u32) -> Result<Load> {
303 if depth > MAX_RESOLVE_DEPTH {
304 return Err(Error::DepthLimitExceeded);
305 }
306 if let (Some(from), Some(to)) = (num_kind(w), num_kind(r)) {
307 return if widenable(from, to) {
308 Ok(Load::Num { from, to })
309 } else {
310 Err(Error::Incompatible(format!(
311 "cannot read writer {} as reader {} (only lossless widening is allowed)",
312 w.describe(self.writer),
313 r.describe(self.reader)
314 )))
315 };
316 }
317 match (w, r) {
318 (Type::Bool, Type::Bool) => Ok(Load::Bool),
319 (Type::String, Type::String) => Ok(Load::Str),
320 (Type::Bytes, Type::Bytes) => Ok(Load::Bytes),
321 (Type::Enum(_), Type::Enum(_)) => Ok(Load::Enum),
323 (Type::Struct(wi), Type::Struct(ri)) => {
324 Ok(Load::Struct(self.pair(*wi, *ri, depth + 1)?))
325 }
326 (Type::List(we), Type::List(re)) => {
327 let load = self.compat(we, re, depth + 1)?;
328 let (stride, align, struct_inline) = writer_elem_stride_align(self.writer, we);
329 Ok(Load::List(Box::new(ElemPlan {
330 load,
331 stride,
332 align,
333 struct_inline,
334 })))
335 }
336 (Type::Map(wk, wv), Type::Map(rk, rv)) => {
337 if wk != rk {
340 return Err(Error::Incompatible(format!(
341 "map key type changed: writer {} vs reader {}",
342 wk.describe(self.writer),
343 rk.describe(self.reader)
344 )));
345 }
346 let key = self.compat(wk, rk, depth + 1)?;
347 let value = self.compat(wv, rv, depth + 1)?;
348 let lay = crate::layout::map_entry_layout(wk, wv);
349 Ok(Load::Map(Box::new(MapPlan {
350 key,
351 value,
352 stride: lay.size,
353 align: lay.align,
354 key_off: lay.slots[0],
355 value_off: lay.slots[1],
356 })))
357 }
358 (Type::Union(wv), Type::Union(rv)) => {
359 if wv.len() != rv.len() {
363 return Err(Error::Incompatible(format!(
364 "union variant count changed: writer {} vs reader {}",
365 wv.len(),
366 rv.len()
367 )));
368 }
369 let mut variants = Vec::with_capacity(wv.len());
370 for (w, r) in wv.iter().zip(rv) {
371 variants.push(VariantPlan {
372 load: self.compat(w, r, depth + 1)?,
373 payload_off: crate::layout::union_payload_offset(w),
374 });
375 }
376 Ok(Load::Union(Box::new(UnionPlan { variants })))
377 }
378 _ => Err(Error::Incompatible(format!(
379 "writer {} vs reader {}",
380 w.describe(self.writer),
381 r.describe(self.reader)
382 ))),
383 }
384 }
385}
386
387fn writer_elem_stride_align(writer: &Schema, elem: &Type) -> (u32, u32, bool) {
390 match elem {
391 Type::Struct(i) => match writer.layout_unchecked(*i) {
392 crate::layout::StructLayout::Fixed(f) => (f.size, f.align, true),
393 crate::layout::StructLayout::Packed(_) => (4, 4, false),
395 },
396 Type::String | Type::Bytes | Type::List(_) => (4, 4, false),
397 other => {
398 let (s, a) = slot_size_align(other);
399 (s, a, false)
400 }
401 }
402}