1#![allow(missing_docs)]
34
35use std::collections::VecDeque;
36
37use crate::canonical::remap_node;
38use crate::encode::{encode_with, hash_image};
39use crate::error::{Error, Result};
40use crate::ir::{Annotation, AnnotationKind, InfinityModule, NodeId, Provenance};
41use crate::layout::{
42 align8, read_u32, read_u64, EncodeOpts, Section, HEADER_SIZE, SECTION_COUNT, STREAM_MAGIC,
43};
44use crate::validate::Validated;
45
46pub const STREAM_VERSION: u8 = 1;
48pub const FRAME_HEADER_SIZE: usize = 32;
50pub const DEFAULT_CHUNK: usize = 64 << 10;
52pub const MAX_FRAME_PAYLOAD: u32 = 64 << 20;
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum FrameKind {
58 Begin,
60 Batch,
62 Delta,
64 End,
66}
67
68impl FrameKind {
69 pub const fn code(self) -> u8 {
70 match self {
71 FrameKind::Begin => 1,
72 FrameKind::Batch => 2,
73 FrameKind::Delta => 3,
74 FrameKind::End => 4,
75 }
76 }
77
78 pub const fn from_code(code: u8) -> Option<Self> {
79 match code {
80 1 => Some(FrameKind::Begin),
81 2 => Some(FrameKind::Batch),
82 3 => Some(FrameKind::Delta),
83 4 => Some(FrameKind::End),
84 _ => None,
85 }
86 }
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct Frame {
92 pub kind: FrameKind,
93 pub sequence: u64,
94 pub payload: Vec<u8>,
95}
96
97impl Frame {
98 pub fn new(kind: FrameKind, sequence: u64, payload: Vec<u8>) -> Self {
99 Frame {
100 kind,
101 sequence,
102 payload,
103 }
104 }
105
106 pub fn to_bytes(&self) -> Vec<u8> {
108 let mut out = Vec::with_capacity(FRAME_HEADER_SIZE + self.payload.len());
109 out.extend_from_slice(&STREAM_MAGIC);
110 out.push(STREAM_VERSION);
111 out.push(self.kind.code());
112 out.extend_from_slice(&0u16.to_le_bytes());
113 out.extend_from_slice(&self.sequence.to_le_bytes());
114 out.extend_from_slice(&(self.payload.len() as u32).to_le_bytes());
115 out.extend_from_slice(&0u32.to_le_bytes());
116 let checksum = frame_checksum(&self.payload);
117 out.extend_from_slice(&checksum[..8]);
118 out.extend_from_slice(&self.payload);
119 out
120 }
121
122 pub fn parse(buf: &[u8]) -> Result<(Frame, usize)> {
124 if buf.len() < 4 || buf[0..4] != STREAM_MAGIC {
125 return Err(Error::StreamMagic);
126 }
127 if buf.len() < FRAME_HEADER_SIZE {
128 return Err(Error::Truncated {
129 needed: FRAME_HEADER_SIZE as u64,
130 found: buf.len() as u64,
131 });
132 }
133 let version = buf[4];
134 if version != STREAM_VERSION {
135 return Err(Error::StreamVersion(version));
136 }
137 let kind = FrameKind::from_code(buf[5]).ok_or(Error::StreamFrameKind(buf[5]))?;
138 let flags = u16::from_le_bytes([buf[6], buf[7]]);
139 if flags != 0 {
140 return Err(Error::UnknownFlags(flags));
141 }
142 let sequence = u64::from_le_bytes(buf[8..16].try_into().expect("8 bytes"));
143 let len = u32::from_le_bytes(buf[16..20].try_into().expect("4 bytes"));
144 if len > MAX_FRAME_PAYLOAD {
145 return Err(Error::StreamFrameLen {
146 declared: len,
147 available: buf.len() as u64,
148 });
149 }
150 let reserved = u32::from_le_bytes(buf[20..24].try_into().expect("4 bytes"));
151 if reserved != 0 {
152 return Err(Error::ReservedNonZero {
153 section: "stream-frame",
154 offset: 20,
155 });
156 }
157 let payload_start = FRAME_HEADER_SIZE;
158 let payload_end = payload_start + len as usize;
159 if payload_end > buf.len() {
160 return Err(Error::StreamFrameLen {
161 declared: len,
162 available: (buf.len() - payload_start) as u64,
163 });
164 }
165 let payload = &buf[payload_start..payload_end];
166 let expected = frame_checksum(payload);
167 if buf[24..32] != expected[..8] {
168 return Err(Error::HashMismatch);
169 }
170 Ok((
171 Frame {
172 kind,
173 sequence,
174 payload: payload.to_vec(),
175 },
176 payload_end,
177 ))
178 }
179}
180
181pub fn frame_checksum(payload: &[u8]) -> [u8; 32] {
183 *blake3::hash(payload).as_bytes()
184}
185
186pub fn encode_stream(module: &InfinityModule, chunk: usize) -> Result<Vec<Frame>> {
192 encode_stream_with(module, chunk, EncodeOpts::default())
193}
194
195pub fn encode_stream_with(
197 module: &InfinityModule,
198 chunk: usize,
199 opts: EncodeOpts,
200) -> Result<Vec<Frame>> {
201 if chunk == 0 {
202 return Err(Error::InvalidArgument("chunk size must be non-zero"));
203 }
204 let image = encode_with(module, opts)?;
205 let header = crate::layout::Header::parse(&image)?;
206 let mut frames = Vec::new();
207 let mut sequence = 0u64;
208 let mut begin = Vec::with_capacity(24);
209 begin.extend_from_slice(&header.major.to_le_bytes());
210 begin.extend_from_slice(&header.minor.to_le_bytes());
211 begin.extend_from_slice(&header.flags.to_le_bytes());
212 begin.extend_from_slice(&0u16.to_le_bytes());
213 begin.extend_from_slice(&header.dialect_hash.to_le_bytes());
214 begin.extend_from_slice(&header.total_size.to_le_bytes());
215 frames.push(Frame::new(FrameKind::Begin, sequence, begin));
216 sequence += 1;
217
218 for section in Section::ALL {
221 let span = header.span(section);
222 let start = span.offset as usize;
223 let len = section_body_len(&image, &header, section);
224 let body = &image[start..start + len];
225 if body.is_empty() {
226 let mut payload = Vec::with_capacity(9);
227 payload.push(section as u8);
228 payload.extend_from_slice(&span.count.to_le_bytes());
229 payload.extend_from_slice(&0u32.to_le_bytes());
230 frames.push(Frame::new(FrameKind::Batch, sequence, payload));
231 sequence += 1;
232 continue;
233 }
234 for piece in body.chunks(chunk) {
235 let mut payload = Vec::with_capacity(9 + piece.len());
236 payload.push(section as u8);
237 payload.extend_from_slice(&span.count.to_le_bytes());
238 payload.extend_from_slice(&(piece.len() as u32).to_le_bytes());
239 payload.extend_from_slice(piece);
240 frames.push(Frame::new(FrameKind::Batch, sequence, payload));
241 sequence += 1;
242 }
243 }
244
245 let mut end = Vec::with_capacity(40);
246 let hash = hash_image(&image)?;
247 end.extend_from_slice(&hash);
248 end.extend_from_slice(&(image.len() as u64).to_le_bytes());
249 frames.push(Frame::new(FrameKind::End, sequence, end));
250 Ok(frames)
251}
252
253fn section_body_len(image: &[u8], header: &crate::layout::Header, section: Section) -> usize {
254 let span = header.span(section);
255 let start = span.offset as usize;
256 let end = match section {
257 Section::Strings => {
258 if span.count == 0 {
259 start
260 } else {
261 let heap = read_u32(image, start + span.count as usize * 4).unwrap_or(0) as usize;
262 start + (span.count as usize + 1) * 4 + heap
263 }
264 }
265 Section::Blobs => {
266 let mut heap = 0usize;
267 for index in 0..span.count {
268 let len = read_u32(image, start + index as usize * 40 + 36).unwrap_or(0) as usize;
269 heap += len;
270 }
271 start + span.count as usize * 40 + heap
272 }
273 Section::Provenance => start + span.count as usize,
274 Section::Secrets => {
275 let mut heap = 0usize;
276 for index in 0..span.count {
277 let len = read_u32(image, start + index as usize * 8 + 4).unwrap_or(0) as usize;
278 heap += len;
279 }
280 start + span.count as usize * 8 + heap
281 }
282 Section::Extensions => {
283 let mut end = start + span.count as usize * 16;
284 for index in 0..span.count {
285 let dir = start + index as usize * 16;
286 let offset = read_u32(image, dir + 8).unwrap_or(0) as usize;
287 let len = read_u32(image, dir + 12).unwrap_or(0) as usize;
288 end = end.max(offset + len);
289 }
290 end
291 }
292 Section::Nodes => start + span.count as usize * 32,
293 Section::Children => start + span.count as usize * 4,
294 Section::Fields => start + span.count as usize * 16,
295 Section::Parts => start + span.count as usize * 8,
296 Section::Edges => start + span.count as usize * 8,
297 Section::Annotations => start + span.count as usize * 16,
298 Section::Units => start + span.count as usize * 8,
299 Section::Types => start + span.count as usize * 16,
300 Section::Aux => start + span.count as usize * 8,
301 Section::Numbers => start + span.count as usize * 8,
302 Section::Hashes => start + span.count as usize * 32,
303 Section::Bindings => start + span.count as usize * 8,
304 };
305 end.saturating_sub(start)
306}
307
308#[derive(Debug, Clone, PartialEq)]
310pub enum DeltaOp {
311 SetRoot(Option<NodeId>),
313 PutNode {
315 target: NodeId,
316 fragment: Box<InfinityModule>,
317 },
318 PushNode { fragment: Box<InfinityModule> },
320 SetBinding { name: String, node: NodeId },
322 RemoveBinding { name: String },
324 SetAnnotation { node: NodeId, kind: AnnotationKind },
326 ClearAnnotations,
328 SetProvenance(Option<Provenance>),
330}
331
332impl DeltaOp {
333 fn encode(&self, out: &mut Vec<u8>) -> Result<()> {
334 match self {
335 DeltaOp::SetRoot(root) => {
336 out.push(0);
337 out.extend_from_slice(&root.map(|id| id.0).unwrap_or(u32::MAX).to_le_bytes());
338 }
339 DeltaOp::PutNode { target, fragment } => {
340 out.push(1);
341 out.extend_from_slice(&target.0.to_le_bytes());
342 let image = encode_with(fragment, EncodeOpts::default())?;
343 out.extend_from_slice(&(image.len() as u32).to_le_bytes());
344 out.extend_from_slice(&image);
345 }
346 DeltaOp::PushNode { fragment } => {
347 out.push(2);
348 let image = encode_with(fragment, EncodeOpts::default())?;
349 out.extend_from_slice(&(image.len() as u32).to_le_bytes());
350 out.extend_from_slice(&image);
351 }
352 DeltaOp::SetBinding { name, node } => {
353 out.push(3);
354 let bytes = name.as_bytes();
355 out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
356 out.extend_from_slice(bytes);
357 out.extend_from_slice(&node.0.to_le_bytes());
358 }
359 DeltaOp::RemoveBinding { name } => {
360 out.push(4);
361 let bytes = name.as_bytes();
362 out.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
363 out.extend_from_slice(bytes);
364 }
365 DeltaOp::SetAnnotation { node, kind } => {
366 out.push(5);
367 out.extend_from_slice(&node.0.to_le_bytes());
368 match kind {
369 AnnotationKind::Intent(text) => {
370 out.push(0);
371 write_string(out, text)?;
372 }
373 AnnotationKind::Constraint { source, expr } => {
374 out.push(1);
375 write_string(out, source)?;
376 out.extend_from_slice(&expr.0.to_le_bytes());
377 }
378 AnnotationKind::AiImmutable(flag) => {
379 out.push(2);
380 out.push(u8::from(*flag));
381 }
382 }
383 }
384 DeltaOp::ClearAnnotations => out.push(6),
385 DeltaOp::SetProvenance(prov) => {
386 out.push(7);
387 match prov {
388 None => out.push(0),
389 Some(prov) => {
390 out.push(1);
391 write_opt_string(out, prov.producer.as_deref())?;
392 write_opt_string(out, prov.model.as_deref())?;
393 write_opt_string(out, prov.tool.as_deref())?;
394 out.extend_from_slice(&prov.timestamp_ms.to_le_bytes());
395 match prov.parent {
396 None => out.push(0),
397 Some(hash) => {
398 out.push(1);
399 out.extend_from_slice(&hash);
400 }
401 }
402 out.extend_from_slice(&(prov.entries.len() as u32).to_le_bytes());
403 for entry in &prov.entries {
404 write_string(out, &entry.key)?;
405 match &entry.value {
406 crate::ir::ProvValue::Text(text) => {
407 out.push(0);
408 write_string(out, text)?;
409 }
410 crate::ir::ProvValue::Int(value) => {
411 out.push(1);
412 out.extend_from_slice(&value.to_le_bytes());
413 }
414 crate::ir::ProvValue::Flag(flag) => {
415 out.push(2);
416 out.push(u8::from(*flag));
417 }
418 crate::ir::ProvValue::Hash(hash) => {
419 out.push(3);
420 out.extend_from_slice(hash);
421 }
422 }
423 }
424 }
425 }
426 }
427 }
428 Ok(())
429 }
430
431 fn decode(buf: &[u8], cursor: &mut usize) -> Result<DeltaOp> {
432 let tag = take_u8(buf, cursor)?;
433 let op = match tag {
434 0 => {
435 let id = take_u32(buf, cursor)?;
436 DeltaOp::SetRoot(if id == u32::MAX {
437 None
438 } else {
439 Some(NodeId(id))
440 })
441 }
442 1 => {
443 let target = NodeId(take_u32(buf, cursor)?);
444 let fragment = Box::new(decode_fragment(buf, cursor)?);
445 DeltaOp::PutNode { target, fragment }
446 }
447 2 => {
448 let fragment = Box::new(decode_fragment(buf, cursor)?);
449 DeltaOp::PushNode { fragment }
450 }
451 3 => {
452 let name = take_string(buf, cursor)?;
453 let node = NodeId(take_u32(buf, cursor)?);
454 DeltaOp::SetBinding { name, node }
455 }
456 4 => {
457 let name = take_string(buf, cursor)?;
458 DeltaOp::RemoveBinding { name }
459 }
460 5 => {
461 let node = NodeId(take_u32(buf, cursor)?);
462 let kind_tag = take_u8(buf, cursor)?;
463 let kind = match kind_tag {
464 0 => AnnotationKind::Intent(take_string(buf, cursor)?),
465 1 => {
466 let source = take_string(buf, cursor)?;
467 let expr = NodeId(take_u32(buf, cursor)?);
468 AnnotationKind::Constraint { source, expr }
469 }
470 2 => AnnotationKind::AiImmutable(take_u8(buf, cursor)? != 0),
471 _ => return Err(Error::StreamDelta("unknown annotation kind")),
472 };
473 DeltaOp::SetAnnotation { node, kind }
474 }
475 6 => DeltaOp::ClearAnnotations,
476 7 => {
477 if take_u8(buf, cursor)? == 0 {
478 DeltaOp::SetProvenance(None)
479 } else {
480 let producer = take_opt_string(buf, cursor)?;
481 let model = take_opt_string(buf, cursor)?;
482 let tool = take_opt_string(buf, cursor)?;
483 let timestamp_ms = take_u64(buf, cursor)?;
484 let parent = if take_u8(buf, cursor)? == 0 {
485 None
486 } else {
487 Some(take_hash(buf, cursor)?)
488 };
489 let count = take_u32(buf, cursor)?;
490 let mut entries = Vec::with_capacity(count.min(4096) as usize);
491 for _ in 0..count {
492 let key = take_string(buf, cursor)?;
493 let value = match take_u8(buf, cursor)? {
494 0 => crate::ir::ProvValue::Text(take_string(buf, cursor)?),
495 1 => crate::ir::ProvValue::Int(take_i64(buf, cursor)?),
496 2 => crate::ir::ProvValue::Flag(take_u8(buf, cursor)? != 0),
497 3 => crate::ir::ProvValue::Hash(take_hash(buf, cursor)?),
498 _ => return Err(Error::StreamDelta("unknown provenance value kind")),
499 };
500 entries.push(crate::ir::ProvEntry { key, value });
501 }
502 DeltaOp::SetProvenance(Some(Provenance {
503 producer,
504 timestamp_ms,
505 model,
506 tool,
507 parent,
508 entries,
509 }))
510 }
511 }
512 _ => return Err(Error::StreamDelta("unknown delta operation")),
513 };
514 Ok(op)
515 }
516}
517
518pub fn apply_delta(module: &InfinityModule, ops: &[DeltaOp]) -> Result<InfinityModule> {
520 let mut out = module.clone();
521 for op in ops {
522 match op {
523 DeltaOp::SetRoot(root) => {
524 if let Some(root) = root {
525 if out.get(*root).is_none() {
526 return Err(Error::StreamDelta("root node does not exist"));
527 }
528 }
529 out.set_root(*root);
530 }
531 DeltaOp::PutNode { target, fragment } => {
532 if out.get(*target).is_none() {
533 return Err(Error::StreamDelta("target node does not exist"));
534 }
535 if fragment.node_count() == 0 {
536 return Err(Error::StreamDelta("fragment is empty"));
537 }
538 let replacement = merge_fragment(&mut out, fragment)?;
539 let content = out
540 .get(replacement)
541 .cloned()
542 .ok_or(Error::StreamDelta("fragment root missing"))?;
543 *out.node_mut(*target)? = content;
544 }
545 DeltaOp::PushNode { fragment } => {
546 if fragment.node_count() == 0 {
547 return Err(Error::StreamDelta("fragment is empty"));
548 }
549 let _ = merge_fragment(&mut out, fragment)?;
550 }
551 DeltaOp::SetBinding { name, node } => {
552 if out.get(*node).is_none() {
553 return Err(Error::StreamDelta("binding target does not exist"));
554 }
555 out.bind(name.clone(), *node);
556 }
557 DeltaOp::RemoveBinding { name } => {
558 out.remove_binding(name);
559 }
560 DeltaOp::SetAnnotation { node, kind } => {
561 if out.get(*node).is_none() {
562 return Err(Error::StreamDelta("annotation subject does not exist"));
563 }
564 out.annotate(*node, kind.clone());
565 }
566 DeltaOp::ClearAnnotations => out.clear_annotations(),
567 DeltaOp::SetProvenance(prov) => out.set_provenance(prov.clone()),
568 }
569 }
570 Ok(out)
571}
572
573fn merge_fragment(base: &mut InfinityModule, fragment: &InfinityModule) -> Result<NodeId> {
574 let base_count = base.node_count();
575 let map: Vec<NodeId> = (0..fragment.node_count())
576 .map(|index| NodeId(base_count as u32 + index as u32))
577 .collect();
578 for node in fragment.nodes() {
579 base.push(remap_node(node, &map)?);
580 }
581 Ok(map[fragment.root().unwrap_or(NodeId(0)).index()])
582}
583
584#[derive(Debug, Clone, PartialEq)]
586pub struct StreamedModule {
587 pub module: InfinityModule,
588 pub image: Vec<u8>,
589 pub content_hash: [u8; 32],
590}
591
592#[derive(Debug, Default)]
594pub struct StreamDecoder {
595 sections: [Vec<u8>; SECTION_COUNT],
596 counts: [u32; SECTION_COUNT],
597 seen: [bool; SECTION_COUNT],
598 begun: bool,
599 ended: bool,
600 expected: u64,
601 flags: u16,
602 total_size: Option<u64>,
603 module: Option<InfinityModule>,
604 image: Option<Vec<u8>>,
605 pending_deltas: Vec<DeltaOp>,
606 frames: u64,
607 partial: Vec<u8>,
608 cursor: usize,
609}
610
611impl StreamDecoder {
612 pub fn new() -> Self {
613 Self {
614 sections: Default::default(),
615 counts: [0; SECTION_COUNT],
616 seen: [false; SECTION_COUNT],
617 begun: false,
618 ended: false,
619 expected: 0,
620 flags: 0,
621 total_size: None,
622 module: None,
623 image: None,
624 pending_deltas: Vec::new(),
625 frames: 0,
626 partial: Vec::new(),
627 cursor: 0,
628 }
629 }
630
631 pub fn push_bytes(&mut self, buf: &[u8]) -> Result<()> {
634 self.partial.extend_from_slice(buf);
635 loop {
636 if self.partial.len() < FRAME_HEADER_SIZE {
637 return Ok(());
638 }
639 match Frame::parse(&self.partial) {
640 Ok((frame, consumed)) => {
641 self.push(&frame)?;
642 self.partial.drain(..consumed);
643 }
644 Err(Error::Truncated { .. }) => return Ok(()),
648 Err(Error::StreamFrameLen { declared, .. }) if declared <= MAX_FRAME_PAYLOAD => {
649 return Ok(())
650 }
651 Err(error) => return Err(error),
652 }
653 }
654 }
655
656 pub fn push(&mut self, frame: &Frame) -> Result<()> {
658 if frame.sequence != self.expected {
659 return Err(Error::StreamSequence {
660 expected: self.expected,
661 found: frame.sequence,
662 });
663 }
664 self.expected += 1;
665 self.frames += 1;
666 match frame.kind {
667 FrameKind::Begin => {
668 if self.begun {
669 return Err(Error::StreamState("duplicate begin frame"));
670 }
671 if self.ended {
672 return Err(Error::StreamState("begin after end"));
673 }
674 if frame.payload.len() != 24 {
675 return Err(Error::StreamFrameLen {
676 declared: 24,
677 available: frame.payload.len() as u64,
678 });
679 }
680 let major = u16::from_le_bytes([frame.payload[0], frame.payload[1]]);
681 let minor = u16::from_le_bytes([frame.payload[2], frame.payload[3]]);
682 if major != crate::layout::FORMAT_MAJOR || minor > crate::layout::FORMAT_MINOR {
683 return Err(Error::VersionMajor(major));
684 }
685 let flags = u16::from_le_bytes([frame.payload[4], frame.payload[5]]);
686 if flags & !crate::layout::FLAG_KNOWN_MASK != 0 {
687 return Err(Error::UnknownFlags(flags & !crate::layout::FLAG_KNOWN_MASK));
688 }
689 if frame.payload[6..8] != [0, 0] {
690 return Err(Error::ReservedNonZero {
691 section: "stream-begin",
692 offset: 6,
693 });
694 }
695 let dialect = read_u64(&frame.payload, 8)?;
696 if dialect != crate::layout::dialect_hash() {
697 return Err(Error::DialectMismatch(dialect));
698 }
699 let size = read_u64(&frame.payload, 16)?;
700 if size > crate::layout::Limits::default().max_total_bytes {
701 return Err(Error::Limit {
702 kind: "total bytes",
703 limit: crate::layout::Limits::default().max_total_bytes,
704 });
705 }
706 self.total_size = Some(size);
707 self.flags = flags;
708 self.begun = true;
709 }
710 FrameKind::Batch => {
711 if !self.begun || self.ended {
712 return Err(Error::StreamState("batch outside a generation"));
713 }
714 if frame.payload.len() < 9 {
715 return Err(Error::StreamFrameLen {
716 declared: 9,
717 available: frame.payload.len() as u64,
718 });
719 }
720 let section = frame.payload[0] as usize;
721 if section >= SECTION_COUNT {
722 return Err(Error::StreamState("unknown section id"));
723 }
724 let count = read_u32(&frame.payload, 1)?;
725 let len = read_u32(&frame.payload, 5)? as usize;
726 if frame.payload.len() != 9 + len {
727 return Err(Error::StreamFrameLen {
728 declared: len as u32,
729 available: (frame.payload.len() - 9) as u64,
730 });
731 }
732 if section != self.cursor && section != self.cursor + 1 {
733 return Err(Error::StreamState("sections out of order"));
734 }
735 if section == self.cursor + 1 {
736 self.cursor = section;
737 }
738 if self.seen[section] && self.counts[section] != count {
739 return Err(Error::StreamState("section count changed mid-stream"));
740 }
741 self.seen[section] = true;
742 self.counts[section] = count;
743 self.sections[section].extend_from_slice(&frame.payload[9..]);
744 }
745 FrameKind::Delta => {
746 if !self.ended {
747 return Err(Error::StreamState("delta before end"));
748 }
749 let mut cursor = 0usize;
750 let count = take_u32(&frame.payload, &mut cursor)?;
751 if count > crate::layout::Limits::default().max_aux {
752 return Err(Error::StreamDelta("too many delta operations"));
753 }
754 for _ in 0..count {
755 self.pending_deltas
756 .push(DeltaOp::decode(&frame.payload, &mut cursor)?);
757 }
758 if cursor != frame.payload.len() {
759 return Err(Error::StreamDelta("trailing bytes in delta payload"));
760 }
761 }
762 FrameKind::End => {
763 if !self.begun {
764 return Err(Error::StreamState("end before begin"));
765 }
766 if frame.payload.len() != 40 {
767 return Err(Error::StreamFrameLen {
768 declared: 40,
769 available: frame.payload.len() as u64,
770 });
771 }
772 let mut hash = [0u8; 32];
773 hash.copy_from_slice(&frame.payload[..32]);
774 let size = read_u64(&frame.payload, 32)?;
775 if self.ended {
776 let base = self
778 .module
779 .as_ref()
780 .ok_or(Error::StreamState("no module to patch"))?;
781 let updated = apply_delta(base, &self.pending_deltas)?;
782 self.pending_deltas.clear();
783 let image = encode_with(&updated, EncodeOpts::default())?;
784 if image.len() as u64 != size {
785 return Err(Error::StreamDelta("delta changed the declared size"));
786 }
787 if hash_image(&image)? != hash {
788 return Err(Error::StreamHashMismatch);
789 }
790 let validated = Validated::new(&image, crate::layout::DecodeOpts::default())?;
791 let module = crate::decode::decode_validated(&validated)?;
792 self.module = Some(module);
793 self.image = Some(image);
794 return Ok(());
795 }
796 let image = self.assemble(size)?;
797 if hash_image(&image)? != hash {
798 return Err(Error::StreamHashMismatch);
799 }
800 let validated = Validated::new(&image, crate::layout::DecodeOpts::default())?;
801 let module = crate::decode::decode_validated(&validated)?;
802 self.module = Some(module);
803 self.image = Some(image);
804 self.ended = true;
805 }
806 }
807 Ok(())
808 }
809
810 fn assemble(&self, size: u64) -> Result<Vec<u8>> {
811 if size < HEADER_SIZE as u64 {
812 return Err(Error::StreamDelta("declared size smaller than a header"));
813 }
814 if self.seen.iter().any(|seen| !seen) {
815 return Err(Error::StreamState("generation missing a section"));
816 }
817 let mut image = vec![0u8; HEADER_SIZE];
818 let mut header = crate::layout::Header {
819 major: crate::layout::FORMAT_MAJOR,
820 minor: crate::layout::FORMAT_MINOR,
821 flags: self.flags,
822 header_size: HEADER_SIZE as u16,
823 content_hash: [0u8; 32],
824 dialect_hash: crate::layout::dialect_hash(),
825 total_size: 0,
826 sections: [crate::layout::SectionSpan::default(); SECTION_COUNT],
827 };
828 for section in Section::ALL {
829 while image.len() % 8 != 0 {
830 image.push(0);
831 }
832 header.span_mut(section).offset = image.len() as u32;
833 header.span_mut(section).count = self.counts[section as usize];
834 image.extend_from_slice(&self.sections[section as usize]);
835 }
836 while image.len() % 8 != 0 {
837 image.push(0);
838 }
839 header.total_size = image.len() as u64;
840 header.write(&mut image);
841 let hash = hash_image(&image)?;
842 image[16..48].copy_from_slice(&hash);
843 if image.len() as u64 != size {
844 return Err(Error::StreamDelta(
845 "reassembled size disagrees with the end frame",
846 ));
847 }
848 Ok(image)
849 }
850
851 pub fn module(&self) -> Option<&InfinityModule> {
853 self.module.as_ref()
854 }
855
856 pub fn image(&self) -> Option<&[u8]> {
858 self.image.as_deref()
859 }
860
861 pub fn finish(self) -> Result<StreamedModule> {
864 if !self.partial.is_empty() {
865 return Err(Error::Truncated {
866 needed: self.partial.len() as u64
867 + self.partial[16..20]
868 .iter()
869 .fold(0u64, |acc, byte| acc * 256 + *byte as u64),
870 found: self.partial.len() as u64,
871 });
872 }
873 let module = self.module.ok_or(Error::StreamUnterminated)?;
874 let image = self.image.ok_or(Error::StreamUnterminated)?;
875 let hash = hash_image(&image)?;
876 Ok(StreamedModule {
877 module,
878 image,
879 content_hash: hash,
880 })
881 }
882
883 pub fn frames(&self) -> u64 {
885 self.frames
886 }
887}
888
889#[derive(Debug)]
891pub struct StreamEncoder {
892 frames: VecDeque<Frame>,
893 sequence: u64,
894}
895
896impl StreamEncoder {
897 pub fn new(module: &InfinityModule, chunk: usize) -> Result<Self> {
899 let frames = encode_stream(module, chunk)?;
900 let sequence = frames.len() as u64;
901 Ok(Self {
902 frames: frames.into(),
903 sequence,
904 })
905 }
906
907 pub fn push_delta(&mut self, base: &InfinityModule, ops: &[DeltaOp]) -> Result<()> {
910 let updated = apply_delta(base, ops)?;
911 let image = encode_with(&updated, EncodeOpts::default())?;
912 let mut payload = Vec::new();
913 payload.extend_from_slice(&(ops.len() as u32).to_le_bytes());
914 for op in ops {
915 op.encode(&mut payload)?;
916 }
917 self.frames
918 .push_back(Frame::new(FrameKind::Delta, self.sequence, payload));
919 self.sequence += 1;
920 let mut end = Vec::new();
921 end.extend_from_slice(&hash_image(&image)?);
922 end.extend_from_slice(&(image.len() as u64).to_le_bytes());
923 self.frames
924 .push_back(Frame::new(FrameKind::End, self.sequence, end));
925 self.sequence += 1;
926 Ok(())
927 }
928
929 pub fn frames(&self) -> Vec<Frame> {
931 self.frames.iter().cloned().collect()
932 }
933
934 pub fn to_bytes(&self) -> Vec<u8> {
936 let mut out = Vec::new();
937 for frame in &self.frames {
938 out.extend_from_slice(&frame.to_bytes());
939 }
940 out
941 }
942}
943
944fn write_string(out: &mut Vec<u8>, text: &str) -> Result<()> {
945 let bytes = text.as_bytes();
946 if bytes.len() > u16::MAX as usize {
947 return Err(Error::TooLarge);
948 }
949 out.extend_from_slice(&(bytes.len() as u16).to_le_bytes());
950 out.extend_from_slice(bytes);
951 Ok(())
952}
953
954fn write_opt_string(out: &mut Vec<u8>, text: Option<&str>) -> Result<()> {
955 match text {
956 None => {
957 out.push(0);
958 Ok(())
959 }
960 Some(text) => {
961 out.push(1);
962 write_string(out, text)
963 }
964 }
965}
966
967fn take_u8(buf: &[u8], cursor: &mut usize) -> Result<u8> {
968 if *cursor >= buf.len() {
969 return Err(Error::StreamDelta("truncated delta payload"));
970 }
971 let value = buf[*cursor];
972 *cursor += 1;
973 Ok(value)
974}
975
976fn take_u32(buf: &[u8], cursor: &mut usize) -> Result<u32> {
977 if *cursor + 4 > buf.len() {
978 return Err(Error::StreamDelta("truncated delta payload"));
979 }
980 let value = read_u32(buf, *cursor)?;
981 *cursor += 4;
982 Ok(value)
983}
984
985fn take_u64(buf: &[u8], cursor: &mut usize) -> Result<u64> {
986 if *cursor + 8 > buf.len() {
987 return Err(Error::StreamDelta("truncated delta payload"));
988 }
989 let value = read_u64(buf, *cursor)?;
990 *cursor += 8;
991 Ok(value)
992}
993
994fn take_i64(buf: &[u8], cursor: &mut usize) -> Result<i64> {
995 take_u64(buf, cursor).map(|value| value as i64)
996}
997
998fn take_hash(buf: &[u8], cursor: &mut usize) -> Result<[u8; 32]> {
999 if *cursor + 32 > buf.len() {
1000 return Err(Error::StreamDelta("truncated delta payload"));
1001 }
1002 let mut hash = [0u8; 32];
1003 hash.copy_from_slice(&buf[*cursor..*cursor + 32]);
1004 *cursor += 32;
1005 Ok(hash)
1006}
1007
1008fn take_string(buf: &[u8], cursor: &mut usize) -> Result<String> {
1009 if *cursor + 2 > buf.len() {
1010 return Err(Error::StreamDelta("truncated delta payload"));
1011 }
1012 let len = u16::from_le_bytes([buf[*cursor], buf[*cursor + 1]]) as usize;
1013 *cursor += 2;
1014 if *cursor + len > buf.len() {
1015 return Err(Error::StreamDelta("truncated delta payload"));
1016 }
1017 let text = std::str::from_utf8(&buf[*cursor..*cursor + len])
1018 .map_err(|_| Error::Utf8 {
1019 offset: *cursor as u64,
1020 })?
1021 .to_string();
1022 *cursor += len;
1023 Ok(text)
1024}
1025
1026fn take_opt_string(buf: &[u8], cursor: &mut usize) -> Result<Option<String>> {
1027 match take_u8(buf, cursor)? {
1028 0 => Ok(None),
1029 1 => Ok(Some(take_string(buf, cursor)?)),
1030 _ => Err(Error::StreamDelta("invalid optional string tag")),
1031 }
1032}
1033
1034fn decode_fragment(buf: &[u8], cursor: &mut usize) -> Result<InfinityModule> {
1035 let len = take_u32(buf, cursor)? as usize;
1036 if *cursor + len > buf.len() {
1037 return Err(Error::StreamDelta("truncated fragment"));
1038 }
1039 let bytes = &buf[*cursor..*cursor + len];
1040 *cursor += len;
1041 crate::decode::decode(bytes)
1042}
1043
1044pub fn delta_hash(module: &InfinityModule, ops: &[DeltaOp]) -> Result<[u8; 32]> {
1046 let updated = apply_delta(module, ops)?;
1047 let image = encode_with(&updated, EncodeOpts::default())?;
1048 hash_image(&image)
1049}
1050
1051pub fn decode_stream(bytes: &[u8]) -> Result<StreamedModule> {
1053 let mut decoder = StreamDecoder::new();
1054 decoder.push_bytes(bytes)?;
1055 decoder.finish()
1056}
1057
1058pub fn aligned_section_len(len: usize) -> usize {
1060 align8(len as u64) as usize
1061}
1062
1063pub fn delta_annotations(ops: &[DeltaOp]) -> Vec<Annotation> {
1065 ops.iter()
1066 .filter_map(|op| match op {
1067 DeltaOp::SetAnnotation { node, kind } => Some(Annotation {
1068 node: *node,
1069 kind: kind.clone(),
1070 }),
1071 _ => None,
1072 })
1073 .collect()
1074}