1extern crate alloc;
36use alloc::vec::Vec;
37
38use crate::buffer::{BufferReader, BufferWriter};
39use crate::error::{DecodeError, EncodeError};
40
41pub fn encode_appendable<F>(writer: &mut BufferWriter, body: F) -> Result<(), EncodeError>
52where
53 F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
54{
55 let mut inner =
58 BufferWriter::new(writer.endianness()).with_max_alignment(writer.max_alignment());
59 body(&mut inner)?;
60 let bytes = inner.into_bytes();
61 let len = u32::try_from(bytes.len()).map_err(|_| EncodeError::ValueOutOfRange {
62 message: "appendable struct body exceeds u32::MAX",
63 })?;
64 writer.write_u32(len)?;
65 writer.write_bytes(&bytes)?;
66 Ok(())
67}
68
69pub fn decode_appendable<T, F>(reader: &mut BufferReader<'_>, body: F) -> Result<T, DecodeError>
78where
79 F: FnOnce(&mut BufferReader<'_>) -> Result<T, DecodeError>,
80{
81 let len = reader.read_u32()? as usize;
82 if len > reader.remaining() {
83 return Err(DecodeError::LengthExceeded {
84 announced: len,
85 remaining: reader.remaining(),
86 offset: reader.position(),
87 });
88 }
89 let body_bytes = reader.read_bytes(len)?;
90 let mut sub = BufferReader::new(body_bytes, reader.endianness())
93 .with_max_alignment(reader.max_alignment());
94 body(&mut sub)
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
103#[repr(u8)]
104pub enum LengthCode {
105 Lc0 = 0,
107 Lc1 = 1,
109 Lc2 = 2,
111 Lc3 = 3,
113 Lc4 = 4,
115 Lc5 = 5,
117 Lc6 = 6,
119 Lc7 = 7,
121}
122
123impl LengthCode {
124 #[must_use]
126 pub fn body_len(self, framing_word: u32) -> u64 {
127 match self {
128 Self::Lc0 => 1,
129 Self::Lc1 => 2,
130 Self::Lc2 => 4,
131 Self::Lc3 => 8,
132 Self::Lc4 => u64::from(framing_word),
135 Self::Lc5 | Self::Lc6 | Self::Lc7 => u64::from(framing_word) + 4,
139 }
140 }
141
142 #[must_use]
146 pub const fn has_nextint(self) -> bool {
147 matches!(self, Self::Lc4)
148 }
149
150 #[must_use]
154 pub const fn reuses_leading_len(self) -> bool {
155 matches!(self, Self::Lc5 | Self::Lc6 | Self::Lc7)
156 }
157
158 #[must_use]
160 pub const fn from_wire(value: u8) -> Option<Self> {
161 match value {
162 0 => Some(Self::Lc0),
163 1 => Some(Self::Lc1),
164 2 => Some(Self::Lc2),
165 3 => Some(Self::Lc3),
166 4 => Some(Self::Lc4),
167 5 => Some(Self::Lc5),
168 6 => Some(Self::Lc6),
169 7 => Some(Self::Lc7),
170 _ => None,
171 }
172 }
173}
174
175pub fn encode_mutable_member<F>(
180 writer: &mut BufferWriter,
181 member_id: u32,
182 must_understand: bool,
183 body: F,
184) -> Result<(), EncodeError>
185where
186 F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
187{
188 encode_mutable_member_lc(writer, member_id, must_understand, LengthCode::Lc4, body)
189}
190
191pub fn encode_mutable_member_lc<F>(
197 writer: &mut BufferWriter,
198 member_id: u32,
199 must_understand: bool,
200 lc: LengthCode,
201 body: F,
202) -> Result<(), EncodeError>
203where
204 F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
205{
206 if member_id > 0x0FFF_FFFF {
207 return Err(EncodeError::ValueOutOfRange {
208 message: "EMHEADER member_id exceeds 28-bit field",
209 });
210 }
211 let mut inner =
214 BufferWriter::new(writer.endianness()).with_max_alignment(writer.max_alignment());
215 body(&mut inner)?;
216 let body_bytes = inner.into_bytes();
217 let body_len = body_bytes.len();
218
219 let nextint: Option<u32> = match lc {
220 LengthCode::Lc0 => {
221 if body_len != 1 {
222 return Err(EncodeError::ValueOutOfRange {
223 message: "LC0 requires exactly 1 byte body",
224 });
225 }
226 None
227 }
228 LengthCode::Lc1 => {
229 if body_len != 2 {
230 return Err(EncodeError::ValueOutOfRange {
231 message: "LC1 requires exactly 2 bytes body",
232 });
233 }
234 None
235 }
236 LengthCode::Lc2 => {
237 if body_len != 4 {
238 return Err(EncodeError::ValueOutOfRange {
239 message: "LC2 requires exactly 4 bytes body",
240 });
241 }
242 None
243 }
244 LengthCode::Lc3 => {
245 if body_len != 8 {
246 return Err(EncodeError::ValueOutOfRange {
247 message: "LC3 requires exactly 8 bytes body",
248 });
249 }
250 None
251 }
252 LengthCode::Lc4 => {
253 let n = u32::try_from(body_len).map_err(|_| EncodeError::ValueOutOfRange {
255 message: "LC4 body exceeds u32::MAX",
256 })?;
257 Some(n)
258 }
259 LengthCode::Lc5 | LengthCode::Lc6 | LengthCode::Lc7 => {
260 if body_len < 4 {
266 return Err(EncodeError::ValueOutOfRange {
267 message: "LC5/6/7 member body must start with a 4-byte length word",
268 });
269 }
270 let mut w = [0u8; 4];
271 w.copy_from_slice(&body_bytes[0..4]);
272 let leading = writer.endianness().read_u32(w);
273 let content = body_len - 4;
274 if u64::from(leading) != content as u64 {
275 return Err(EncodeError::ValueOutOfRange {
276 message: "LC5/6/7 leading length word must equal content byte count",
277 });
278 }
279 if lc == LengthCode::Lc6 && content % 4 != 0 {
282 return Err(EncodeError::ValueOutOfRange {
283 message: "LC6 content must be a multiple of 4 bytes",
284 });
285 }
286 if lc == LengthCode::Lc7 && content % 8 != 0 {
287 return Err(EncodeError::ValueOutOfRange {
288 message: "LC7 content must be a multiple of 8 bytes",
289 });
290 }
291 None
294 }
295 };
296
297 let m_bit = u32::from(must_understand) << 31;
298 let lc_bits = (lc as u32) << 28;
299 let emheader = m_bit + lc_bits + member_id;
305 writer.write_u32(emheader)?;
306 if let Some(ni) = nextint {
307 writer.write_u32(ni)?;
308 }
309 writer.write_bytes(&body_bytes)?;
310 Ok(())
311}
312
313pub struct MutableStructEncoder<'a> {
326 writer: &'a mut BufferWriter,
327 required_ids: Vec<u32>,
328 emitted_ids: Vec<u32>,
329}
330
331impl<'a> MutableStructEncoder<'a> {
332 pub fn new(writer: &'a mut BufferWriter, required_ids: Vec<u32>) -> Self {
336 Self {
337 writer,
338 required_ids,
339 emitted_ids: Vec::new(),
340 }
341 }
342
343 pub fn encode_member<F>(
349 &mut self,
350 member_id: u32,
351 must_understand: bool,
352 body: F,
353 ) -> Result<(), EncodeError>
354 where
355 F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
356 {
357 encode_mutable_member(self.writer, member_id, must_understand, body)?;
358 self.emitted_ids.push(member_id);
359 Ok(())
360 }
361
362 pub fn encode_member_lc<F>(
367 &mut self,
368 member_id: u32,
369 must_understand: bool,
370 lc: LengthCode,
371 body: F,
372 ) -> Result<(), EncodeError>
373 where
374 F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
375 {
376 encode_mutable_member_lc(self.writer, member_id, must_understand, lc, body)?;
377 self.emitted_ids.push(member_id);
378 Ok(())
379 }
380
381 pub fn finish(self) -> Result<(), EncodeError> {
388 for required in &self.required_ids {
389 if !self.emitted_ids.contains(required) {
390 return Err(EncodeError::MissingNonOptionalMember {
391 member_id: *required,
392 });
393 }
394 }
395 Ok(())
396 }
397}
398
399#[derive(Debug, Clone)]
401pub struct MutableMember<'a> {
402 pub member_id: u32,
404 pub must_understand: bool,
406 pub length_code: LengthCode,
408 pub body: &'a [u8],
410}
411
412pub fn read_mutable_member<'a>(
417 reader: &mut BufferReader<'a>,
418) -> Result<Option<MutableMember<'a>>, DecodeError> {
419 if reader.remaining() == 0 {
420 return Ok(None);
421 }
422 let emheader = reader.read_u32()?;
423 let must_understand = (emheader >> 31) & 1 == 1;
424 let lc_raw = ((emheader >> 28) & 0b0111) as u8;
425 let member_id = emheader & 0x0FFF_FFFF;
426 let length_code = LengthCode::from_wire(lc_raw).ok_or_else(|| DecodeError::LengthExceeded {
427 announced: usize::from(lc_raw),
428 remaining: 0,
429 offset: reader.position(),
430 })?;
431
432 let framing_word = if length_code.has_nextint() {
433 reader.read_u32()?
435 } else if length_code.reuses_leading_len() {
436 reader.peek_u32()?
439 } else {
440 0
441 };
442
443 let body_len_u64 = length_code.body_len(framing_word);
444 let body_len = usize::try_from(body_len_u64).map_err(|_| DecodeError::LengthExceeded {
445 announced: usize::MAX,
446 remaining: reader.remaining(),
447 offset: reader.position(),
448 })?;
449 if body_len > reader.remaining() {
450 return Err(DecodeError::LengthExceeded {
451 announced: body_len,
452 remaining: reader.remaining(),
453 offset: reader.position(),
454 });
455 }
456 let body = reader.read_bytes(body_len)?;
457 Ok(Some(MutableMember {
458 member_id,
459 must_understand,
460 length_code,
461 body,
462 }))
463}
464
465pub fn read_all_mutable_members<'a>(
471 reader: &mut BufferReader<'a>,
472) -> Result<Vec<MutableMember<'a>>, DecodeError> {
473 let mut out = Vec::new();
474 while let Some(m) = read_mutable_member(reader)? {
475 out.push(m);
476 }
477 Ok(out)
478}
479
480pub fn encode_final<F>(writer: &mut BufferWriter, body: F) -> Result<(), EncodeError>
491where
492 F: FnOnce(&mut BufferWriter) -> Result<(), EncodeError>,
493{
494 body(writer)
495}
496
497pub fn decode_final<T, F>(reader: &mut BufferReader<'_>, body: F) -> Result<T, DecodeError>
502where
503 F: FnOnce(&mut BufferReader<'_>) -> Result<T, DecodeError>,
504{
505 body(reader)
506}
507
508#[cfg(test)]
509mod tests {
510 #![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
511 use super::*;
512 use crate::Endianness;
513 use crate::encode::{CdrDecode, CdrEncode};
514 use alloc::vec;
515
516 #[test]
519 fn final_struct_two_u32_members() {
520 let mut w = BufferWriter::new(Endianness::Little);
521 encode_final(&mut w, |w| {
522 42u32.encode(w)?;
523 100u32.encode(w)?;
524 Ok(())
525 })
526 .unwrap();
527 let bytes = w.into_bytes();
528 assert_eq!(bytes.len(), 8);
530
531 let mut r = BufferReader::new(&bytes, Endianness::Little);
532 let (a, b) = decode_final(&mut r, |r| {
533 Ok::<_, DecodeError>((u32::decode(r)?, u32::decode(r)?))
534 })
535 .unwrap();
536 assert_eq!((a, b), (42, 100));
537 }
538
539 #[test]
542 fn appendable_struct_writes_dheader() {
543 let mut w = BufferWriter::new(Endianness::Little);
544 encode_appendable(&mut w, |w| {
545 42u32.encode(w)?;
546 7u8.encode(w)?;
547 Ok(())
548 })
549 .unwrap();
550 let bytes = w.into_bytes();
551 assert_eq!(&bytes[0..4], &[5, 0, 0, 0]); assert_eq!(&bytes[4..8], &[42, 0, 0, 0]); assert_eq!(bytes[8], 7);
556 assert_eq!(bytes.len(), 9);
557 }
558
559 #[test]
560 fn appendable_struct_roundtrip() {
561 let mut w = BufferWriter::new(Endianness::Little);
562 encode_appendable(&mut w, |w| {
563 42u32.encode(w)?;
564 7u8.encode(w)?;
565 Ok(())
566 })
567 .unwrap();
568 let bytes = w.into_bytes();
569
570 let mut r = BufferReader::new(&bytes, Endianness::Little);
571 let (a, b) = decode_appendable(&mut r, |r| {
572 Ok::<_, DecodeError>((u32::decode(r)?, u8::decode(r)?))
573 })
574 .unwrap();
575 assert_eq!((a, b), (42, 7));
576 }
577
578 #[test]
579 fn appendable_decoder_skips_extra_trailing_bytes() {
580 let mut w = BufferWriter::new(Endianness::Little);
584 encode_appendable(&mut w, |w| {
585 42u32.encode(w)?;
586 99u8.encode(w)?;
587 Ok(())
588 })
589 .unwrap();
590 let bytes = w.into_bytes();
591
592 let mut r = BufferReader::new(&bytes, Endianness::Little);
593 let only_first = decode_appendable(&mut r, u32::decode).unwrap();
594 assert_eq!(only_first, 42);
595 assert_eq!(r.remaining(), 0);
597 }
598
599 #[test]
600 fn appendable_decoder_rejects_announced_overrun() {
601 let bytes = [0xFFu8, 0xFF, 0xFF, 0xFF, 1, 2, 3];
602 let mut r = BufferReader::new(&bytes, Endianness::Little);
603 let res = decode_appendable(&mut r, u32::decode);
604 assert!(matches!(res, Err(DecodeError::LengthExceeded { .. })));
605 }
606
607 #[test]
612 fn mutable_struct_encoder_succeeds_when_all_required_emitted() {
613 let mut w = BufferWriter::new(Endianness::Little);
614 let mut enc = MutableStructEncoder::new(&mut w, vec![1, 2, 3]);
615 enc.encode_member(1, false, |w| 42u32.encode(w)).unwrap();
616 enc.encode_member(2, false, |w| 7u8.encode(w)).unwrap();
617 enc.encode_member(3, false, |w| 99u16.encode(w)).unwrap();
618 enc.finish().unwrap();
619 }
620
621 #[test]
622 fn mutable_encode_omitting_non_optional_member_errors() {
623 let mut w = BufferWriter::new(Endianness::Little);
624 let mut enc = MutableStructEncoder::new(&mut w, vec![1, 2, 3]);
625 enc.encode_member(1, false, |w| 42u32.encode(w)).unwrap();
626 enc.encode_member(3, false, |w| 99u16.encode(w)).unwrap();
628 let err = enc.finish().unwrap_err();
629 assert_eq!(err, EncodeError::MissingNonOptionalMember { member_id: 2 });
630 }
631
632 #[test]
633 fn mutable_encode_first_missing_id_is_reported() {
634 let mut w = BufferWriter::new(Endianness::Little);
635 let mut enc = MutableStructEncoder::new(&mut w, vec![10, 20, 30]);
636 enc.encode_member(20, false, |w| 5u32.encode(w)).unwrap();
637 let err = enc.finish().unwrap_err();
639 assert_eq!(err, EncodeError::MissingNonOptionalMember { member_id: 10 });
640 }
641
642 #[test]
643 fn mutable_encode_optional_only_with_no_required_succeeds() {
644 let mut w = BufferWriter::new(Endianness::Little);
647 let enc = MutableStructEncoder::new(&mut w, vec![]);
648 enc.finish().unwrap();
649 }
650
651 #[test]
652 fn mutable_encode_extra_optional_emitted_does_not_break_finish() {
653 let mut w = BufferWriter::new(Endianness::Little);
655 let mut enc = MutableStructEncoder::new(&mut w, vec![1]);
656 enc.encode_member(1, false, |w| 42u32.encode(w)).unwrap();
657 enc.encode_member(99, false, |w| 0u8.encode(w)).unwrap();
658 enc.finish().unwrap();
659 }
660
661 #[test]
662 fn mutable_encode_with_lc_variant_tracks_id() {
663 let mut w = BufferWriter::new(Endianness::Little);
664 let mut enc = MutableStructEncoder::new(&mut w, vec![5]);
665 enc.encode_member_lc(5, false, LengthCode::Lc0, |w| 0x42u8.encode(w))
666 .unwrap();
667 enc.finish().unwrap();
668 }
669
670 #[test]
671 fn mutable_member_emheader_layout() {
672 let mut w = BufferWriter::new(Endianness::Little);
673 encode_mutable_member(&mut w, 0x1234, false, |w| 42u32.encode(w)).unwrap();
674 let bytes = w.into_bytes();
675 assert_eq!(&bytes[0..4], &[0x34, 0x12, 0x00, 0x40]);
678 assert_eq!(&bytes[4..8], &[4, 0, 0, 0]);
680 assert_eq!(&bytes[8..12], &[42, 0, 0, 0]);
682 }
683
684 #[test]
685 fn mutable_member_must_understand_sets_high_bit() {
686 let mut w = BufferWriter::new(Endianness::Little);
687 encode_mutable_member(&mut w, 1, true, |w| 0u8.encode(w)).unwrap();
688 let bytes = w.into_bytes();
689 assert_eq!(&bytes[0..4], &[0x01, 0x00, 0x00, 0xC0]);
691 }
692
693 #[test]
694 fn mutable_member_rejects_oversized_id() {
695 let mut w = BufferWriter::new(Endianness::Little);
696 let res = encode_mutable_member(&mut w, 0xFFFF_FFFF, false, |w| 0u8.encode(w));
697 assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
698 }
699
700 #[test]
701 fn mutable_struct_roundtrip_two_members() {
702 let mut w = BufferWriter::new(Endianness::Little);
703 encode_mutable_member(&mut w, 1, false, |w| 42u32.encode(w)).unwrap();
704 encode_mutable_member(&mut w, 2, true, |w| 7u8.encode(w)).unwrap();
705 let bytes = w.into_bytes();
706
707 let mut r = BufferReader::new(&bytes, Endianness::Little);
708 let members = read_all_mutable_members(&mut r).unwrap();
709 assert_eq!(members.len(), 2);
710 assert_eq!(members[0].member_id, 1);
711 assert!(!members[0].must_understand);
712 assert_eq!(members[1].member_id, 2);
713 assert!(members[1].must_understand);
714
715 let mut sub = BufferReader::new(members[0].body, Endianness::Little);
716 assert_eq!(u32::decode(&mut sub).unwrap(), 42);
717 let mut sub = BufferReader::new(members[1].body, Endianness::Little);
718 assert_eq!(u8::decode(&mut sub).unwrap(), 7);
719 }
720
721 #[test]
722 fn mutable_member_reads_none_on_eof() {
723 let bytes: [u8; 0] = [];
724 let mut r = BufferReader::new(&bytes, Endianness::Little);
725 let res = read_mutable_member(&mut r).unwrap();
726 assert!(res.is_none());
727 }
728
729 #[test]
732 fn lc0_encode_decode_one_byte_body() {
733 let mut w = BufferWriter::new(Endianness::Little);
734 encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc0, |w| 0x42u8.encode(w)).unwrap();
735 let bytes = w.into_bytes();
736 assert_eq!(&bytes[0..4], &[0x01, 0x00, 0x00, 0x00]);
737 assert_eq!(bytes[4], 0x42);
738 assert_eq!(bytes.len(), 5);
739 let mut r = BufferReader::new(&bytes, Endianness::Little);
740 let m = read_mutable_member(&mut r).unwrap().unwrap();
741 assert_eq!(m.length_code, LengthCode::Lc0);
742 assert_eq!(m.body, &[0x42]);
743 }
744
745 #[test]
746 fn lc1_encode_decode_two_byte_body() {
747 let mut w = BufferWriter::new(Endianness::Little);
748 encode_mutable_member_lc(&mut w, 7, false, LengthCode::Lc1, |w| 0x1234u16.encode(w))
749 .unwrap();
750 let bytes = w.into_bytes();
751 assert_eq!(bytes.len(), 4 + 2);
752 let mut r = BufferReader::new(&bytes, Endianness::Little);
753 let m = read_mutable_member(&mut r).unwrap().unwrap();
754 assert_eq!(m.length_code, LengthCode::Lc1);
755 assert_eq!(m.body, &[0x34, 0x12]);
756 }
757
758 #[test]
759 fn lc2_encode_decode_four_byte_body() {
760 let mut w = BufferWriter::new(Endianness::Little);
761 encode_mutable_member_lc(&mut w, 9, true, LengthCode::Lc2, |w| 42u32.encode(w)).unwrap();
762 let bytes = w.into_bytes();
763 assert_eq!(&bytes[0..4], &[0x09, 0x00, 0x00, 0xA0]);
765 assert_eq!(bytes.len(), 4 + 4);
766 let mut r = BufferReader::new(&bytes, Endianness::Little);
767 let m = read_mutable_member(&mut r).unwrap().unwrap();
768 assert_eq!(m.length_code, LengthCode::Lc2);
769 assert!(m.must_understand);
770 }
771
772 #[test]
773 fn lc3_encode_decode_eight_byte_body() {
774 let mut w = BufferWriter::new(Endianness::Little);
775 encode_mutable_member_lc(&mut w, 11, false, LengthCode::Lc3, |w| {
776 0xDEADBEEF_CAFEBABEu64.encode(w)
777 })
778 .unwrap();
779 let bytes = w.into_bytes();
780 assert_eq!(bytes.len(), 4 + 8);
781 let mut r = BufferReader::new(&bytes, Endianness::Little);
782 let m = read_mutable_member(&mut r).unwrap().unwrap();
783 assert_eq!(m.length_code, LengthCode::Lc3);
784 assert_eq!(m.body.len(), 8);
785 }
786
787 #[test]
788 fn lc4_default_path_unchanged() {
789 let mut w = BufferWriter::new(Endianness::Little);
790 encode_mutable_member(&mut w, 1, false, |w| 42u32.encode(w)).unwrap();
791 let bytes = w.into_bytes();
792 assert_eq!(bytes.len(), 4 + 4 + 4);
793 let mut r = BufferReader::new(&bytes, Endianness::Little);
794 let m = read_mutable_member(&mut r).unwrap().unwrap();
795 assert_eq!(m.length_code, LengthCode::Lc4);
796 }
797
798 #[test]
799 fn lc5_aggregate_with_dheader() {
800 let mut w = BufferWriter::new(Endianness::Little);
801 encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc5, |w| {
802 8u32.encode(w)?;
803 42u32.encode(w)?;
804 7u32.encode(w)?;
805 Ok(())
806 })
807 .unwrap();
808 let bytes = w.into_bytes();
809 assert_eq!(bytes.len(), 16);
812 assert_eq!(&bytes[4..8], &[8, 0, 0, 0]);
815 let mut r = BufferReader::new(&bytes, Endianness::Little);
816 let m = read_mutable_member(&mut r).unwrap().unwrap();
817 assert_eq!(m.length_code, LengthCode::Lc5);
818 assert_eq!(m.body.len(), 12);
819 }
820
821 #[test]
822 fn lc6_array_of_4byte_primitives() {
823 let mut w = BufferWriter::new(Endianness::Little);
824 encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |w| {
825 12u32.encode(w)?;
826 10u32.encode(w)?;
827 20u32.encode(w)?;
828 30u32.encode(w)?;
829 Ok(())
830 })
831 .unwrap();
832 let bytes = w.into_bytes();
833 assert_eq!(&bytes[4..8], &[12, 0, 0, 0]);
836 assert_eq!(bytes.len(), 4 + 16);
837 let mut r = BufferReader::new(&bytes, Endianness::Little);
838 let m = read_mutable_member(&mut r).unwrap().unwrap();
839 assert_eq!(m.length_code, LengthCode::Lc6);
840 assert_eq!(m.body.len(), 16);
841 }
842
843 #[test]
844 fn lc6_lc7_roundtrip_against_cyclone_sample() {
845 let mut w = BufferWriter::new(Endianness::Little);
853 encode_mutable_member_lc(&mut w, 0xABCD, false, LengthCode::Lc6, |w| {
856 400u32.encode(w)?;
858 for i in 0..100u32 {
859 i.encode(w)?;
860 }
861 Ok(())
862 })
863 .unwrap();
864 let bytes = w.into_bytes();
865
866 assert_eq!(&bytes[0..4], &[0xCD, 0xAB, 0x00, 0x60]);
869 assert_eq!(&bytes[4..8], &[0x90, 0x01, 0, 0]);
872 assert_eq!(bytes.len(), 4 + 404);
874
875 let mut r = BufferReader::new(&bytes, Endianness::Little);
877 let m = read_mutable_member(&mut r).unwrap().unwrap();
878 assert_eq!(m.length_code, LengthCode::Lc6);
879 assert_eq!(m.member_id, 0xABCD);
880 assert_eq!(m.body.len(), 404);
881 }
882
883 #[test]
884 fn lc6_with_many_elements_decodes_correctly() {
885 let mut w = BufferWriter::new(Endianness::Little);
889 encode_mutable_member_lc(&mut w, 5, false, LengthCode::Lc6, |w| {
890 280_000u32.encode(w)?;
892 for i in 0..70_000u32 {
893 i.encode(w)?;
894 }
895 Ok(())
896 })
897 .unwrap();
898 let bytes = w.into_bytes();
899 let leading = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
901 assert_eq!(leading, 280_000);
902 let mut r = BufferReader::new(&bytes, Endianness::Little);
903 let m = read_mutable_member(&mut r).unwrap().unwrap();
904 assert_eq!(m.body.len(), 4 + 70_000 * 4);
906 }
907
908 #[test]
909 fn lc7_array_of_8byte_primitives() {
910 let mut w = BufferWriter::new(Endianness::Little);
911 encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc7, |w| {
912 16u32.encode(w)?;
913 w.write_bytes(&100u64.to_le_bytes())?;
914 w.write_bytes(&200u64.to_le_bytes())?;
915 Ok(())
916 })
917 .unwrap();
918 let bytes = w.into_bytes();
919 assert_eq!(&bytes[4..8], &[16, 0, 0, 0]);
922 assert_eq!(bytes.len(), 4 + 20);
923 let mut r = BufferReader::new(&bytes, Endianness::Little);
924 let m = read_mutable_member(&mut r).unwrap().unwrap();
925 assert_eq!(m.length_code, LengthCode::Lc7);
926 assert_eq!(m.body.len(), 20);
927 }
928
929 #[test]
930 fn lc0_rejects_wrong_body_size() {
931 let mut w = BufferWriter::new(Endianness::Little);
932 let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc0, |w| 42u32.encode(w));
933 assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
934 }
935
936 #[test]
937 fn lc6_rejects_misaligned_body() {
938 let mut w = BufferWriter::new(Endianness::Little);
939 let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |w| {
940 0u32.encode(w)?;
941 0u8.encode(w)?;
942 0u8.encode(w)?;
943 0u8.encode(w)?;
944 Ok(())
945 });
946 assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
947 }
948
949 #[test]
950 fn length_code_body_len_calculation() {
951 assert_eq!(LengthCode::Lc0.body_len(0), 1);
952 assert_eq!(LengthCode::Lc1.body_len(0), 2);
953 assert_eq!(LengthCode::Lc2.body_len(0), 4);
954 assert_eq!(LengthCode::Lc3.body_len(0), 8);
955 assert_eq!(LengthCode::Lc4.body_len(100), 100);
957 assert_eq!(LengthCode::Lc5.body_len(20), 24);
960 assert_eq!(LengthCode::Lc6.body_len(12), 16);
961 assert_eq!(LengthCode::Lc7.body_len(16), 20);
962 }
963
964 #[test]
965 fn length_code_has_nextint_flag() {
966 assert!(!LengthCode::Lc0.has_nextint());
968 assert!(!LengthCode::Lc3.has_nextint());
969 assert!(LengthCode::Lc4.has_nextint());
970 assert!(!LengthCode::Lc5.has_nextint());
971 assert!(!LengthCode::Lc7.has_nextint());
972 assert!(!LengthCode::Lc4.reuses_leading_len());
974 assert!(LengthCode::Lc5.reuses_leading_len());
975 assert!(LengthCode::Lc6.reuses_leading_len());
976 assert!(LengthCode::Lc7.reuses_leading_len());
977 }
978
979 #[test]
980 fn length_code_from_wire_roundtrip() {
981 for v in 0..=7u8 {
982 let lc = LengthCode::from_wire(v).expect("valid");
983 assert_eq!(lc as u8, v);
984 }
985 assert!(LengthCode::from_wire(8).is_none());
986 }
987
988 #[test]
991 fn appendable_in_mutable_member() {
992 let mut w = BufferWriter::new(Endianness::Little);
993 encode_mutable_member(&mut w, 5, false, |w| {
994 encode_appendable(w, |w| {
995 42u32.encode(w)?;
996 100u32.encode(w)?;
997 Ok(())
998 })
999 })
1000 .unwrap();
1001 let bytes = w.into_bytes();
1002 let mut r = BufferReader::new(&bytes, Endianness::Little);
1003 let m = read_mutable_member(&mut r).unwrap().unwrap();
1004 assert_eq!(m.member_id, 5);
1005 let mut sub = BufferReader::new(m.body, Endianness::Little);
1006 let (a, b) = decode_appendable(&mut sub, |r| {
1007 Ok::<_, DecodeError>((u32::decode(r)?, u32::decode(r)?))
1008 })
1009 .unwrap();
1010 assert_eq!((a, b), (42, 100));
1011 }
1012
1013 #[test]
1018 fn mutable_member_id_at_28bit_max_accepted() {
1019 let mut w = BufferWriter::new(Endianness::Little);
1020 let res = encode_mutable_member_lc(&mut w, 0x0FFF_FFFF, false, LengthCode::Lc2, |inner| {
1021 u32::encode(&0u32, inner)
1022 });
1023 assert!(
1024 res.is_ok(),
1025 "member_id=0x0FFFFFFF must succeed, got {res:?}"
1026 );
1027 }
1028
1029 #[test]
1033 fn mutable_member_id_29bit_rejected() {
1034 let mut w = BufferWriter::new(Endianness::Little);
1035 let res = encode_mutable_member_lc(&mut w, 0x1000_0000, false, LengthCode::Lc2, |inner| {
1036 u32::encode(&0u32, inner)
1037 });
1038 assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
1039 }
1040
1041 #[test]
1044 fn lc6_body_len_less_than_4_rejected() {
1045 for short_len in [0usize, 1, 2, 3] {
1046 let mut w = BufferWriter::new(Endianness::Little);
1047 let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |inner| {
1048 inner.write_bytes(&vec![0u8; short_len])
1049 });
1050 assert!(
1051 matches!(res, Err(EncodeError::ValueOutOfRange { .. })),
1052 "Lc6 with body_len={short_len} must error, got {res:?}"
1053 );
1054 }
1055 }
1056
1057 #[test]
1060 fn lc6_body_len_exactly_4_accepted() {
1061 let mut w = BufferWriter::new(Endianness::Little);
1062 let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |inner| {
1063 inner.write_bytes(&[0u8; 4])
1064 });
1065 assert!(res.is_ok(), "Lc6 body_len=4 must succeed, got {res:?}");
1066 }
1067
1068 #[test]
1073 fn lc6_no_separate_nextint_reuses_dheader() {
1074 let mut w = BufferWriter::new(Endianness::Little);
1075 encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |inner| {
1077 8u32.encode(inner)?;
1078 10u32.encode(inner)?;
1079 20u32.encode(inner)?;
1080 Ok(())
1081 })
1082 .unwrap();
1083 let bytes = w.into_bytes();
1084 assert_eq!(bytes.len(), 16);
1087 let leading = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
1089 assert_eq!(
1090 leading, 8,
1091 "leading word is the reused DHEADER, not a separate NEXTINT"
1092 );
1093 }
1094
1095 #[test]
1098 fn lc7_body_len_less_than_4_rejected() {
1099 for short_len in [0usize, 1, 2, 3] {
1100 let mut w = BufferWriter::new(Endianness::Little);
1101 let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc7, |inner| {
1102 inner.write_bytes(&vec![0u8; short_len])
1103 });
1104 assert!(
1105 matches!(res, Err(EncodeError::ValueOutOfRange { .. })),
1106 "Lc7 with body_len={short_len} must error"
1107 );
1108 }
1109 }
1110
1111 #[test]
1114 fn lc7_body_len_exactly_4_accepted() {
1115 let mut w = BufferWriter::new(Endianness::Little);
1116 let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc7, |inner| {
1117 inner.write_bytes(&[0u8; 4])
1118 });
1119 assert!(res.is_ok());
1120 }
1121
1122 #[test]
1125 fn lc7_no_separate_nextint_reuses_dheader() {
1126 let mut w = BufferWriter::new(Endianness::Little);
1127 encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc7, |inner| {
1128 16u32.encode(inner)?;
1129 inner.write_bytes(&100u64.to_le_bytes())?;
1130 inner.write_bytes(&200u64.to_le_bytes())?;
1131 Ok(())
1132 })
1133 .unwrap();
1134 let bytes = w.into_bytes();
1135 assert_eq!(bytes.len(), 24);
1138 let leading = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
1139 assert_eq!(
1140 leading, 16,
1141 "leading word is the reused DHEADER, not a separate NEXTINT"
1142 );
1143 }
1144
1145 #[test]
1150 fn lc6_misaligned_body_len_rejected() {
1151 let mut w = BufferWriter::new(Endianness::Little);
1152 let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc6, |inner| {
1153 inner.write_bytes(&[0u8; 6])
1154 });
1155 assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
1156 }
1157
1158 #[test]
1161 fn lc7_misaligned_body_len_rejected() {
1162 let mut w = BufferWriter::new(Endianness::Little);
1163 let res = encode_mutable_member_lc(&mut w, 1, false, LengthCode::Lc7, |inner| {
1164 inner.write_bytes(&[0u8; 10])
1165 });
1166 assert!(matches!(res, Err(EncodeError::ValueOutOfRange { .. })));
1167 }
1168
1169 #[test]
1173 fn emheader_combines_must_understand_lc_and_member_id() {
1174 let mut w = BufferWriter::new(Endianness::Little);
1175 encode_mutable_member_lc(&mut w, 0x123_4567, true, LengthCode::Lc6, |inner| {
1176 inner.write_bytes(&[0u8; 4])
1177 })
1178 .unwrap();
1179 let bytes = w.into_bytes();
1180 let mut h = [0u8; 4];
1181 h.copy_from_slice(&bytes[..4]);
1182 let emheader = u32::from_le_bytes(h);
1183 assert_eq!(emheader, 0xE123_4567);
1188 }
1189}