1use crate::error::{Error, Result};
8use crate::limits::Limits;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum Op {
13 EmitObject {
15 object_id: u32,
17 },
18 Inline {
20 bytes: Vec<u8>,
22 },
23 RepeatLast {
26 count: u32,
28 },
29 DecodeChannel {
31 channel_id: u32,
33 },
34 InterleaveChannels {
39 kinds_channel: u32,
41 lengths_channel: u32,
43 first_payload_channel: u32,
45 payload_channel_count: u8,
47 },
48 MarkOffset {
52 slot: u8,
54 },
55 EmitOffset {
58 slot: u8,
60 width: u8,
62 },
63 PackSegments {
68 data_object: u32,
70 items: Vec<PackItem>,
72 },
73 PackedChannels {
80 data_channel: u32,
82 plan_channel: u32,
84 declared_output_len: u64,
86 },
87 DeflateReplay {
100 replay_codec: u8,
106 source_kind: u8,
108 source_id: u32,
110 corrections_object: u32,
112 declared_output_len: u32,
114 },
115}
116
117#[derive(Debug, Clone, PartialEq, Eq)]
119pub enum PackItem {
120 Literal {
122 len: u32,
124 },
125 Mark {
127 slot: u8,
129 },
130 Emit {
132 slot: u8,
134 width: u8,
136 },
137}
138
139pub const OP_EMIT_OBJECT: u8 = 0x01;
141pub const OP_INLINE: u8 = 0x02;
143pub const OP_REPEAT_LAST: u8 = 0x03;
145pub const OP_DECODE_CHANNEL: u8 = 0x04;
147pub const OP_INTERLEAVE_CHANNELS: u8 = 0x05;
149pub const OP_MARK_OFFSET: u8 = 0x06;
151pub const OP_EMIT_OFFSET: u8 = 0x07;
153pub const OP_PACK_SEGMENTS: u8 = 0x08;
155pub const OP_PACKED_CHANNELS: u8 = 0x09;
157pub const OP_DEFLATE_REPLAY: u8 = 0x0A;
159pub const DEFLATE_SOURCE_OBJECT: u8 = 0;
161pub const DEFLATE_SOURCE_CHANNEL: u8 = 1;
163pub const REPLAY_DEFLATE_PREFLATE_0_7_6: u8 = 1;
170
171const PACK_ITEM_LITERAL: u8 = 0x01;
173const PACK_ITEM_MARK: u8 = 0x02;
175const PACK_ITEM_EMIT: u8 = 0x03;
177
178impl Op {
179 pub fn encode(&self, out: &mut Vec<u8>) -> Result<()> {
181 match self {
182 Op::EmitObject { object_id } => {
183 out.push(OP_EMIT_OBJECT);
184 out.extend_from_slice(&object_id.to_le_bytes());
185 }
186 Op::Inline { bytes } => {
187 let len = u32::try_from(bytes.len())
188 .map_err(|_| Error::resource_limit("inline literal exceeds 4 GiB"))?;
189 out.push(OP_INLINE);
190 out.extend_from_slice(&len.to_le_bytes());
191 out.extend_from_slice(bytes);
192 }
193 Op::RepeatLast { count } => {
194 out.push(OP_REPEAT_LAST);
195 out.extend_from_slice(&count.to_le_bytes());
196 }
197 Op::DecodeChannel { channel_id } => {
198 out.push(OP_DECODE_CHANNEL);
199 out.extend_from_slice(&channel_id.to_le_bytes());
200 }
201 Op::InterleaveChannels {
202 kinds_channel,
203 lengths_channel,
204 first_payload_channel,
205 payload_channel_count,
206 } => {
207 out.push(OP_INTERLEAVE_CHANNELS);
208 out.extend_from_slice(&kinds_channel.to_le_bytes());
209 out.extend_from_slice(&lengths_channel.to_le_bytes());
210 out.extend_from_slice(&first_payload_channel.to_le_bytes());
211 out.push(*payload_channel_count);
212 }
213 Op::MarkOffset { slot } => {
214 out.push(OP_MARK_OFFSET);
215 out.push(*slot);
216 }
217 Op::EmitOffset { slot, width } => {
218 out.push(OP_EMIT_OFFSET);
219 out.push(*slot);
220 out.push(*width);
221 }
222 Op::PackSegments { data_object, items } => {
223 out.push(OP_PACK_SEGMENTS);
224 out.extend_from_slice(&data_object.to_le_bytes());
225 write_item_table(items, out)?;
226 }
227 Op::PackedChannels {
228 data_channel,
229 plan_channel,
230 declared_output_len,
231 } => {
232 out.push(OP_PACKED_CHANNELS);
233 out.extend_from_slice(&data_channel.to_le_bytes());
234 out.extend_from_slice(&plan_channel.to_le_bytes());
235 out.extend_from_slice(&declared_output_len.to_le_bytes());
236 }
237 Op::DeflateReplay {
238 replay_codec,
239 source_kind,
240 source_id,
241 corrections_object,
242 declared_output_len,
243 } => {
244 out.push(OP_DEFLATE_REPLAY);
245 out.push(*replay_codec);
246 out.push(*source_kind);
247 out.extend_from_slice(&source_id.to_le_bytes());
248 out.extend_from_slice(&corrections_object.to_le_bytes());
249 out.extend_from_slice(&declared_output_len.to_le_bytes());
250 }
251 }
252 Ok(())
253 }
254
255 pub fn decode(data: &[u8], pos: &mut usize, limits: Limits) -> Result<Op> {
257 let tag = *data
258 .get(*pos)
259 .ok_or_else(|| Error::invalid_graph("truncated instruction opcode"))?;
260 *pos += 1;
261 match tag {
262 OP_EMIT_OBJECT => {
263 let id = read_u32(data, pos)?;
264 Ok(Op::EmitObject { object_id: id })
265 }
266 OP_INLINE => {
267 let len = read_u32(data, pos)?;
268 if len > limits.max_record_len {
269 return Err(Error::resource_limit(
270 "inline literal length exceeds record limit",
271 ));
272 }
273 let end = pos
274 .checked_add(len as usize)
275 .ok_or_else(|| Error::invalid_graph("inline length overflow"))?;
276 if end > data.len() {
277 return Err(Error::invalid_graph("truncated inline literal"));
278 }
279 let bytes = data[*pos..end].to_vec();
280 *pos = end;
281 Ok(Op::Inline { bytes })
282 }
283 OP_REPEAT_LAST => {
284 let count = read_u32(data, pos)?;
285 Ok(Op::RepeatLast { count })
286 }
287 OP_DECODE_CHANNEL => {
288 let id = read_u32(data, pos)?;
289 Ok(Op::DecodeChannel { channel_id: id })
290 }
291 OP_INTERLEAVE_CHANNELS => {
292 let kinds_channel = read_u32(data, pos)?;
293 let lengths_channel = read_u32(data, pos)?;
294 let first_payload_channel = read_u32(data, pos)?;
295 let payload_channel_count = *data
296 .get(*pos)
297 .ok_or_else(|| Error::invalid_graph("truncated instruction operand"))?;
298 *pos += 1;
299 Ok(Op::InterleaveChannels {
300 kinds_channel,
301 lengths_channel,
302 first_payload_channel,
303 payload_channel_count,
304 })
305 }
306 OP_MARK_OFFSET => {
307 let slot = read_u8(data, pos)?;
308 Ok(Op::MarkOffset { slot })
309 }
310 OP_EMIT_OFFSET => {
311 let slot = read_u8(data, pos)?;
312 let width = read_u8(data, pos)?;
313 Ok(Op::EmitOffset { slot, width })
314 }
315 OP_PACK_SEGMENTS => {
316 let data_object = read_u32(data, pos)?;
317 let item_count = read_u32(data, pos)?;
318 let items = read_items(data, pos, item_count, limits)?;
319 Ok(Op::PackSegments { data_object, items })
320 }
321 OP_PACKED_CHANNELS => {
322 let data_channel = read_u32(data, pos)?;
323 let plan_channel = read_u32(data, pos)?;
324 let declared_output_len = read_u64(data, pos)?;
325 Ok(Op::PackedChannels {
326 data_channel,
327 plan_channel,
328 declared_output_len,
329 })
330 }
331 OP_DEFLATE_REPLAY => {
332 let replay_codec = read_u8(data, pos)?;
333 if replay_codec != REPLAY_DEFLATE_PREFLATE_0_7_6 {
334 return Err(Error::unsupported_feature(format!(
335 "DEFLATE_REPLAY codec {replay_codec} is not the supported preflate-0.7.6 semantics"
336 )));
337 }
338 let source_kind = read_u8(data, pos)?;
339 if source_kind != DEFLATE_SOURCE_OBJECT && source_kind != DEFLATE_SOURCE_CHANNEL {
340 return Err(Error::invalid_graph(format!(
341 "DEFLATE_REPLAY source kind {source_kind} is not 0 (object) or 1 (channel)"
342 )));
343 }
344 let source_id = read_u32(data, pos)?;
345 let corrections_object = read_u32(data, pos)?;
346 let declared_output_len = read_u32(data, pos)?;
347 Ok(Op::DeflateReplay {
348 replay_codec,
349 source_kind,
350 source_id,
351 corrections_object,
352 declared_output_len,
353 })
354 }
355 other => Err(Error::invalid_graph(format!(
356 "unknown DRA opcode {other:#04x}"
357 ))),
358 }
359 }
360}
361
362pub fn encode_items(items: &[PackItem]) -> Result<Vec<u8>> {
369 let mut out = Vec::with_capacity(4 + items.len() * 3);
370 write_item_table(items, &mut out)?;
371 Ok(out)
372}
373
374pub fn decode_items(bytes: &[u8], limits: Limits) -> Result<Vec<PackItem>> {
379 if bytes.len() < 4 {
380 return Err(Error::invalid_graph("truncated packed item table header"));
381 }
382 let count = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
383 let mut pos = 4usize;
384 let items = read_items(bytes, &mut pos, count, limits)?;
385 if pos != bytes.len() {
386 return Err(Error::invalid_graph(format!(
387 "packed item table has {} trailing bytes",
388 bytes.len() - pos
389 )));
390 }
391 Ok(items)
392}
393
394fn write_item_table(items: &[PackItem], out: &mut Vec<u8>) -> Result<()> {
396 let count = u32::try_from(items.len())
397 .map_err(|_| Error::resource_limit("packed item table exceeds 4 GiB"))?;
398 out.extend_from_slice(&count.to_le_bytes());
399 write_items_into(items, out)
400}
401
402fn write_items_into(items: &[PackItem], out: &mut Vec<u8>) -> Result<()> {
404 for item in items {
405 match item {
406 PackItem::Literal { len } => {
407 out.push(PACK_ITEM_LITERAL);
408 write_leb128_u32(*len, out);
409 }
410 PackItem::Mark { slot } => {
411 out.push(PACK_ITEM_MARK);
412 out.push(*slot);
413 }
414 PackItem::Emit { slot, width } => {
415 out.push(PACK_ITEM_EMIT);
416 out.push(*slot);
417 out.push(*width);
418 }
419 }
420 }
421 Ok(())
422}
423
424fn read_items(data: &[u8], pos: &mut usize, count: u32, limits: Limits) -> Result<Vec<PackItem>> {
427 if count > limits.max_graph_ops {
428 return Err(Error::resource_limit(format!(
429 "packed item count {count} exceeds limit {}",
430 limits.max_graph_ops
431 )));
432 }
433 let mut items = Vec::with_capacity(count.min(4096) as usize);
434 for _ in 0..count {
435 let tag = *data
436 .get(*pos)
437 .ok_or_else(|| Error::invalid_graph("truncated packed item"))?;
438 *pos += 1;
439 match tag {
440 PACK_ITEM_LITERAL => {
441 let len = read_leb128_u32(data, pos)?;
442 items.push(PackItem::Literal { len });
443 }
444 PACK_ITEM_MARK => {
445 let slot = read_u8(data, pos)?;
446 items.push(PackItem::Mark { slot });
447 }
448 PACK_ITEM_EMIT => {
449 let slot = read_u8(data, pos)?;
450 let width = read_u8(data, pos)?;
451 items.push(PackItem::Emit { slot, width });
452 }
453 other => {
454 return Err(Error::invalid_graph(format!(
455 "unknown packed item tag {other:#04x}"
456 )));
457 }
458 }
459 }
460 Ok(items)
461}
462
463fn read_u8(data: &[u8], pos: &mut usize) -> Result<u8> {
464 let v = *data
465 .get(*pos)
466 .ok_or_else(|| Error::invalid_graph("truncated instruction operand"))?;
467 *pos += 1;
468 Ok(v)
469}
470
471fn write_leb128_u32(value: u32, out: &mut Vec<u8>) {
473 let mut v = value;
474 loop {
475 let byte = (v & 0x7f) as u8;
476 v >>= 7;
477 if v == 0 {
478 out.push(byte);
479 return;
480 }
481 out.push(byte | 0x80);
482 }
483}
484
485fn read_leb128_u32(data: &[u8], pos: &mut usize) -> Result<u32> {
489 let mut result: u32 = 0;
490 for shift in [0u32, 7, 14, 21, 28] {
491 let byte = *data
492 .get(*pos)
493 .ok_or_else(|| Error::invalid_graph("truncated LEB128 operand"))?;
494 *pos += 1;
495 let low = u32::from(byte & 0x7f);
496 if shift == 28 && low > 0x0f {
497 return Err(Error::invalid_graph("LEB128 varint overflows u32"));
498 }
499 result |= low << shift;
500 if byte & 0x80 == 0 {
501 return Ok(result);
502 }
503 }
504 Err(Error::invalid_graph("LEB128 varint overflows u32"))
505}
506
507fn read_u64(data: &[u8], pos: &mut usize) -> Result<u64> {
508 let end = pos
509 .checked_add(8)
510 .ok_or_else(|| Error::invalid_graph("operand offset overflow"))?;
511 if end > data.len() {
512 return Err(Error::invalid_graph("truncated instruction operand"));
513 }
514 let mut buf = [0u8; 8];
515 buf.copy_from_slice(&data[*pos..end]);
516 *pos = end;
517 Ok(u64::from_le_bytes(buf))
518}
519
520fn read_u32(data: &[u8], pos: &mut usize) -> Result<u32> {
521 let end = pos
522 .checked_add(4)
523 .ok_or_else(|| Error::invalid_graph("operand offset overflow"))?;
524 if end > data.len() {
525 return Err(Error::invalid_graph("truncated instruction operand"));
526 }
527 let v = u32::from_le_bytes([data[*pos], data[*pos + 1], data[*pos + 2], data[*pos + 3]]);
528 *pos = end;
529 Ok(v)
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535
536 fn roundtrip(op: Op) {
537 let mut buf = Vec::new();
538 op.encode(&mut buf).unwrap();
539 let mut pos = 0;
540 let back = Op::decode(&buf, &mut pos, Limits::DEFAULT).unwrap();
541 assert_eq!(op, back);
542 assert_eq!(pos, buf.len());
543 }
544
545 #[test]
546 fn op_roundtrips() {
547 roundtrip(Op::EmitObject { object_id: 7 });
548 roundtrip(Op::Inline {
549 bytes: vec![1, 2, 3, 4, 5],
550 });
551 roundtrip(Op::RepeatLast { count: 1_000_000 });
552 roundtrip(Op::Inline { bytes: Vec::new() });
553 roundtrip(Op::DecodeChannel { channel_id: 3 });
554 roundtrip(Op::InterleaveChannels {
555 kinds_channel: 0,
556 lengths_channel: 1,
557 first_payload_channel: 2,
558 payload_channel_count: 3,
559 });
560 roundtrip(Op::MarkOffset { slot: 0 });
561 roundtrip(Op::MarkOffset { slot: 15 });
562 roundtrip(Op::EmitOffset { slot: 0, width: 1 });
563 roundtrip(Op::EmitOffset { slot: 7, width: 20 });
564 roundtrip(Op::PackSegments {
565 data_object: 2,
566 items: vec![
567 PackItem::Literal { len: 3 },
568 PackItem::Mark { slot: 0 },
569 PackItem::Literal { len: 300 },
570 PackItem::Emit { slot: 0, width: 3 },
571 ],
572 });
573 roundtrip(Op::PackedChannels {
574 data_channel: 0,
575 plan_channel: 1,
576 declared_output_len: 9,
577 });
578 roundtrip(Op::PackedChannels {
579 data_channel: 3,
580 plan_channel: 4,
581 declared_output_len: u64::MAX,
582 });
583 roundtrip(Op::DeflateReplay {
584 replay_codec: REPLAY_DEFLATE_PREFLATE_0_7_6,
585 source_kind: DEFLATE_SOURCE_OBJECT,
586 source_id: 0,
587 corrections_object: 1,
588 declared_output_len: 1234,
589 });
590 roundtrip(Op::DeflateReplay {
591 replay_codec: REPLAY_DEFLATE_PREFLATE_0_7_6,
592 source_kind: DEFLATE_SOURCE_CHANNEL,
593 source_id: u32::MAX,
594 corrections_object: u32::MAX,
595 declared_output_len: u32::MAX,
596 });
597 }
598
599 #[test]
600 fn rejects_unknown_replay_codec_as_unsupported_feature() {
601 let data = [OP_DEFLATE_REPLAY, 2];
605 let mut pos = 0;
606 let e = Op::decode(&data, &mut pos, Limits::DEFAULT).unwrap_err();
607 assert_eq!(e.class(), crate::ErrorClass::UnsupportedFeature);
608 }
609
610 #[test]
611 fn rejects_unknown_opcode() {
612 let data = [0xEEu8];
613 let mut pos = 0;
614 let e = Op::decode(&data, &mut pos, Limits::DEFAULT).unwrap_err();
615 assert_eq!(e.class(), crate::ErrorClass::InvalidGraph);
616 }
617
618 #[test]
619 fn rejects_truncated_operand() {
620 let data = [OP_EMIT_OBJECT, 0x01, 0x02];
621 let mut pos = 0;
622 let e = Op::decode(&data, &mut pos, Limits::DEFAULT).unwrap_err();
623 assert_eq!(e.class(), crate::ErrorClass::InvalidGraph);
624 }
625}