1#![allow(missing_docs)]
14
15use crate::canonical::{prepare, Prepared};
16use crate::error::{Error, Result};
17use crate::ir::{
18 AnnotationKind, Constraint, InfinityModule, Inode, InterpPart, PatchOp, ProvValue, Provenance,
19 TypeSpec,
20};
21use crate::layout::{
22 align8, dialect_hash, EncodeOpts, Header, Section, SectionSpan, ANN_KIND_AI_IMMUTABLE,
23 ANN_KIND_CONSTRAINT, ANN_KIND_INTENT, AUX_CONSTRAINT_CUSTOM, AUX_CONSTRAINT_ENUM_ITEM,
24 AUX_CONSTRAINT_MAX, AUX_CONSTRAINT_MIN, AUX_CONSTRAINT_PATTERN, AUX_PATCH_PATH,
25 AUX_PATCH_RENAME_TO, AUX_PATCH_VALUE, AUX_SCHEMA_DEFAULT, AUX_SCHEMA_DEPRECATED,
26 AUX_SCHEMA_MAX, AUX_SCHEMA_MAX_LEN, AUX_SCHEMA_MIN, AUX_SCHEMA_MIN_LEN, AUX_SCHEMA_PATTERN,
27 AUX_TYPE_ENUM_ITEM, AUX_TYPE_TABLE, CONSTRAINT_KIND_CUSTOM, CONSTRAINT_KIND_ENUM,
28 CONSTRAINT_KIND_MIN_MAX, CONSTRAINT_KIND_ODD, CONSTRAINT_KIND_PATTERN, EXT_DIR_SIZE,
29 FLAG_ANNOTATIONS, FLAG_CANONICAL, FLAG_EXTENSIONS, FLAG_PROVENANCE, FLAG_SECRETS,
30 FLAG_STREAMED, FORMAT_MAJOR, FORMAT_MINOR, HEADER_SIZE, NODE_FLAG_0, NODE_FLAG_1,
31 NODE_RECORD_SIZE, PART_EXPR, PART_LITERAL, PATCH_KIND_REMOVE, PATCH_KIND_RENAME,
32 PATCH_KIND_SET, PROV_HEADER_SIZE,
33};
34
35#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
37pub(crate) struct NodeRec {
38 pub kind: u8,
39 pub flags: u8,
40 pub aux: u32,
42 pub a: u64,
43 pub b: u64,
44 pub c: u64,
45}
46
47impl NodeRec {
48 pub(crate) fn to_bytes(self) -> [u8; NODE_RECORD_SIZE] {
49 let mut out = [0u8; NODE_RECORD_SIZE];
50 out[0] = self.kind;
51 out[1] = self.flags;
52 out[2..4].copy_from_slice(&0u16.to_le_bytes());
53 out[4..8].copy_from_slice(&self.aux.to_le_bytes());
54 out[8..16].copy_from_slice(&self.a.to_le_bytes());
55 out[16..24].copy_from_slice(&self.b.to_le_bytes());
56 out[24..32].copy_from_slice(&self.c.to_le_bytes());
57 out
58 }
59}
60
61struct NodeTables {
62 records: Vec<NodeRec>,
63 children: Vec<u32>,
64 fields: Vec<(u32, u32)>,
65 parts: Vec<(u32, u32)>,
66 aux: Vec<(u32, u32)>,
67}
68
69pub fn encode(module: &InfinityModule) -> Result<Vec<u8>> {
71 encode_with(module, EncodeOpts::default())
72}
73
74pub fn encode_with(module: &InfinityModule, opts: EncodeOpts) -> Result<Vec<u8>> {
76 let prepared = prepare(module)?;
77 encode_prepared(&prepared, opts)
78}
79
80pub(crate) fn encode_prepared(prepared: &Prepared, opts: EncodeOpts) -> Result<Vec<u8>> {
82 let mut header = Header {
83 major: FORMAT_MAJOR,
84 minor: FORMAT_MINOR,
85 flags: 0,
86 header_size: HEADER_SIZE as u16,
87 content_hash: [0u8; 32],
88 dialect_hash: dialect_hash(),
89 total_size: 0,
90 sections: [SectionSpan::default(); crate::layout::SECTION_COUNT],
91 };
92 if opts.mark_canonical {
93 header.flags |= FLAG_CANONICAL;
94 }
95 if opts.streamed {
96 header.flags |= FLAG_STREAMED;
97 }
98 if !prepared.annotations.is_empty() {
99 header.flags |= FLAG_ANNOTATIONS;
100 }
101 if prepared.provenance.is_some() {
102 header.flags |= FLAG_PROVENANCE;
103 }
104 if !prepared.extensions.is_empty() {
105 header.flags |= FLAG_EXTENSIONS;
106 }
107 if !prepared.secrets.is_empty() {
108 header.flags |= FLAG_SECRETS;
109 }
110
111 let tables = build_node_tables(prepared)?;
112
113 let mut image: Vec<u8> =
114 Vec::with_capacity(HEADER_SIZE + prepared.nodes.len() * NODE_RECORD_SIZE);
115 image.resize(HEADER_SIZE, 0);
116
117 header.span_mut(Section::Nodes).offset = image.len() as u32;
119 header.span_mut(Section::Nodes).count = prepared.nodes.len() as u32;
120 for record in &tables.records {
121 image.extend_from_slice(&record.to_bytes());
122 }
123 pad(&mut image);
124
125 set_span(
127 &mut header,
128 &image,
129 Section::Children,
130 tables.children.len(),
131 );
132 for child in &tables.children {
133 image.extend_from_slice(&child.to_le_bytes());
134 }
135 pad(&mut image);
136
137 set_span(&mut header, &image, Section::Fields, tables.fields.len());
139 for (name, node) in &tables.fields {
140 image.extend_from_slice(&name.to_le_bytes());
141 image.extend_from_slice(&0u32.to_le_bytes());
142 image.extend_from_slice(&node.to_le_bytes());
143 image.extend_from_slice(&0u32.to_le_bytes());
144 }
145 pad(&mut image);
146
147 set_span(&mut header, &image, Section::Parts, tables.parts.len());
149 for (tag, value) in &tables.parts {
150 image.extend_from_slice(&tag.to_le_bytes());
151 image.extend_from_slice(&value.to_le_bytes());
152 }
153 pad(&mut image);
154
155 let edges = derive_edges(prepared);
157 set_span(&mut header, &image, Section::Edges, edges.len());
158 for (from, to) in &edges {
159 image.extend_from_slice(&from.to_le_bytes());
160 image.extend_from_slice(&to.to_le_bytes());
161 }
162 pad(&mut image);
163
164 header.span_mut(Section::Strings).offset = image.len() as u32;
166 header.span_mut(Section::Strings).count = prepared.strings.len() as u32;
167 if !prepared.strings.is_empty() {
168 let mut heap_len = 0u32;
169 for string in &prepared.strings {
170 image.extend_from_slice(&heap_len.to_le_bytes());
171 heap_len = heap_len
172 .checked_add(string.len() as u32)
173 .ok_or(Error::TooLarge)?;
174 }
175 image.extend_from_slice(&heap_len.to_le_bytes());
176 for string in &prepared.strings {
177 image.extend_from_slice(string.as_bytes());
178 }
179 }
180 pad(&mut image);
181
182 header.span_mut(Section::Blobs).offset = image.len() as u32;
184 header.span_mut(Section::Blobs).count = prepared.blobs.len() as u32;
185 let mut blob_heap_len = 0u32;
186 for blob in &prepared.blobs {
187 let len = blob.payload.as_ref().map(|p| p.len()).unwrap_or(0) as u32;
188 image.extend_from_slice(&blob.hash);
189 image.extend_from_slice(&blob_heap_len.to_le_bytes());
190 image.extend_from_slice(&len.to_le_bytes());
191 blob_heap_len = blob_heap_len.checked_add(len).ok_or(Error::TooLarge)?;
192 }
193 for blob in &prepared.blobs {
194 if let Some(payload) = &blob.payload {
195 image.extend_from_slice(payload);
196 }
197 }
198 pad(&mut image);
199
200 set_span(
202 &mut header,
203 &image,
204 Section::Annotations,
205 prepared.annotations.len(),
206 );
207 for annotation in &prepared.annotations {
208 let (kind, a, b) = match &annotation.kind {
209 AnnotationKind::Intent(text) => (
210 ANN_KIND_INTENT,
211 *prepared
212 .string_index
213 .get(text)
214 .ok_or(Error::Encode("annotation string missing"))?,
215 0,
216 ),
217 AnnotationKind::Constraint { source, expr } => (
218 ANN_KIND_CONSTRAINT,
219 *prepared
220 .string_index
221 .get(source)
222 .ok_or(Error::Encode("constraint source missing"))?,
223 expr.0,
224 ),
225 AnnotationKind::AiImmutable(flag) => (ANN_KIND_AI_IMMUTABLE, u32::from(*flag), 0),
226 };
227 image.extend_from_slice(&annotation.node.0.to_le_bytes());
228 image.extend_from_slice(&kind.to_le_bytes());
229 image.extend_from_slice(&a.to_le_bytes());
230 image.extend_from_slice(&b.to_le_bytes());
231 }
232 pad(&mut image);
233
234 header.span_mut(Section::Provenance).offset = image.len() as u32;
236 if let Some(prov) = &prepared.provenance {
237 let block = encode_provenance(prov, prepared)?;
238 header.span_mut(Section::Provenance).count = block.len() as u32;
239 image.extend_from_slice(&block);
240 } else {
241 header.span_mut(Section::Provenance).count = 0;
242 }
243 pad(&mut image);
244
245 header.span_mut(Section::Extensions).offset = image.len() as u32;
247 header.span_mut(Section::Extensions).count = prepared.extensions.len() as u32;
248 encode_extensions(&prepared.extensions, image.len(), &mut image)?;
249 pad(&mut image);
250
251 set_span(&mut header, &image, Section::Units, prepared.units.len());
253 for unit in &prepared.units {
254 image.extend_from_slice(&unit.discriminant().to_le_bytes());
255 let name = if unit.is_builtin() {
256 0
257 } else {
258 *prepared
259 .string_index
260 .get(unit.name())
261 .ok_or(Error::Encode("unit name missing from heap"))?
262 };
263 image.extend_from_slice(&name.to_le_bytes());
264 }
265 pad(&mut image);
266
267 set_span(&mut header, &image, Section::Types, prepared.types.len());
269 let mut type_aux_start = vec![0u32; prepared.types.len()];
270 let mut type_aux_count = vec![0u32; prepared.types.len()];
271 {
272 let mut offset = tables.aux.len() as u32;
273 for (index, prepared_type) in prepared.types.iter().enumerate() {
274 type_aux_start[index] = offset;
275 type_aux_count[index] = prepared_type.aux.len() as u32;
276 offset = offset
277 .checked_add(prepared_type.aux.len() as u32)
278 .ok_or(Error::TooLarge)?;
279 }
280 }
281 for (index, prepared_type) in prepared.types.iter().enumerate() {
282 let (code, flags) = type_code(&prepared_type.spec)?;
283 image.extend_from_slice(&code.to_le_bytes());
284 image.extend_from_slice(&flags.to_le_bytes());
285 image.extend_from_slice(&type_aux_start[index].to_le_bytes());
286 image.extend_from_slice(&type_aux_count[index].to_le_bytes());
287 }
288 pad(&mut image);
289
290 let aux_count = tables.aux.len() + prepared.types.iter().map(|t| t.aux.len()).sum::<usize>();
292 set_span(&mut header, &image, Section::Aux, aux_count);
293 for (tag, value) in &tables.aux {
294 image.extend_from_slice(&tag.to_le_bytes());
295 image.extend_from_slice(&value.to_le_bytes());
296 }
297 for prepared_type in &prepared.types {
298 for (tag, value) in &prepared_type.aux {
299 image.extend_from_slice(&tag.to_le_bytes());
300 image.extend_from_slice(&value.to_le_bytes());
301 }
302 }
303 pad(&mut image);
304
305 set_span(
307 &mut header,
308 &image,
309 Section::Numbers,
310 prepared.numbers.len(),
311 );
312 for value in &prepared.numbers {
313 image.extend_from_slice(&value.to_le_bytes());
314 }
315 pad(&mut image);
316
317 header.span_mut(Section::Secrets).offset = image.len() as u32;
319 header.span_mut(Section::Secrets).count = prepared.secrets.len() as u32;
320 let mut secret_heap = 0u32;
321 for secret in &prepared.secrets {
322 image.extend_from_slice(&secret_heap.to_le_bytes());
323 image.extend_from_slice(&(secret.len() as u32).to_le_bytes());
324 secret_heap = secret_heap
325 .checked_add(secret.len() as u32)
326 .ok_or(Error::TooLarge)?;
327 }
328 for secret in &prepared.secrets {
329 image.extend_from_slice(secret);
330 }
331 pad(&mut image);
332
333 set_span(&mut header, &image, Section::Hashes, prepared.hashes.len());
335 for hash in &prepared.hashes {
336 image.extend_from_slice(hash);
337 }
338 pad(&mut image);
339
340 header.span_mut(Section::Bindings).offset = image.len() as u32;
342 header.span_mut(Section::Bindings).count = prepared.bindings.len() as u32 + 1;
343 image.extend_from_slice(
344 &prepared
345 .root
346 .map(|root| root.0)
347 .unwrap_or(u32::MAX)
348 .to_le_bytes(),
349 );
350 image.extend_from_slice(&0u32.to_le_bytes());
351 for (name, id) in &prepared.bindings {
352 let sid = *prepared
353 .string_index
354 .get(name)
355 .ok_or(Error::Encode("binding name missing from heap"))?;
356 image.extend_from_slice(&sid.to_le_bytes());
357 image.extend_from_slice(&id.0.to_le_bytes());
358 }
359 pad(&mut image);
360
361 header.total_size = image.len() as u64;
362 header.write(&mut image);
363
364 if opts.write_hash {
365 let hash = hash_image(&image)?;
366 image[16..48].copy_from_slice(&hash);
367 let _ = hash;
368 }
369 Ok(image)
370}
371
372fn set_span(header: &mut Header, image: &[u8], section: Section, count: usize) {
373 header.span_mut(section).offset = image.len() as u32;
374 header.span_mut(section).count = count as u32;
375}
376
377fn pad(image: &mut Vec<u8>) {
378 while image.len() % 8 != 0 {
379 image.push(0);
380 }
381}
382
383pub fn hash_image(image: &[u8]) -> Result<[u8; 32]> {
386 if image.len() < HEADER_SIZE {
387 return Err(Error::HeaderTruncated);
388 }
389 let mut hasher = blake3::Hasher::new();
390 hasher.update(&image[..16]);
391 hasher.update(&[0u8; 32]);
392 hasher.update(&image[48..]);
393 Ok(*hasher.finalize().as_bytes())
394}
395
396fn build_node_tables(prepared: &Prepared) -> Result<NodeTables> {
397 let mut tables = NodeTables {
398 records: Vec::with_capacity(prepared.nodes.len()),
399 children: Vec::new(),
400 fields: Vec::new(),
401 parts: Vec::new(),
402 aux: Vec::new(),
403 };
404 let mut secret_cursor = 0u32;
405 for node in &prepared.nodes {
406 let string = |text: &str| -> Result<u32> {
407 prepared
408 .string_index
409 .get(text)
410 .copied()
411 .ok_or(Error::Encode("string missing from heap"))
412 };
413 let record = match node {
414 Inode::Null => NodeRec {
415 kind: 0,
416 ..NodeRec::default()
417 },
418 Inode::Bool(value) => NodeRec {
419 kind: 1,
420 a: u64::from(*value),
421 ..NodeRec::default()
422 },
423 Inode::Int(value) => NodeRec {
424 kind: 2,
425 a: *value as u64,
426 ..NodeRec::default()
427 },
428 Inode::Float(value) => NodeRec {
429 kind: 3,
430 a: crate::layout::canonical_f64(*value).to_bits(),
431 ..NodeRec::default()
432 },
433 Inode::Str(text) => NodeRec {
434 kind: 4,
435 aux: string(text)?,
436 ..NodeRec::default()
437 },
438 Inode::DurationMs(ms) => NodeRec {
439 kind: 5,
440 a: *ms,
441 ..NodeRec::default()
442 },
443 Inode::Quantity { value, unit } => NodeRec {
444 kind: 6,
445 aux: value.0,
446 a: *prepared
447 .unit_index
448 .get(unit)
449 .ok_or(Error::Encode("unit missing from table"))? as u64,
450 ..NodeRec::default()
451 },
452 Inode::Array { elements } => {
453 let start = tables.children.len() as u32;
454 tables.children.extend(elements.iter().map(|id| id.0));
455 NodeRec {
456 kind: 7,
457 a: u64::from(start),
458 b: elements.len() as u64,
459 ..NodeRec::default()
460 }
461 }
462 Inode::Table { fields } => {
463 let start = tables.fields.len() as u32;
464 for (name, id) in fields {
465 tables.fields.push((string(name)?, id.0));
466 }
467 NodeRec {
468 kind: 8,
469 a: u64::from(start),
470 b: fields.len() as u64,
471 ..NodeRec::default()
472 }
473 }
474 Inode::Secret(_) => {
475 let index = secret_cursor;
476 secret_cursor += 1;
477 NodeRec {
478 kind: 9,
479 a: u64::from(index),
480 ..NodeRec::default()
481 }
482 }
483 Inode::SecretRef { locator } => NodeRec {
484 kind: 10,
485 a: u64::from(locator.0),
486 ..NodeRec::default()
487 },
488 Inode::Ref(id) => NodeRec {
489 kind: 11,
490 a: u64::from(id.0),
491 ..NodeRec::default()
492 },
493 Inode::Env { name, default } => NodeRec {
494 kind: 12,
495 aux: string(name)?,
496 a: default.map(|id| id.0 as u64).unwrap_or(u32::MAX as u64),
497 ..NodeRec::default()
498 },
499 Inode::Ident(name) => NodeRec {
500 kind: 13,
501 aux: string(name)?,
502 ..NodeRec::default()
503 },
504 Inode::Interpolate { parts } => {
505 let start = tables.parts.len() as u32;
506 for part in parts {
507 match part {
508 InterpPart::Literal(text) => {
509 tables.parts.push((PART_LITERAL, string(text)?));
510 }
511 InterpPart::Expr(id) => {
512 tables.parts.push((PART_EXPR, id.0));
513 }
514 }
515 }
516 NodeRec {
517 kind: 14,
518 a: u64::from(start),
519 b: parts.len() as u64,
520 ..NodeRec::default()
521 }
522 }
523 Inode::Binary { op, lhs, rhs } => NodeRec {
524 kind: 15,
525 aux: op.code(),
526 a: u64::from(lhs.0),
527 b: u64::from(rhs.0),
528 ..NodeRec::default()
529 },
530 Inode::Unary { op, expr } => NodeRec {
531 kind: 16,
532 aux: op.code(),
533 a: u64::from(expr.0),
534 ..NodeRec::default()
535 },
536 Inode::Constrained { target, constraint } => {
537 let start = tables.aux.len() as u32;
538 let kind = match constraint {
539 Constraint::MinMax { min, max } => {
540 if let Some(min) = min {
541 tables.aux.push((
542 AUX_CONSTRAINT_MIN,
543 *prepared
544 .number_index
545 .get(min)
546 .ok_or(Error::Encode("constraint min missing"))?,
547 ));
548 }
549 if let Some(max) = max {
550 tables.aux.push((
551 AUX_CONSTRAINT_MAX,
552 *prepared
553 .number_index
554 .get(max)
555 .ok_or(Error::Encode("constraint max missing"))?,
556 ));
557 }
558 CONSTRAINT_KIND_MIN_MAX
559 }
560 Constraint::Enum(items) => {
561 for item in items {
562 tables.aux.push((AUX_CONSTRAINT_ENUM_ITEM, string(item)?));
563 }
564 CONSTRAINT_KIND_ENUM
565 }
566 Constraint::Pattern(pattern) => {
567 tables.aux.push((AUX_CONSTRAINT_PATTERN, string(pattern)?));
568 CONSTRAINT_KIND_PATTERN
569 }
570 Constraint::Odd => CONSTRAINT_KIND_ODD,
571 Constraint::Custom(text) => {
572 tables.aux.push((AUX_CONSTRAINT_CUSTOM, string(text)?));
573 CONSTRAINT_KIND_CUSTOM
574 }
575 };
576 NodeRec {
577 kind: 17,
578 aux: kind,
579 a: u64::from(target.0),
580 b: u64::from(start),
581 c: (tables.aux.len() as u32 - start) as u64,
582 ..NodeRec::default()
583 }
584 }
585 Inode::Patch(op) => {
586 let start = tables.aux.len() as u32;
587 let kind = match op {
588 PatchOp::Set { path, value } => {
589 tables.aux.push((AUX_PATCH_PATH, string(path)?));
590 tables.aux.push((AUX_PATCH_VALUE, value.0));
591 PATCH_KIND_SET
592 }
593 PatchOp::Remove { path } => {
594 tables.aux.push((AUX_PATCH_PATH, string(path)?));
595 PATCH_KIND_REMOVE
596 }
597 PatchOp::Rename { from, to } => {
598 tables.aux.push((AUX_PATCH_PATH, string(from)?));
599 tables.aux.push((AUX_PATCH_RENAME_TO, string(to)?));
600 PATCH_KIND_RENAME
601 }
602 };
603 NodeRec {
604 kind: 18,
605 aux: kind,
606 b: u64::from(start),
607 c: (tables.aux.len() as u32 - start) as u64,
608 ..NodeRec::default()
609 }
610 }
611 Inode::SchemaField(spec) => {
612 let (code, payload) = type_payload(&spec.typ, prepared)?;
613 let type_id = *prepared
614 .type_index
615 .get(&(code, payload))
616 .ok_or(Error::Encode("schema type missing from table"))?;
617 let start = tables.aux.len() as u32;
618 for (tag, node) in [
619 (AUX_SCHEMA_MIN, spec.min),
620 (AUX_SCHEMA_MAX, spec.max),
621 (AUX_SCHEMA_MIN_LEN, spec.min_len),
622 (AUX_SCHEMA_MAX_LEN, spec.max_len),
623 (AUX_SCHEMA_PATTERN, spec.pattern),
624 (AUX_SCHEMA_DEFAULT, spec.default),
625 ] {
626 if let Some(id) = node {
627 tables.aux.push((tag, id.0));
628 }
629 }
630 if let Some(deprecated) = &spec.deprecated {
631 tables
632 .aux
633 .push((AUX_SCHEMA_DEPRECATED, string(deprecated)?));
634 }
635 NodeRec {
636 kind: 19,
637 aux: type_id,
638 b: u64::from(start),
639 c: (tables.aux.len() as u32 - start) as u64,
640 ..NodeRec::default()
641 }
642 }
643 Inode::SchemaTable { fields, closed } => {
644 let start = tables.fields.len() as u32;
645 for (name, id) in fields {
646 tables.fields.push((string(name)?, id.0));
647 }
648 NodeRec {
649 kind: 20,
650 flags: if *closed { NODE_FLAG_0 } else { 0 },
651 a: u64::from(start),
652 b: fields.len() as u64,
653 ..NodeRec::default()
654 }
655 }
656 Inode::SchemaArray { element } => NodeRec {
657 kind: 21,
658 a: u64::from(element.0),
659 ..NodeRec::default()
660 },
661 Inode::Semantic { tag, inner } => NodeRec {
662 kind: 22,
663 aux: tag.code(),
664 a: u64::from(inner.0),
665 ..NodeRec::default()
666 },
667 Inode::Blob {
668 hash,
669 size,
670 tainted,
671 media_type,
672 payload,
673 } => {
674 let key = (*hash, *size, if *tainted { None } else { payload.clone() });
675 let index = *prepared
676 .blob_index
677 .get(&key)
678 .ok_or(Error::Encode("blob missing from table"))?;
679 let mut flags = 0u8;
680 if *tainted {
681 flags |= NODE_FLAG_0;
682 }
683 if payload.is_some() {
684 flags |= NODE_FLAG_1;
685 }
686 NodeRec {
687 kind: 23,
688 flags,
689 aux: string(media_type)?,
690 a: u64::from(index),
691 b: *size,
692 ..NodeRec::default()
693 }
694 }
695 Inode::ContentAddressed { media_type, data } => NodeRec {
696 kind: 24,
697 a: u64::from(media_type.0),
698 b: u64::from(data.0),
699 ..NodeRec::default()
700 },
701 };
702 tables.records.push(record);
703 }
704 Ok(tables)
705}
706
707fn type_payload(spec: &TypeSpec, prepared: &Prepared) -> Result<(u32, Vec<(u32, u32)>)> {
708 let string = |text: &str| -> Result<u32> {
709 prepared
710 .string_index
711 .get(text)
712 .copied()
713 .ok_or(Error::Encode("type string missing from heap"))
714 };
715 let mut aux = Vec::new();
716 match spec {
717 TypeSpec::Enum(items) => {
718 for item in items {
719 aux.push((AUX_TYPE_ENUM_ITEM, string(item)?));
720 }
721 }
722 TypeSpec::Table { closed } => aux.push((AUX_TYPE_TABLE, u32::from(*closed))),
723 _ => {}
724 }
725 Ok((spec.code(), aux))
726}
727
728fn type_code(spec: &TypeSpec) -> Result<(u32, u32)> {
729 let flags = match spec {
730 TypeSpec::Table { closed } => u32::from(*closed),
731 _ => 0,
732 };
733 Ok((spec.code(), flags))
734}
735
736fn derive_edges(prepared: &Prepared) -> Vec<(u32, u32)> {
737 let mut edges = Vec::new();
738 for (index, node) in prepared.nodes.iter().enumerate() {
739 let dependent = index as u32;
740 for child in node.children() {
741 edges.push((child.0, dependent));
742 }
743 }
744 edges.sort_unstable();
745 edges.dedup();
746 edges
747}
748
749fn encode_provenance(prov: &Provenance, prepared: &Prepared) -> Result<Vec<u8>> {
750 let mut block = vec![0u8; PROV_HEADER_SIZE];
751 block[0..4].copy_from_slice(&1u32.to_le_bytes());
752 block[4..8].copy_from_slice(&(prov.entries.len() as u32).to_le_bytes());
753 block[8..16].copy_from_slice(&prov.timestamp_ms.to_le_bytes());
754 let sid = |text: &Option<String>| -> Result<u32> {
755 match text {
756 Some(text) => Ok(*prepared
757 .string_index
758 .get(text)
759 .ok_or(Error::Encode("provenance string missing"))?),
760 None => Ok(u32::MAX),
761 }
762 };
763 block[16..20].copy_from_slice(&sid(&prov.producer)?.to_le_bytes());
764 block[20..24].copy_from_slice(&sid(&prov.model)?.to_le_bytes());
765 block[24..28].copy_from_slice(&sid(&prov.tool)?.to_le_bytes());
766 let mut flags = 0u32;
767 if prov.parent.is_some() {
768 flags |= 1;
769 }
770 block[28..32].copy_from_slice(&flags.to_le_bytes());
771 if let Some(parent) = prov.parent {
772 block[32..64].copy_from_slice(&parent);
773 }
774 for entry in &prov.entries {
775 let key = *prepared
776 .string_index
777 .get(&entry.key)
778 .ok_or(Error::Encode("provenance key missing"))?;
779 let (kind, a) = match &entry.value {
780 ProvValue::Text(text) => (
781 0u32,
782 *prepared
783 .string_index
784 .get(text)
785 .ok_or(Error::Encode("provenance text missing"))?,
786 ),
787 ProvValue::Int(value) => (
788 1u32,
789 *prepared
790 .number_index
791 .get(value)
792 .ok_or(Error::Encode("provenance int missing"))?,
793 ),
794 ProvValue::Flag(flag) => (2u32, u32::from(*flag)),
795 ProvValue::Hash(hash) => (
796 3u32,
797 *prepared
798 .hash_index
799 .get(hash)
800 .ok_or(Error::Encode("provenance hash missing"))?,
801 ),
802 };
803 block.extend_from_slice(&kind.to_le_bytes());
804 block.extend_from_slice(&key.to_le_bytes());
805 block.extend_from_slice(&a.to_le_bytes());
806 block.extend_from_slice(&0u32.to_le_bytes());
807 }
808 Ok(block)
809}
810
811fn encode_extensions(
812 extensions: &[(u64, Vec<u8>)],
813 start: usize,
814 image: &mut Vec<u8>,
815) -> Result<()> {
816 if extensions.is_empty() {
817 return Ok(());
818 }
819 let dir_size = EXT_DIR_SIZE * extensions.len();
820 let mut payload_offset = start + dir_size;
821 let mut directory = Vec::with_capacity(dir_size);
822 for (tag, payload) in extensions {
823 directory.extend_from_slice(&tag.to_le_bytes());
824 directory.extend_from_slice(&(payload_offset as u32).to_le_bytes());
825 directory.extend_from_slice(&(payload.len() as u32).to_le_bytes());
826 payload_offset = align8(payload_offset as u64 + payload.len() as u64) as usize;
827 }
828 image.extend_from_slice(&directory);
829 for (_, payload) in extensions {
830 image.extend_from_slice(payload);
831 pad(image);
832 }
833 Ok(())
834}