1use crate::{
4 CDR_LE_HEADER, CDR2_DELIMITED_LE_HEADER,
5 error::{DeserError, SerError},
6};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
19pub enum EncodingVersion {
20 #[default]
23 Xcdr1,
24 Xcdr2,
26}
27
28#[derive(Debug, Clone, Copy)]
32pub struct DHeaderMark(Option<usize>);
33
34impl DHeaderMark {
35 #[inline]
38 pub fn raw(self) -> Option<usize> {
39 self.0
40 }
41
42 #[inline]
44 pub fn from_raw(at: Option<usize>) -> Self {
45 DHeaderMark(at)
46 }
47}
48
49#[derive(Debug, Clone, Copy)]
53pub struct DHeaderScope(Option<usize>);
54
55impl DHeaderScope {
56 #[inline]
58 pub fn raw(self) -> Option<usize> {
59 self.0
60 }
61
62 #[inline]
64 pub fn from_raw(end: Option<usize>) -> Self {
65 DHeaderScope(end)
66 }
67}
68
69pub struct CdrWriter<'a> {
70 buf: &'a mut [u8],
71 pos: usize,
72 origin: usize,
75 version: EncodingVersion,
77 measure: bool,
86}
87
88impl<'a> CdrWriter<'a> {
89 pub fn new(buf: &'a mut [u8]) -> Self {
91 Self {
92 buf,
93 pos: 0,
94 origin: 0,
95 version: EncodingVersion::Xcdr1,
96 measure: false,
97 }
98 }
99
100 pub fn new_at(buf: &'a mut [u8], pos: usize) -> Result<Self, SerError> {
106 if pos > buf.len() {
107 return Err(SerError::BufferTooSmall);
108 }
109 Ok(Self {
110 buf,
111 pos,
112 origin: 0,
113 version: EncodingVersion::Xcdr1,
114 measure: false,
115 })
116 }
117
118 pub fn new_at_xcdr2(buf: &'a mut [u8], pos: usize) -> Result<Self, SerError> {
123 if pos > buf.len() {
124 return Err(SerError::BufferTooSmall);
125 }
126 Ok(Self {
127 buf,
128 pos,
129 origin: 0,
130 version: EncodingVersion::Xcdr2,
131 measure: false,
132 })
133 }
134
135 pub fn new_with_header(buf: &'a mut [u8]) -> Result<Self, SerError> {
142 if buf.len() < 4 {
143 return Err(SerError::BufferTooSmall);
144 }
145 buf[0..4].copy_from_slice(&CDR_LE_HEADER);
146 Ok(Self {
147 buf,
148 pos: 4,
149 origin: 4,
150 version: EncodingVersion::Xcdr1,
151 measure: false,
152 })
153 }
154
155 pub fn new_with_header_xcdr2(buf: &'a mut [u8]) -> Result<Self, SerError> {
160 if buf.len() < 4 {
161 return Err(SerError::BufferTooSmall);
162 }
163 buf[0..4].copy_from_slice(&CDR2_DELIMITED_LE_HEADER);
164 Ok(Self {
165 buf,
166 pos: 4,
167 origin: 4,
168 version: EncodingVersion::Xcdr2,
169 measure: false,
170 })
171 }
172
173 pub fn measuring(buf: &'a mut [u8], version: EncodingVersion) -> Self {
194 Self {
195 buf,
196 pos: 0,
197 origin: 0,
198 version,
199 measure: true,
200 }
201 }
202
203 #[inline]
205 pub fn version(&self) -> EncodingVersion {
206 self.version
207 }
208
209 #[inline]
215 pub fn begin_dheader(&mut self) -> Result<DHeaderMark, SerError> {
216 match self.version {
217 EncodingVersion::Xcdr1 => Ok(DHeaderMark(None)),
218 EncodingVersion::Xcdr2 => {
219 self.align(4)?;
220 if self.remaining() < 4 {
221 return Err(SerError::BufferTooSmall);
222 }
223 let at = self.pos;
224 if !self.measure {
225 self.buf[at..at + 4].copy_from_slice(&[0, 0, 0, 0]);
226 }
227 self.pos += 4;
228 Ok(DHeaderMark(Some(at)))
229 }
230 }
231 }
232
233 #[inline]
237 pub fn end_dheader(&mut self, mark: DHeaderMark) -> Result<(), SerError> {
238 if let Some(at) = mark.0 {
239 let size = (self.pos - (at + 4)) as u32;
240 if !self.measure {
241 self.buf[at..at + 4].copy_from_slice(&size.to_le_bytes());
242 }
243 }
244 Ok(())
245 }
246
247 #[inline]
249 pub fn position(&self) -> usize {
250 self.pos
251 }
252
253 #[inline]
255 pub fn remaining(&self) -> usize {
256 if self.measure {
257 return usize::MAX;
261 }
262 self.buf.len().saturating_sub(self.pos)
263 }
264
265 pub fn as_slice(&self) -> &[u8] {
267 &self.buf[..self.pos]
268 }
269
270 #[inline]
274 pub fn align(&mut self, alignment: usize) -> Result<(), SerError> {
275 let alignment = match self.version {
276 EncodingVersion::Xcdr2 => alignment.min(4),
277 EncodingVersion::Xcdr1 => alignment,
278 };
279 let offset = self.pos - self.origin;
280 let padding = (alignment - (offset % alignment)) % alignment;
281 if self.remaining() < padding {
282 return Err(SerError::BufferTooSmall);
283 }
284 for i in 0..padding {
286 if !self.measure {
287 self.buf[self.pos + i] = 0;
288 }
289 }
290 self.pos += padding;
291 Ok(())
292 }
293
294 #[inline]
296 pub fn write_u8(&mut self, value: u8) -> Result<(), SerError> {
297 if self.remaining() < 1 {
298 return Err(SerError::BufferTooSmall);
299 }
300 if !self.measure {
301 self.buf[self.pos] = value;
302 }
303 self.pos += 1;
304 Ok(())
305 }
306
307 #[inline]
309 pub fn write_bool(&mut self, value: bool) -> Result<(), SerError> {
310 self.write_u8(value as u8)
311 }
312
313 #[inline]
315 pub fn write_i8(&mut self, value: i8) -> Result<(), SerError> {
316 self.write_u8(value as u8)
317 }
318
319 #[inline]
321 pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), SerError> {
322 if self.remaining() < bytes.len() {
323 return Err(SerError::BufferTooSmall);
324 }
325 if !self.measure {
326 self.buf[self.pos..self.pos + bytes.len()].copy_from_slice(bytes);
327 }
328 self.pos += bytes.len();
329 Ok(())
330 }
331
332 #[inline]
334 pub fn write_u16(&mut self, value: u16) -> Result<(), SerError> {
335 self.align(2)?;
336 if self.remaining() < 2 {
337 return Err(SerError::BufferTooSmall);
338 }
339 if !self.measure {
340 self.buf[self.pos..self.pos + 2].copy_from_slice(&value.to_le_bytes());
341 }
342 self.pos += 2;
343 Ok(())
344 }
345
346 #[inline]
348 pub fn write_u32(&mut self, value: u32) -> Result<(), SerError> {
349 self.align(4)?;
350 if self.remaining() < 4 {
351 return Err(SerError::BufferTooSmall);
352 }
353 if !self.measure {
354 self.buf[self.pos..self.pos + 4].copy_from_slice(&value.to_le_bytes());
355 }
356 self.pos += 4;
357 Ok(())
358 }
359
360 #[inline]
362 pub fn write_u64(&mut self, value: u64) -> Result<(), SerError> {
363 self.align(8)?;
364 if self.remaining() < 8 {
365 return Err(SerError::BufferTooSmall);
366 }
367 if !self.measure {
368 self.buf[self.pos..self.pos + 8].copy_from_slice(&value.to_le_bytes());
369 }
370 self.pos += 8;
371 Ok(())
372 }
373
374 #[inline]
376 pub fn write_i16(&mut self, value: i16) -> Result<(), SerError> {
377 self.write_u16(value as u16)
378 }
379
380 #[inline]
382 pub fn write_i32(&mut self, value: i32) -> Result<(), SerError> {
383 self.write_u32(value as u32)
384 }
385
386 #[inline]
388 pub fn write_i64(&mut self, value: i64) -> Result<(), SerError> {
389 self.write_u64(value as u64)
390 }
391
392 #[inline]
394 pub fn write_f32(&mut self, value: f32) -> Result<(), SerError> {
395 self.write_u32(value.to_bits())
396 }
397
398 #[inline]
400 pub fn write_f64(&mut self, value: f64) -> Result<(), SerError> {
401 self.write_u64(value.to_bits())
402 }
403
404 pub fn write_string(&mut self, s: &str) -> Result<(), SerError> {
406 let len = s.len() + 1; if len > u32::MAX as usize {
408 return Err(SerError::StringTooLong);
409 }
410 self.write_u32(len as u32)?;
411 self.write_bytes(s.as_bytes())?;
412 self.write_u8(0)?; Ok(())
414 }
415
416 #[inline]
418 pub fn write_sequence_len(&mut self, len: usize) -> Result<(), SerError> {
419 if len > u32::MAX as usize {
420 return Err(SerError::SequenceTooLong);
421 }
422 self.write_u32(len as u32)
423 }
424}
425
426pub struct CdrReader<'a> {
430 buf: &'a [u8],
431 pos: usize,
432 origin: usize,
433 version: EncodingVersion,
436}
437
438impl<'a> CdrReader<'a> {
439 pub fn new(buf: &'a [u8]) -> Self {
441 Self {
442 buf,
443 pos: 0,
444 origin: 0,
445 version: EncodingVersion::Xcdr1,
446 }
447 }
448
449 pub fn new_at(buf: &'a [u8], pos: usize) -> Result<Self, DeserError> {
455 if pos > buf.len() {
456 return Err(DeserError::UnexpectedEof);
457 }
458 Ok(Self {
459 buf,
460 pos,
461 origin: 0,
462 version: EncodingVersion::Xcdr1,
463 })
464 }
465
466 pub fn new_at_xcdr2(buf: &'a [u8], pos: usize) -> Result<Self, DeserError> {
470 if pos > buf.len() {
471 return Err(DeserError::UnexpectedEof);
472 }
473 Ok(Self {
474 buf,
475 pos,
476 origin: 0,
477 version: EncodingVersion::Xcdr2,
478 })
479 }
480
481 pub fn new_with_header(buf: &'a [u8]) -> Result<Self, DeserError> {
485 if buf.len() < 4 {
486 return Err(DeserError::UnexpectedEof);
487 }
488 if buf[0] != 0x00 {
492 return Err(DeserError::InvalidHeader);
493 }
494 let version = match buf[1] {
495 0x00 | 0x01 => EncodingVersion::Xcdr1,
496 0x08 | 0x09 => EncodingVersion::Xcdr2,
497 _ => return Err(DeserError::InvalidHeader),
498 };
499 Ok(Self {
500 buf,
501 pos: 4,
502 origin: 4,
503 version,
504 })
505 }
506
507 #[inline]
509 pub fn position(&self) -> usize {
510 self.pos
511 }
512
513 #[inline]
523 pub fn is_at_origin(&self) -> bool {
524 self.pos == self.origin
525 }
526
527 #[inline]
529 pub fn remaining(&self) -> usize {
530 self.buf.len().saturating_sub(self.pos)
531 }
532
533 #[inline]
535 pub fn is_empty(&self) -> bool {
536 self.remaining() == 0
537 }
538
539 #[inline]
542 pub fn align(&mut self, alignment: usize) -> Result<(), DeserError> {
543 let alignment = match self.version {
544 EncodingVersion::Xcdr2 => alignment.min(4),
545 EncodingVersion::Xcdr1 => alignment,
546 };
547 let offset = self.pos - self.origin;
548 let padding = (alignment - (offset % alignment)) % alignment;
549 if self.remaining() < padding {
550 return Err(DeserError::UnexpectedEof);
551 }
552 self.pos += padding;
553 Ok(())
554 }
555
556 #[inline]
558 pub fn version(&self) -> EncodingVersion {
559 self.version
560 }
561
562 #[inline]
568 pub fn begin_dheader(&mut self) -> Result<DHeaderScope, DeserError> {
569 match self.version {
570 EncodingVersion::Xcdr1 => Ok(DHeaderScope(None)),
571 EncodingVersion::Xcdr2 => {
572 let size = self.read_u32()? as usize;
573 let end = self
574 .pos
575 .checked_add(size)
576 .ok_or(DeserError::UnexpectedEof)?;
577 if end > self.buf.len() {
578 return Err(DeserError::UnexpectedEof);
579 }
580 Ok(DHeaderScope(Some(end)))
581 }
582 }
583 }
584
585 #[inline]
590 pub fn end_dheader(&mut self, scope: DHeaderScope) -> Result<(), DeserError> {
591 if let Some(end) = scope.0 {
592 if self.pos > end {
593 return Err(DeserError::DHeaderOverrun);
594 }
595 self.pos = end;
596 }
597 Ok(())
598 }
599
600 #[inline]
602 pub fn read_u8(&mut self) -> Result<u8, DeserError> {
603 if self.remaining() < 1 {
604 return Err(DeserError::UnexpectedEof);
605 }
606 let value = self.buf[self.pos];
607 self.pos += 1;
608 Ok(value)
609 }
610
611 #[inline]
613 pub fn read_bool(&mut self) -> Result<bool, DeserError> {
614 Ok(self.read_u8()? != 0)
615 }
616
617 #[inline]
619 pub fn read_i8(&mut self) -> Result<i8, DeserError> {
620 Ok(self.read_u8()? as i8)
621 }
622
623 #[inline]
625 pub fn read_bytes(&mut self, len: usize) -> Result<&'a [u8], DeserError> {
626 if self.remaining() < len {
627 return Err(DeserError::UnexpectedEof);
628 }
629 let bytes = &self.buf[self.pos..self.pos + len];
630 self.pos += len;
631 Ok(bytes)
632 }
633
634 #[inline]
636 pub fn read_u16(&mut self) -> Result<u16, DeserError> {
637 self.align(2)?;
638 if self.remaining() < 2 {
639 return Err(DeserError::UnexpectedEof);
640 }
641 let value = u16::from_le_bytes([self.buf[self.pos], self.buf[self.pos + 1]]);
642 self.pos += 2;
643 Ok(value)
644 }
645
646 #[inline]
648 pub fn read_u32(&mut self) -> Result<u32, DeserError> {
649 self.align(4)?;
650 if self.remaining() < 4 {
651 return Err(DeserError::UnexpectedEof);
652 }
653 let value = u32::from_le_bytes([
654 self.buf[self.pos],
655 self.buf[self.pos + 1],
656 self.buf[self.pos + 2],
657 self.buf[self.pos + 3],
658 ]);
659 self.pos += 4;
660 Ok(value)
661 }
662
663 #[inline]
665 pub fn read_u64(&mut self) -> Result<u64, DeserError> {
666 self.align(8)?;
667 if self.remaining() < 8 {
668 return Err(DeserError::UnexpectedEof);
669 }
670 let value = u64::from_le_bytes([
671 self.buf[self.pos],
672 self.buf[self.pos + 1],
673 self.buf[self.pos + 2],
674 self.buf[self.pos + 3],
675 self.buf[self.pos + 4],
676 self.buf[self.pos + 5],
677 self.buf[self.pos + 6],
678 self.buf[self.pos + 7],
679 ]);
680 self.pos += 8;
681 Ok(value)
682 }
683
684 #[inline]
686 pub fn read_i16(&mut self) -> Result<i16, DeserError> {
687 Ok(self.read_u16()? as i16)
688 }
689
690 #[inline]
692 pub fn read_i32(&mut self) -> Result<i32, DeserError> {
693 Ok(self.read_u32()? as i32)
694 }
695
696 #[inline]
698 pub fn read_i64(&mut self) -> Result<i64, DeserError> {
699 Ok(self.read_u64()? as i64)
700 }
701
702 #[inline]
704 pub fn read_f32(&mut self) -> Result<f32, DeserError> {
705 Ok(f32::from_bits(self.read_u32()?))
706 }
707
708 #[inline]
710 pub fn read_f64(&mut self) -> Result<f64, DeserError> {
711 Ok(f64::from_bits(self.read_u64()?))
712 }
713
714 pub fn read_string(&mut self) -> Result<&'a str, DeserError> {
718 let len = self.read_u32()? as usize;
719 if len == 0 {
720 return Err(DeserError::InvalidData);
721 }
722 if self.remaining() < len {
723 return Err(DeserError::UnexpectedEof);
724 }
725 let bytes = &self.buf[self.pos..self.pos + len - 1];
727 self.pos += len;
728 core::str::from_utf8(bytes).map_err(|_| DeserError::InvalidUtf8)
729 }
730
731 #[inline]
733 pub fn read_sequence_len(&mut self) -> Result<usize, DeserError> {
734 Ok(self.read_u32()? as usize)
735 }
736
737 pub fn read_slice_u8(&mut self) -> Result<&'a [u8], DeserError> {
744 let len = self.read_u32()? as usize;
745 self.read_bytes(len)
746 }
747
748 pub fn read_slice_i8(&mut self) -> Result<&'a [u8], DeserError> {
750 self.read_slice_u8()
752 }
753
754 pub fn read_slice_bool(&mut self) -> Result<&'a [u8], DeserError> {
759 self.read_slice_u8()
760 }
761
762 pub fn read_le_slice<T: LeDecode>(&mut self) -> Result<LeSliceView<'a, T>, DeserError> {
772 let len = self.read_u32()? as usize;
773 self.align(T::SIZE)?;
774 let byte_len = len * T::SIZE;
775 let bytes = self.read_bytes(byte_len)?;
776 Ok(LeSliceView::new(bytes))
777 }
778}
779
780pub trait LeDecode: Sized + Copy {
785 const SIZE: usize;
787 fn from_le(bytes: &[u8]) -> Self;
789}
790
791macro_rules! impl_le_decode {
792 ($($t:ty),+ $(,)?) => {$(
793 impl LeDecode for $t {
794 const SIZE: usize = core::mem::size_of::<$t>();
795 #[inline]
796 fn from_le(bytes: &[u8]) -> Self {
797 let mut buf = [0u8; core::mem::size_of::<$t>()];
798 buf.copy_from_slice(bytes);
799 <$t>::from_le_bytes(buf)
800 }
801 }
802 )+};
803}
804impl_le_decode!(u16, i16, u32, i32, u64, i64, f32, f64);
805
806#[derive(Clone, Copy)]
812pub struct LeSliceView<'a, T> {
813 bytes: &'a [u8],
814 _marker: core::marker::PhantomData<fn() -> T>,
815}
816
817impl<'a, T: LeDecode> LeSliceView<'a, T> {
818 #[inline]
821 pub fn new(bytes: &'a [u8]) -> Self {
822 Self {
823 bytes,
824 _marker: core::marker::PhantomData,
825 }
826 }
827
828 #[inline]
830 pub fn len(&self) -> usize {
831 self.bytes.len() / T::SIZE
832 }
833
834 #[inline]
836 pub fn is_empty(&self) -> bool {
837 self.bytes.is_empty()
838 }
839
840 #[inline]
842 pub fn as_bytes(&self) -> &'a [u8] {
843 self.bytes
844 }
845
846 #[inline]
848 pub fn get(&self, index: usize) -> Option<T> {
849 let start = index.checked_mul(T::SIZE)?;
850 let end = start.checked_add(T::SIZE)?;
851 self.bytes.get(start..end).map(T::from_le)
852 }
853
854 #[inline]
856 pub fn iter(&self) -> impl Iterator<Item = T> + 'a {
857 let bytes = self.bytes;
858 (0..bytes.len() / T::SIZE).map(move |i| T::from_le(&bytes[i * T::SIZE..(i + 1) * T::SIZE]))
859 }
860}
861
862#[cfg(test)]
863mod tests {
864 use super::*;
865
866 fn serialize_header(w: &mut CdrWriter) {
876 let h = w.begin_dheader().unwrap();
877 let t = w.begin_dheader().unwrap();
879 w.write_i32(7).unwrap();
880 w.write_u32(9).unwrap();
881 w.end_dheader(t).unwrap();
882 w.write_string("ab").unwrap();
883 w.end_dheader(h).unwrap();
884 }
885
886 #[test]
901 fn xcdr1_header_matches_live_jazzy_wire_bytes() {
902 let mut buf = [0u8; 64];
903 let n = {
904 let mut w = CdrWriter::new_with_header(&mut buf).unwrap();
905 serialize_header(&mut w);
906 w.position()
907 };
908 let jazzy: [u8; 19] = [
911 0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00,
912 0x00, 0x00, 0x61, 0x62, 0x00,
913 ];
914 assert_eq!(
915 &buf[..n],
916 &jazzy,
917 "nano-ros XCDR1 Header must equal live Jazzy wire bytes"
918 );
919 }
920
921 #[test]
925 fn xcdr2_header_has_dheaders_and_roundtrips() {
926 let mut buf = [0u8; 64];
927 let n = {
928 let mut w = CdrWriter::new_with_header_xcdr2(&mut buf).unwrap();
929 serialize_header(&mut w);
930 w.position()
931 };
932 assert_eq!(&buf[0..2], &[0x00, 0x09], "XCDR2 DELIMITED encapsulation");
933 let mut r = CdrReader::new_with_header(&buf[..n]).unwrap();
935 assert_eq!(r.version(), EncodingVersion::Xcdr2);
936 let h = r.begin_dheader().unwrap();
937 let t = r.begin_dheader().unwrap();
938 assert_eq!(r.read_i32().unwrap(), 7);
939 assert_eq!(r.read_u32().unwrap(), 9);
940 r.end_dheader(t).unwrap();
941 assert_eq!(r.read_string().unwrap(), "ab");
942 r.end_dheader(h).unwrap();
943 }
944
945 #[test]
946 fn xcdr2_nested_dheader_layout_and_roundtrip() {
947 let mut buf = [0u8; 64];
948 {
949 let mut w = CdrWriter::new_with_header_xcdr2(&mut buf).unwrap();
950 let top = w.begin_dheader().unwrap();
951 let time = w.begin_dheader().unwrap();
953 w.write_i32(7).unwrap();
954 w.write_u32(9).unwrap();
955 w.end_dheader(time).unwrap();
956 w.write_string("ab").unwrap();
958 w.end_dheader(top).unwrap();
959 }
960 assert_eq!(&buf[0..4], &[0x00, 0x09, 0x00, 0x00]);
962 assert_eq!(u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]), 8);
965 assert_eq!(u32::from_le_bytes([buf[12], buf[13], buf[14], buf[15]]), 7);
967 assert_eq!(u32::from_le_bytes([buf[16], buf[17], buf[18], buf[19]]), 9);
968
969 let mut r = CdrReader::new_with_header(&buf).unwrap();
971 assert_eq!(r.version(), EncodingVersion::Xcdr2);
972 let top = r.begin_dheader().unwrap();
973 let time = r.begin_dheader().unwrap();
974 assert_eq!(r.read_i32().unwrap(), 7);
975 assert_eq!(r.read_u32().unwrap(), 9);
976 r.end_dheader(time).unwrap();
977 assert_eq!(r.read_string().unwrap(), "ab");
978 r.end_dheader(top).unwrap();
979 }
980
981 #[test]
985 fn xcdr1_dheader_calls_are_byte_identical_noops() {
986 let mut a = [0u8; 32];
987 let mut b = [0u8; 32];
988 let na = {
989 let mut w = CdrWriter::new_with_header(&mut a).unwrap();
990 let d = w.begin_dheader().unwrap();
991 w.write_i32(-2).unwrap();
992 w.write_u32(3).unwrap();
993 w.end_dheader(d).unwrap();
994 w.position()
995 };
996 let nb = {
997 let mut w = CdrWriter::new_with_header(&mut b).unwrap();
998 w.write_i32(-2).unwrap();
999 w.write_u32(3).unwrap();
1000 w.position()
1001 };
1002 assert_eq!(na, nb);
1003 assert_eq!(a[..na], b[..nb]);
1004 }
1005
1006 #[test]
1009 fn xcdr2_reader_skips_unknown_trailing_members() {
1010 let mut buf = [0u8; 32];
1011 {
1012 let mut w = CdrWriter::new_with_header_xcdr2(&mut buf).unwrap();
1013 let d = w.begin_dheader().unwrap();
1014 w.write_i32(11).unwrap();
1015 w.write_u32(22).unwrap();
1017 w.end_dheader(d).unwrap();
1018 }
1019 let mut r = CdrReader::new_with_header(&buf).unwrap();
1020 let d = r.begin_dheader().unwrap();
1021 assert_eq!(r.read_i32().unwrap(), 11);
1022 assert_eq!(r.position(), 12);
1025 r.end_dheader(d).unwrap();
1026 assert_eq!(
1027 r.position(),
1028 16,
1029 "end_dheader must skip the unknown trailing member"
1030 );
1031 }
1032
1033 #[test]
1034 fn test_write_read_u8() {
1035 let mut buf = [0u8; 16];
1036 let mut writer = CdrWriter::new(&mut buf);
1037 writer.write_u8(0x42).unwrap();
1038 writer.write_u8(0xFF).unwrap();
1039
1040 let mut reader = CdrReader::new(&buf);
1041 assert_eq!(reader.read_u8().unwrap(), 0x42);
1042 assert_eq!(reader.read_u8().unwrap(), 0xFF);
1043 }
1044
1045 #[test]
1046 fn le_slice_view_decodes_unaligned_f32() {
1047 let vals = [1.5f32, -2.25, 3.0e10, 0.0];
1050 let mut backing = [0u8; 1 + 4 * 4];
1051 backing[0] = 0xAA; for (i, v) in vals.iter().enumerate() {
1053 backing[1 + i * 4..1 + i * 4 + 4].copy_from_slice(&v.to_le_bytes());
1054 }
1055 let view: LeSliceView<f32> = LeSliceView::new(&backing[1..]);
1056 assert_eq!(view.len(), 4);
1057 assert!(!view.is_empty());
1058 for (i, v) in vals.iter().enumerate() {
1059 assert_eq!(view.get(i).unwrap(), *v);
1060 }
1061 assert_eq!(view.get(4), None);
1062 let collected: heapless::Vec<f32, 4> = view.iter().collect();
1063 assert_eq!(&collected[..], &vals[..]);
1064 }
1065
1066 #[test]
1067 fn read_le_slice_roundtrips_through_cdr() {
1068 let vals = [10u16, 4000, 65535, 1];
1071 let mut buf = [0u8; 64];
1072 let written = {
1073 let mut w = CdrWriter::new_with_header(&mut buf).unwrap();
1074 w.write_sequence_len(vals.len()).unwrap();
1075 for v in &vals {
1076 w.write_u16(*v).unwrap();
1077 }
1078 w.position()
1079 };
1080 let mut reader = CdrReader::new_with_header(&buf[..written]).unwrap();
1081 let view = reader.read_le_slice::<u16>().unwrap();
1082 assert_eq!(view.len(), vals.len());
1083 for (i, v) in vals.iter().enumerate() {
1084 assert_eq!(view.get(i).unwrap(), *v);
1085 }
1086 }
1087
1088 #[test]
1089 fn test_write_read_u32_alignment() {
1090 let mut buf = [0u8; 16];
1091 let mut writer = CdrWriter::new(&mut buf);
1092 writer.write_u8(0x01).unwrap(); writer.write_u32(0x12345678).unwrap(); assert_eq!(writer.position(), 8); let mut reader = CdrReader::new(&buf);
1098 assert_eq!(reader.read_u8().unwrap(), 0x01);
1099 assert_eq!(reader.read_u32().unwrap(), 0x12345678);
1100 }
1101
1102 #[test]
1103 fn test_write_read_string() {
1104 let mut buf = [0u8; 32];
1105 let mut writer = CdrWriter::new(&mut buf);
1106 writer.write_string("Hello").unwrap();
1107
1108 let mut reader = CdrReader::new(&buf);
1109 assert_eq!(reader.read_string().unwrap(), "Hello");
1110 }
1111
1112 #[test]
1113 fn test_encapsulation_header() {
1114 let mut buf = [0u8; 32];
1115 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1116 writer.write_u32(42).unwrap();
1117
1118 assert_eq!(&buf[0..4], &CDR_LE_HEADER);
1119
1120 let mut reader = CdrReader::new_with_header(&buf).unwrap();
1121 assert_eq!(reader.read_u32().unwrap(), 42);
1122 }
1123
1124 #[test]
1125 fn test_alignment_with_header() {
1126 let mut buf = [0u8; 32];
1127 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1128 writer.write_u8(0x01).unwrap(); writer.write_u32(0xDEADBEEF).unwrap(); assert_eq!(writer.position(), 12); let mut reader = CdrReader::new_with_header(&buf).unwrap();
1135 assert_eq!(reader.read_u8().unwrap(), 0x01);
1136 assert_eq!(reader.read_u32().unwrap(), 0xDEADBEEF);
1137 }
1138}
1139
1140#[cfg(test)]
1145mod ghost_checks {
1146 use super::*;
1147 use nros_ghost_types::CdrGhost;
1148
1149 fn ghost_from_writer(w: &CdrWriter) -> CdrGhost {
1152 CdrGhost {
1153 buf_len: w.buf.len(),
1154 pos: w.pos,
1155 origin: w.origin,
1156 }
1157 }
1158
1159 #[test]
1160 fn ghost_new_state() {
1161 let mut buf = [0u8; 64];
1162 let writer = CdrWriter::new(&mut buf);
1163 let ghost = ghost_from_writer(&writer);
1164 assert_eq!(ghost.pos, 0);
1165 assert_eq!(ghost.origin, 0);
1166 assert_eq!(ghost.buf_len, 64);
1167 }
1168
1169 #[test]
1170 fn ghost_header_origin() {
1171 let mut buf = [0u8; 64];
1172 let writer = CdrWriter::new_with_header(&mut buf).unwrap();
1173 let ghost = ghost_from_writer(&writer);
1174 assert_eq!(ghost.pos, 4);
1175 assert_eq!(ghost.origin, 4);
1176 }
1177
1178 #[test]
1179 fn ghost_position_invariant() {
1180 let mut buf = [0u8; 64];
1181 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1182 writer.write_u32(42).unwrap();
1183 let ghost = ghost_from_writer(&writer);
1184 assert_eq!(ghost.pos + writer.remaining(), ghost.buf_len);
1186 }
1187
1188 #[test]
1189 fn test_read_slice_u8() {
1190 let mut buf = [0u8; 64];
1191 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1192 writer.write_u32(3).unwrap(); writer.write_u8(0x10).unwrap();
1195 writer.write_u8(0x20).unwrap();
1196 writer.write_u8(0x30).unwrap();
1197 let len = writer.position();
1198
1199 let mut reader = CdrReader::new_with_header(&buf[..len]).unwrap();
1200 let slice = reader.read_slice_u8().unwrap();
1201 assert_eq!(slice, &[0x10, 0x20, 0x30]);
1202 }
1203
1204 #[test]
1205 fn test_read_slice_u8_empty() {
1206 let mut buf = [0u8; 64];
1207 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1208 writer.write_u32(0).unwrap(); let len = writer.position();
1210
1211 let mut reader = CdrReader::new_with_header(&buf[..len]).unwrap();
1212 let slice = reader.read_slice_u8().unwrap();
1213 assert!(slice.is_empty());
1214 }
1215}
1216
1217#[cfg(kani)]
1222mod verification {
1223 use super::*;
1224
1225 #[kani::proof]
1228 #[kani::unwind(5)]
1229 fn cdr_write_u8_no_panic() {
1230 let mut buf = [0u8; 8];
1231 let mut writer = CdrWriter::new(&mut buf);
1232 let val: u8 = kani::any();
1233 let _ = writer.write_u8(val);
1234 }
1235
1236 #[kani::proof]
1237 #[kani::unwind(5)]
1238 fn cdr_write_bool_no_panic() {
1239 let mut buf = [0u8; 8];
1240 let mut writer = CdrWriter::new(&mut buf);
1241 let val: bool = kani::any();
1242 let _ = writer.write_bool(val);
1243 }
1244
1245 #[kani::proof]
1246 #[kani::unwind(5)]
1247 fn cdr_write_i16_no_panic() {
1248 let mut buf = [0u8; 16];
1249 let mut writer = CdrWriter::new(&mut buf);
1250 let val: i16 = kani::any();
1251 let _ = writer.write_i16(val);
1252 }
1253
1254 #[kani::proof]
1255 #[kani::unwind(5)]
1256 fn cdr_write_i32_no_panic() {
1257 let mut buf = [0u8; 16];
1258 let mut writer = CdrWriter::new(&mut buf);
1259 let val: i32 = kani::any();
1260 let _ = writer.write_i32(val);
1261 }
1262
1263 #[kani::proof]
1264 #[kani::unwind(5)]
1265 fn cdr_write_i64_no_panic() {
1266 let mut buf = [0u8; 16];
1267 let mut writer = CdrWriter::new(&mut buf);
1268 let val: i64 = kani::any();
1269 let _ = writer.write_i64(val);
1270 }
1271
1272 #[kani::proof]
1273 #[kani::unwind(5)]
1274 fn cdr_write_f32_no_panic() {
1275 let mut buf = [0u8; 16];
1276 let mut writer = CdrWriter::new(&mut buf);
1277 let val: f32 = kani::any();
1278 let _ = writer.write_f32(val);
1279 }
1280
1281 #[kani::proof]
1282 #[kani::unwind(5)]
1283 fn cdr_write_f64_no_panic() {
1284 let mut buf = [0u8; 16];
1285 let mut writer = CdrWriter::new(&mut buf);
1286 let val: f64 = kani::any();
1287 let _ = writer.write_f64(val);
1288 }
1289
1290 #[kani::proof]
1293 #[kani::unwind(5)]
1294 fn cdr_roundtrip_u8() {
1295 let mut buf = [0u8; 8];
1296 let val: u8 = kani::any();
1297 let len = {
1298 let mut writer = CdrWriter::new(&mut buf);
1299 writer.write_u8(val).unwrap();
1300 writer.position()
1301 };
1302 let mut reader = CdrReader::new(&buf[..len]);
1303 assert_eq!(reader.read_u8().unwrap(), val);
1304 }
1305
1306 #[kani::proof]
1307 #[kani::unwind(5)]
1308 fn cdr_roundtrip_bool() {
1309 let mut buf = [0u8; 8];
1310 let val: bool = kani::any();
1311 let len = {
1312 let mut writer = CdrWriter::new(&mut buf);
1313 writer.write_bool(val).unwrap();
1314 writer.position()
1315 };
1316 let mut reader = CdrReader::new(&buf[..len]);
1317 assert_eq!(reader.read_bool().unwrap(), val);
1318 }
1319
1320 #[kani::proof]
1321 #[kani::unwind(5)]
1322 fn cdr_roundtrip_i16() {
1323 let mut buf = [0u8; 16];
1324 let val: i16 = kani::any();
1325 let len = {
1326 let mut writer = CdrWriter::new(&mut buf);
1327 writer.write_i16(val).unwrap();
1328 writer.position()
1329 };
1330 let mut reader = CdrReader::new(&buf[..len]);
1331 assert_eq!(reader.read_i16().unwrap(), val);
1332 }
1333
1334 #[kani::proof]
1335 #[kani::unwind(5)]
1336 fn cdr_roundtrip_i32() {
1337 let mut buf = [0u8; 16];
1338 let val: i32 = kani::any();
1339 let len = {
1340 let mut writer = CdrWriter::new(&mut buf);
1341 writer.write_i32(val).unwrap();
1342 writer.position()
1343 };
1344 let mut reader = CdrReader::new(&buf[..len]);
1345 assert_eq!(reader.read_i32().unwrap(), val);
1346 }
1347
1348 #[kani::proof]
1349 #[kani::unwind(5)]
1350 fn cdr_roundtrip_i64() {
1351 let mut buf = [0u8; 16];
1352 let val: i64 = kani::any();
1353 let len = {
1354 let mut writer = CdrWriter::new(&mut buf);
1355 writer.write_i64(val).unwrap();
1356 writer.position()
1357 };
1358 let mut reader = CdrReader::new(&buf[..len]);
1359 assert_eq!(reader.read_i64().unwrap(), val);
1360 }
1361
1362 #[kani::proof]
1363 #[kani::unwind(5)]
1364 fn cdr_roundtrip_f32() {
1365 let mut buf = [0u8; 16];
1366 let val: f32 = kani::any();
1367 let len = {
1368 let mut writer = CdrWriter::new(&mut buf);
1369 writer.write_f32(val).unwrap();
1370 writer.position()
1371 };
1372 let mut reader = CdrReader::new(&buf[..len]);
1373 let result = reader.read_f32().unwrap();
1374 assert_eq!(val.to_bits(), result.to_bits());
1375 }
1376
1377 #[kani::proof]
1378 #[kani::unwind(5)]
1379 fn cdr_roundtrip_f64() {
1380 let mut buf = [0u8; 16];
1381 let val: f64 = kani::any();
1382 let len = {
1383 let mut writer = CdrWriter::new(&mut buf);
1384 writer.write_f64(val).unwrap();
1385 writer.position()
1386 };
1387 let mut reader = CdrReader::new(&buf[..len]);
1388 let result = reader.read_f64().unwrap();
1389 assert_eq!(val.to_bits(), result.to_bits());
1390 }
1391
1392 #[kani::proof]
1395 #[kani::unwind(5)]
1396 fn cdr_roundtrip_with_header_i32() {
1397 let mut buf = [0u8; 16];
1398 let val: i32 = kani::any();
1399 let len = {
1400 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1401 writer.write_i32(val).unwrap();
1402 writer.position()
1403 };
1404 let mut reader = CdrReader::new_with_header(&buf[..len]).unwrap();
1405 assert_eq!(reader.read_i32().unwrap(), val);
1406 }
1407
1408 #[kani::proof]
1411 #[kani::unwind(5)]
1412 fn cdr_write_buffer_exhaustion_u32() {
1413 let mut buf = [0u8; 3]; let mut writer = CdrWriter::new(&mut buf);
1415 let val: u32 = kani::any();
1416 let result = writer.write_u32(val);
1417 assert!(result.is_err());
1418 }
1419
1420 #[kani::proof]
1421 #[kani::unwind(5)]
1422 fn cdr_write_header_buffer_too_small() {
1423 let mut buf = [0u8; 3]; let result = CdrWriter::new_with_header(&mut buf);
1425 assert!(result.is_err());
1426 }
1427
1428 #[kani::proof]
1431 #[kani::unwind(5)]
1432 fn cdr_deserialize_arbitrary_bytes_i32() {
1433 let mut buf = [0u8; 8];
1434 buf[0] = kani::any();
1435 buf[1] = kani::any();
1436 buf[2] = kani::any();
1437 buf[3] = kani::any();
1438 buf[4] = kani::any();
1439 buf[5] = kani::any();
1440 buf[6] = kani::any();
1441 buf[7] = kani::any();
1442 let result = CdrReader::new_with_header(&buf);
1443 if let Ok(mut reader) = result {
1444 let _ = reader.read_i32(); }
1446 }
1447
1448 #[kani::proof]
1449 #[kani::unwind(5)]
1450 fn cdr_deserialize_empty_buffer() {
1451 let buf = [0u8; 0];
1452 let mut reader = CdrReader::new(&buf);
1453 assert!(reader.read_u8().is_err());
1454 assert!(reader.read_u32().is_err());
1455 }
1456
1457 #[kani::proof]
1460 fn cdr_alignment_no_overflow() {
1461 let offset: usize = kani::any();
1462 let alignment: usize = kani::any();
1463 kani::assume(alignment > 0 && alignment <= 8);
1464 kani::assume(offset <= 1024); let padding = (alignment - (offset % alignment)) % alignment;
1466 let aligned = offset + padding;
1467 assert!(aligned % alignment == 0);
1468 assert!(aligned >= offset);
1469 assert!(aligned < offset + alignment);
1470 }
1471
1472 #[kani::proof]
1475 #[kani::unwind(5)]
1476 fn cdr_writer_position_monotonic() {
1477 let mut buf = [0u8; 32];
1478 let mut writer = CdrWriter::new(&mut buf);
1479 let pos0 = writer.position();
1480
1481 let val: u8 = kani::any();
1482 if writer.write_u8(val).is_ok() {
1483 assert!(writer.position() > pos0);
1484 }
1485 }
1486
1487 #[kani::proof]
1488 #[kani::unwind(5)]
1489 fn cdr_writer_remaining_consistent() {
1490 const BUF_LEN: usize = 32;
1491 let mut buf = [0u8; BUF_LEN];
1492 let mut writer = CdrWriter::new(&mut buf);
1493 assert_eq!(writer.position() + writer.remaining(), BUF_LEN);
1494
1495 let val: u32 = kani::any();
1496 let _ = writer.write_u32(val);
1497 assert_eq!(writer.position() + writer.remaining(), BUF_LEN);
1498 }
1499}