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> {
781 let len = self.read_u32()? as usize;
782 let byte_len = len.checked_mul(T::SIZE).ok_or(DeserError::InvalidData)?;
783 if len > 0 {
784 self.align(T::SIZE)?;
785 }
786 let bytes = self.read_bytes(byte_len)?;
787 Ok(LeSliceView::new(bytes))
788 }
789}
790
791pub trait LeDecode: Sized + Copy {
796 const SIZE: usize;
798 fn from_le(bytes: &[u8]) -> Self;
800}
801
802macro_rules! impl_le_decode {
803 ($($t:ty),+ $(,)?) => {$(
804 impl LeDecode for $t {
805 const SIZE: usize = core::mem::size_of::<$t>();
806 #[inline]
807 fn from_le(bytes: &[u8]) -> Self {
808 let mut buf = [0u8; core::mem::size_of::<$t>()];
809 buf.copy_from_slice(bytes);
810 <$t>::from_le_bytes(buf)
811 }
812 }
813 )+};
814}
815impl_le_decode!(u16, i16, u32, i32, u64, i64, f32, f64);
816
817#[derive(Clone, Copy)]
823pub struct LeSliceView<'a, T> {
824 bytes: &'a [u8],
825 _marker: core::marker::PhantomData<fn() -> T>,
826}
827
828impl<'a, T: LeDecode> LeSliceView<'a, T> {
829 #[inline]
832 pub fn new(bytes: &'a [u8]) -> Self {
833 Self {
834 bytes,
835 _marker: core::marker::PhantomData,
836 }
837 }
838
839 #[inline]
841 pub fn len(&self) -> usize {
842 self.bytes.len() / T::SIZE
843 }
844
845 #[inline]
847 pub fn is_empty(&self) -> bool {
848 self.bytes.is_empty()
849 }
850
851 #[inline]
853 pub fn as_bytes(&self) -> &'a [u8] {
854 self.bytes
855 }
856
857 #[inline]
859 pub fn get(&self, index: usize) -> Option<T> {
860 let start = index.checked_mul(T::SIZE)?;
861 let end = start.checked_add(T::SIZE)?;
862 self.bytes.get(start..end).map(T::from_le)
863 }
864
865 #[inline]
867 pub fn iter(&self) -> impl Iterator<Item = T> + 'a {
868 let bytes = self.bytes;
869 (0..bytes.len() / T::SIZE).map(move |i| T::from_le(&bytes[i * T::SIZE..(i + 1) * T::SIZE]))
870 }
871}
872
873#[cfg(test)]
874mod tests {
875 use super::*;
876
877 fn serialize_header(w: &mut CdrWriter) {
887 let h = w.begin_dheader().unwrap();
888 let t = w.begin_dheader().unwrap();
890 w.write_i32(7).unwrap();
891 w.write_u32(9).unwrap();
892 w.end_dheader(t).unwrap();
893 w.write_string("ab").unwrap();
894 w.end_dheader(h).unwrap();
895 }
896
897 #[test]
912 fn xcdr1_header_matches_live_jazzy_wire_bytes() {
913 let mut buf = [0u8; 64];
914 let n = {
915 let mut w = CdrWriter::new_with_header(&mut buf).unwrap();
916 serialize_header(&mut w);
917 w.position()
918 };
919 let jazzy: [u8; 19] = [
922 0x00, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00,
923 0x00, 0x00, 0x61, 0x62, 0x00,
924 ];
925 assert_eq!(
926 &buf[..n],
927 &jazzy,
928 "nano-ros XCDR1 Header must equal live Jazzy wire bytes"
929 );
930 }
931
932 #[test]
936 fn xcdr2_header_has_dheaders_and_roundtrips() {
937 let mut buf = [0u8; 64];
938 let n = {
939 let mut w = CdrWriter::new_with_header_xcdr2(&mut buf).unwrap();
940 serialize_header(&mut w);
941 w.position()
942 };
943 assert_eq!(&buf[0..2], &[0x00, 0x09], "XCDR2 DELIMITED encapsulation");
944 let mut r = CdrReader::new_with_header(&buf[..n]).unwrap();
946 assert_eq!(r.version(), EncodingVersion::Xcdr2);
947 let h = r.begin_dheader().unwrap();
948 let t = r.begin_dheader().unwrap();
949 assert_eq!(r.read_i32().unwrap(), 7);
950 assert_eq!(r.read_u32().unwrap(), 9);
951 r.end_dheader(t).unwrap();
952 assert_eq!(r.read_string().unwrap(), "ab");
953 r.end_dheader(h).unwrap();
954 }
955
956 #[test]
957 fn xcdr2_nested_dheader_layout_and_roundtrip() {
958 let mut buf = [0u8; 64];
959 {
960 let mut w = CdrWriter::new_with_header_xcdr2(&mut buf).unwrap();
961 let top = w.begin_dheader().unwrap();
962 let time = w.begin_dheader().unwrap();
964 w.write_i32(7).unwrap();
965 w.write_u32(9).unwrap();
966 w.end_dheader(time).unwrap();
967 w.write_string("ab").unwrap();
969 w.end_dheader(top).unwrap();
970 }
971 assert_eq!(&buf[0..4], &[0x00, 0x09, 0x00, 0x00]);
973 assert_eq!(u32::from_le_bytes([buf[8], buf[9], buf[10], buf[11]]), 8);
976 assert_eq!(u32::from_le_bytes([buf[12], buf[13], buf[14], buf[15]]), 7);
978 assert_eq!(u32::from_le_bytes([buf[16], buf[17], buf[18], buf[19]]), 9);
979
980 let mut r = CdrReader::new_with_header(&buf).unwrap();
982 assert_eq!(r.version(), EncodingVersion::Xcdr2);
983 let top = r.begin_dheader().unwrap();
984 let time = r.begin_dheader().unwrap();
985 assert_eq!(r.read_i32().unwrap(), 7);
986 assert_eq!(r.read_u32().unwrap(), 9);
987 r.end_dheader(time).unwrap();
988 assert_eq!(r.read_string().unwrap(), "ab");
989 r.end_dheader(top).unwrap();
990 }
991
992 #[test]
996 fn xcdr1_dheader_calls_are_byte_identical_noops() {
997 let mut a = [0u8; 32];
998 let mut b = [0u8; 32];
999 let na = {
1000 let mut w = CdrWriter::new_with_header(&mut a).unwrap();
1001 let d = w.begin_dheader().unwrap();
1002 w.write_i32(-2).unwrap();
1003 w.write_u32(3).unwrap();
1004 w.end_dheader(d).unwrap();
1005 w.position()
1006 };
1007 let nb = {
1008 let mut w = CdrWriter::new_with_header(&mut b).unwrap();
1009 w.write_i32(-2).unwrap();
1010 w.write_u32(3).unwrap();
1011 w.position()
1012 };
1013 assert_eq!(na, nb);
1014 assert_eq!(a[..na], b[..nb]);
1015 }
1016
1017 #[test]
1020 fn xcdr2_reader_skips_unknown_trailing_members() {
1021 let mut buf = [0u8; 32];
1022 {
1023 let mut w = CdrWriter::new_with_header_xcdr2(&mut buf).unwrap();
1024 let d = w.begin_dheader().unwrap();
1025 w.write_i32(11).unwrap();
1026 w.write_u32(22).unwrap();
1028 w.end_dheader(d).unwrap();
1029 }
1030 let mut r = CdrReader::new_with_header(&buf).unwrap();
1031 let d = r.begin_dheader().unwrap();
1032 assert_eq!(r.read_i32().unwrap(), 11);
1033 assert_eq!(r.position(), 12);
1036 r.end_dheader(d).unwrap();
1037 assert_eq!(
1038 r.position(),
1039 16,
1040 "end_dheader must skip the unknown trailing member"
1041 );
1042 }
1043
1044 #[test]
1045 fn test_write_read_u8() {
1046 let mut buf = [0u8; 16];
1047 let mut writer = CdrWriter::new(&mut buf);
1048 writer.write_u8(0x42).unwrap();
1049 writer.write_u8(0xFF).unwrap();
1050
1051 let mut reader = CdrReader::new(&buf);
1052 assert_eq!(reader.read_u8().unwrap(), 0x42);
1053 assert_eq!(reader.read_u8().unwrap(), 0xFF);
1054 }
1055
1056 #[test]
1057 fn le_slice_view_decodes_unaligned_f32() {
1058 let vals = [1.5f32, -2.25, 3.0e10, 0.0];
1061 let mut backing = [0u8; 1 + 4 * 4];
1062 backing[0] = 0xAA; for (i, v) in vals.iter().enumerate() {
1064 backing[1 + i * 4..1 + i * 4 + 4].copy_from_slice(&v.to_le_bytes());
1065 }
1066 let view: LeSliceView<f32> = LeSliceView::new(&backing[1..]);
1067 assert_eq!(view.len(), 4);
1068 assert!(!view.is_empty());
1069 for (i, v) in vals.iter().enumerate() {
1070 assert_eq!(view.get(i).unwrap(), *v);
1071 }
1072 assert_eq!(view.get(4), None);
1073 let collected: heapless::Vec<f32, 4> = view.iter().collect();
1074 assert_eq!(&collected[..], &vals[..]);
1075 }
1076
1077 #[test]
1078 fn read_le_slice_roundtrips_through_cdr() {
1079 let vals = [10u16, 4000, 65535, 1];
1082 let mut buf = [0u8; 64];
1083 let written = {
1084 let mut w = CdrWriter::new_with_header(&mut buf).unwrap();
1085 w.write_sequence_len(vals.len()).unwrap();
1086 for v in &vals {
1087 w.write_u16(*v).unwrap();
1088 }
1089 w.position()
1090 };
1091 let mut reader = CdrReader::new_with_header(&buf[..written]).unwrap();
1092 let view = reader.read_le_slice::<u16>().unwrap();
1093 assert_eq!(view.len(), vals.len());
1094 for (i, v) in vals.iter().enumerate() {
1095 assert_eq!(view.get(i).unwrap(), *v);
1096 }
1097 }
1098
1099 #[test]
1100 fn read_le_slice_consumes_stream_padding_before_8_byte_elements() {
1101 let vals = [1111.0f64, 2222.0];
1107 let mut buf = [0u8; 64];
1108 let written = {
1109 let mut w = CdrWriter::new_with_header(&mut buf).unwrap();
1110 w.write_u32(7).unwrap();
1111 w.write_u32(9).unwrap();
1112 w.write_sequence_len(vals.len()).unwrap();
1113 for v in &vals {
1114 w.write_f64(*v).unwrap();
1115 }
1116 w.write_u32(0xCAFE).unwrap();
1117 w.position()
1118 };
1119 let mut r = CdrReader::new_with_header(&buf[..written]).unwrap();
1120 assert_eq!(r.read_u32().unwrap(), 7);
1121 assert_eq!(r.read_u32().unwrap(), 9);
1122 let view = r.read_le_slice::<f64>().unwrap();
1123 assert_eq!(view.len(), 2);
1124 assert_eq!(view.get(0), Some(1111.0));
1125 assert_eq!(view.get(1), Some(2222.0));
1126 assert_eq!(r.read_u32().unwrap(), 0xCAFE);
1127 assert!(r.is_empty());
1128 }
1129
1130 #[test]
1131 fn read_le_slice_of_zero_elements_takes_no_padding() {
1132 let mut buf = [0u8; 32];
1136 let written = {
1137 let mut w = CdrWriter::new_with_header(&mut buf).unwrap();
1138 w.write_sequence_len(0).unwrap();
1139 w.write_u8(0xAB).unwrap();
1140 w.position()
1141 };
1142 let mut r = CdrReader::new_with_header(&buf[..written]).unwrap();
1143 let view = r.read_le_slice::<f64>().unwrap();
1144 assert!(view.is_empty());
1145 assert_eq!(r.read_u8().unwrap(), 0xAB);
1146 assert!(r.is_empty());
1147 }
1148
1149 #[test]
1150 fn test_write_read_u32_alignment() {
1151 let mut buf = [0u8; 16];
1152 let mut writer = CdrWriter::new(&mut buf);
1153 writer.write_u8(0x01).unwrap(); writer.write_u32(0x12345678).unwrap(); assert_eq!(writer.position(), 8); let mut reader = CdrReader::new(&buf);
1159 assert_eq!(reader.read_u8().unwrap(), 0x01);
1160 assert_eq!(reader.read_u32().unwrap(), 0x12345678);
1161 }
1162
1163 #[test]
1164 fn test_write_read_string() {
1165 let mut buf = [0u8; 32];
1166 let mut writer = CdrWriter::new(&mut buf);
1167 writer.write_string("Hello").unwrap();
1168
1169 let mut reader = CdrReader::new(&buf);
1170 assert_eq!(reader.read_string().unwrap(), "Hello");
1171 }
1172
1173 #[test]
1174 fn test_encapsulation_header() {
1175 let mut buf = [0u8; 32];
1176 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1177 writer.write_u32(42).unwrap();
1178
1179 assert_eq!(&buf[0..4], &CDR_LE_HEADER);
1180
1181 let mut reader = CdrReader::new_with_header(&buf).unwrap();
1182 assert_eq!(reader.read_u32().unwrap(), 42);
1183 }
1184
1185 #[test]
1186 fn test_alignment_with_header() {
1187 let mut buf = [0u8; 32];
1188 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1189 writer.write_u8(0x01).unwrap(); writer.write_u32(0xDEADBEEF).unwrap(); assert_eq!(writer.position(), 12); let mut reader = CdrReader::new_with_header(&buf).unwrap();
1196 assert_eq!(reader.read_u8().unwrap(), 0x01);
1197 assert_eq!(reader.read_u32().unwrap(), 0xDEADBEEF);
1198 }
1199}
1200
1201#[cfg(test)]
1206mod ghost_checks {
1207 use super::*;
1208 use nros_ghost_types::CdrGhost;
1209
1210 fn ghost_from_writer(w: &CdrWriter) -> CdrGhost {
1213 CdrGhost {
1214 buf_len: w.buf.len(),
1215 pos: w.pos,
1216 origin: w.origin,
1217 }
1218 }
1219
1220 #[test]
1221 fn ghost_new_state() {
1222 let mut buf = [0u8; 64];
1223 let writer = CdrWriter::new(&mut buf);
1224 let ghost = ghost_from_writer(&writer);
1225 assert_eq!(ghost.pos, 0);
1226 assert_eq!(ghost.origin, 0);
1227 assert_eq!(ghost.buf_len, 64);
1228 }
1229
1230 #[test]
1231 fn ghost_header_origin() {
1232 let mut buf = [0u8; 64];
1233 let writer = CdrWriter::new_with_header(&mut buf).unwrap();
1234 let ghost = ghost_from_writer(&writer);
1235 assert_eq!(ghost.pos, 4);
1236 assert_eq!(ghost.origin, 4);
1237 }
1238
1239 #[test]
1240 fn ghost_position_invariant() {
1241 let mut buf = [0u8; 64];
1242 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1243 writer.write_u32(42).unwrap();
1244 let ghost = ghost_from_writer(&writer);
1245 assert_eq!(ghost.pos + writer.remaining(), ghost.buf_len);
1247 }
1248
1249 #[test]
1250 fn test_read_slice_u8() {
1251 let mut buf = [0u8; 64];
1252 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1253 writer.write_u32(3).unwrap(); writer.write_u8(0x10).unwrap();
1256 writer.write_u8(0x20).unwrap();
1257 writer.write_u8(0x30).unwrap();
1258 let len = writer.position();
1259
1260 let mut reader = CdrReader::new_with_header(&buf[..len]).unwrap();
1261 let slice = reader.read_slice_u8().unwrap();
1262 assert_eq!(slice, &[0x10, 0x20, 0x30]);
1263 }
1264
1265 #[test]
1266 fn test_read_slice_u8_empty() {
1267 let mut buf = [0u8; 64];
1268 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1269 writer.write_u32(0).unwrap(); let len = writer.position();
1271
1272 let mut reader = CdrReader::new_with_header(&buf[..len]).unwrap();
1273 let slice = reader.read_slice_u8().unwrap();
1274 assert!(slice.is_empty());
1275 }
1276}
1277
1278#[cfg(kani)]
1283mod verification {
1284 use super::*;
1285
1286 #[kani::proof]
1289 #[kani::unwind(5)]
1290 fn cdr_write_u8_no_panic() {
1291 let mut buf = [0u8; 8];
1292 let mut writer = CdrWriter::new(&mut buf);
1293 let val: u8 = kani::any();
1294 let _ = writer.write_u8(val);
1295 }
1296
1297 #[kani::proof]
1298 #[kani::unwind(5)]
1299 fn cdr_write_bool_no_panic() {
1300 let mut buf = [0u8; 8];
1301 let mut writer = CdrWriter::new(&mut buf);
1302 let val: bool = kani::any();
1303 let _ = writer.write_bool(val);
1304 }
1305
1306 #[kani::proof]
1307 #[kani::unwind(5)]
1308 fn cdr_write_i16_no_panic() {
1309 let mut buf = [0u8; 16];
1310 let mut writer = CdrWriter::new(&mut buf);
1311 let val: i16 = kani::any();
1312 let _ = writer.write_i16(val);
1313 }
1314
1315 #[kani::proof]
1316 #[kani::unwind(5)]
1317 fn cdr_write_i32_no_panic() {
1318 let mut buf = [0u8; 16];
1319 let mut writer = CdrWriter::new(&mut buf);
1320 let val: i32 = kani::any();
1321 let _ = writer.write_i32(val);
1322 }
1323
1324 #[kani::proof]
1325 #[kani::unwind(5)]
1326 fn cdr_write_i64_no_panic() {
1327 let mut buf = [0u8; 16];
1328 let mut writer = CdrWriter::new(&mut buf);
1329 let val: i64 = kani::any();
1330 let _ = writer.write_i64(val);
1331 }
1332
1333 #[kani::proof]
1334 #[kani::unwind(5)]
1335 fn cdr_write_f32_no_panic() {
1336 let mut buf = [0u8; 16];
1337 let mut writer = CdrWriter::new(&mut buf);
1338 let val: f32 = kani::any();
1339 let _ = writer.write_f32(val);
1340 }
1341
1342 #[kani::proof]
1343 #[kani::unwind(5)]
1344 fn cdr_write_f64_no_panic() {
1345 let mut buf = [0u8; 16];
1346 let mut writer = CdrWriter::new(&mut buf);
1347 let val: f64 = kani::any();
1348 let _ = writer.write_f64(val);
1349 }
1350
1351 #[kani::proof]
1354 #[kani::unwind(5)]
1355 fn cdr_roundtrip_u8() {
1356 let mut buf = [0u8; 8];
1357 let val: u8 = kani::any();
1358 let len = {
1359 let mut writer = CdrWriter::new(&mut buf);
1360 writer.write_u8(val).unwrap();
1361 writer.position()
1362 };
1363 let mut reader = CdrReader::new(&buf[..len]);
1364 assert_eq!(reader.read_u8().unwrap(), val);
1365 }
1366
1367 #[kani::proof]
1368 #[kani::unwind(5)]
1369 fn cdr_roundtrip_bool() {
1370 let mut buf = [0u8; 8];
1371 let val: bool = kani::any();
1372 let len = {
1373 let mut writer = CdrWriter::new(&mut buf);
1374 writer.write_bool(val).unwrap();
1375 writer.position()
1376 };
1377 let mut reader = CdrReader::new(&buf[..len]);
1378 assert_eq!(reader.read_bool().unwrap(), val);
1379 }
1380
1381 #[kani::proof]
1382 #[kani::unwind(5)]
1383 fn cdr_roundtrip_i16() {
1384 let mut buf = [0u8; 16];
1385 let val: i16 = kani::any();
1386 let len = {
1387 let mut writer = CdrWriter::new(&mut buf);
1388 writer.write_i16(val).unwrap();
1389 writer.position()
1390 };
1391 let mut reader = CdrReader::new(&buf[..len]);
1392 assert_eq!(reader.read_i16().unwrap(), val);
1393 }
1394
1395 #[kani::proof]
1396 #[kani::unwind(5)]
1397 fn cdr_roundtrip_i32() {
1398 let mut buf = [0u8; 16];
1399 let val: i32 = kani::any();
1400 let len = {
1401 let mut writer = CdrWriter::new(&mut buf);
1402 writer.write_i32(val).unwrap();
1403 writer.position()
1404 };
1405 let mut reader = CdrReader::new(&buf[..len]);
1406 assert_eq!(reader.read_i32().unwrap(), val);
1407 }
1408
1409 #[kani::proof]
1410 #[kani::unwind(5)]
1411 fn cdr_roundtrip_i64() {
1412 let mut buf = [0u8; 16];
1413 let val: i64 = kani::any();
1414 let len = {
1415 let mut writer = CdrWriter::new(&mut buf);
1416 writer.write_i64(val).unwrap();
1417 writer.position()
1418 };
1419 let mut reader = CdrReader::new(&buf[..len]);
1420 assert_eq!(reader.read_i64().unwrap(), val);
1421 }
1422
1423 #[kani::proof]
1424 #[kani::unwind(5)]
1425 fn cdr_roundtrip_f32() {
1426 let mut buf = [0u8; 16];
1427 let val: f32 = kani::any();
1428 let len = {
1429 let mut writer = CdrWriter::new(&mut buf);
1430 writer.write_f32(val).unwrap();
1431 writer.position()
1432 };
1433 let mut reader = CdrReader::new(&buf[..len]);
1434 let result = reader.read_f32().unwrap();
1435 assert_eq!(val.to_bits(), result.to_bits());
1436 }
1437
1438 #[kani::proof]
1439 #[kani::unwind(5)]
1440 fn cdr_roundtrip_f64() {
1441 let mut buf = [0u8; 16];
1442 let val: f64 = kani::any();
1443 let len = {
1444 let mut writer = CdrWriter::new(&mut buf);
1445 writer.write_f64(val).unwrap();
1446 writer.position()
1447 };
1448 let mut reader = CdrReader::new(&buf[..len]);
1449 let result = reader.read_f64().unwrap();
1450 assert_eq!(val.to_bits(), result.to_bits());
1451 }
1452
1453 #[kani::proof]
1456 #[kani::unwind(5)]
1457 fn cdr_roundtrip_with_header_i32() {
1458 let mut buf = [0u8; 16];
1459 let val: i32 = kani::any();
1460 let len = {
1461 let mut writer = CdrWriter::new_with_header(&mut buf).unwrap();
1462 writer.write_i32(val).unwrap();
1463 writer.position()
1464 };
1465 let mut reader = CdrReader::new_with_header(&buf[..len]).unwrap();
1466 assert_eq!(reader.read_i32().unwrap(), val);
1467 }
1468
1469 #[kani::proof]
1472 #[kani::unwind(5)]
1473 fn cdr_write_buffer_exhaustion_u32() {
1474 let mut buf = [0u8; 3]; let mut writer = CdrWriter::new(&mut buf);
1476 let val: u32 = kani::any();
1477 let result = writer.write_u32(val);
1478 assert!(result.is_err());
1479 }
1480
1481 #[kani::proof]
1482 #[kani::unwind(5)]
1483 fn cdr_write_header_buffer_too_small() {
1484 let mut buf = [0u8; 3]; let result = CdrWriter::new_with_header(&mut buf);
1486 assert!(result.is_err());
1487 }
1488
1489 #[kani::proof]
1492 #[kani::unwind(5)]
1493 fn cdr_deserialize_arbitrary_bytes_i32() {
1494 let mut buf = [0u8; 8];
1495 buf[0] = kani::any();
1496 buf[1] = kani::any();
1497 buf[2] = kani::any();
1498 buf[3] = kani::any();
1499 buf[4] = kani::any();
1500 buf[5] = kani::any();
1501 buf[6] = kani::any();
1502 buf[7] = kani::any();
1503 let result = CdrReader::new_with_header(&buf);
1504 if let Ok(mut reader) = result {
1505 let _ = reader.read_i32(); }
1507 }
1508
1509 #[kani::proof]
1510 #[kani::unwind(5)]
1511 fn cdr_deserialize_empty_buffer() {
1512 let buf = [0u8; 0];
1513 let mut reader = CdrReader::new(&buf);
1514 assert!(reader.read_u8().is_err());
1515 assert!(reader.read_u32().is_err());
1516 }
1517
1518 #[kani::proof]
1521 fn cdr_alignment_no_overflow() {
1522 let offset: usize = kani::any();
1523 let alignment: usize = kani::any();
1524 kani::assume(alignment > 0 && alignment <= 8);
1525 kani::assume(offset <= 1024); let padding = (alignment - (offset % alignment)) % alignment;
1527 let aligned = offset + padding;
1528 assert!(aligned % alignment == 0);
1529 assert!(aligned >= offset);
1530 assert!(aligned < offset + alignment);
1531 }
1532
1533 #[kani::proof]
1536 #[kani::unwind(5)]
1537 fn cdr_writer_position_monotonic() {
1538 let mut buf = [0u8; 32];
1539 let mut writer = CdrWriter::new(&mut buf);
1540 let pos0 = writer.position();
1541
1542 let val: u8 = kani::any();
1543 if writer.write_u8(val).is_ok() {
1544 assert!(writer.position() > pos0);
1545 }
1546 }
1547
1548 #[kani::proof]
1549 #[kani::unwind(5)]
1550 fn cdr_writer_remaining_consistent() {
1551 const BUF_LEN: usize = 32;
1552 let mut buf = [0u8; BUF_LEN];
1553 let mut writer = CdrWriter::new(&mut buf);
1554 assert_eq!(writer.position() + writer.remaining(), BUF_LEN);
1555
1556 let val: u32 = kani::any();
1557 let _ = writer.write_u32(val);
1558 assert_eq!(writer.position() + writer.remaining(), BUF_LEN);
1559 }
1560}