1#[cfg(feature = "alloc")]
22extern crate alloc;
23#[cfg(feature = "alloc")]
24use alloc::vec::Vec;
25
26use crate::endianness::Endianness;
27use crate::error::DecodeError;
28#[cfg(feature = "alloc")]
29use crate::error::EncodeError;
30
31#[cfg(feature = "alloc")]
39#[derive(Debug, Clone)]
40pub struct BufferWriter {
41 bytes: Vec<u8>,
42 endianness: Endianness,
43}
44
45#[cfg(feature = "alloc")]
46impl BufferWriter {
47 #[must_use]
49 pub fn new(endianness: Endianness) -> Self {
50 Self {
51 bytes: Vec::new(),
52 endianness,
53 }
54 }
55
56 #[must_use]
58 pub fn with_capacity(endianness: Endianness, cap: usize) -> Self {
59 Self {
60 bytes: Vec::with_capacity(cap),
61 endianness,
62 }
63 }
64
65 #[must_use]
67 pub fn endianness(&self) -> Endianness {
68 self.endianness
69 }
70
71 #[must_use]
73 pub fn position(&self) -> usize {
74 self.bytes.len()
75 }
76
77 #[must_use]
79 pub fn into_bytes(self) -> Vec<u8> {
80 self.bytes
81 }
82
83 #[must_use]
85 pub fn as_bytes(&self) -> &[u8] {
86 &self.bytes
87 }
88
89 pub fn align(&mut self, alignment: usize) {
93 debug_assert!(
94 alignment.is_power_of_two(),
95 "alignment must be a power of two"
96 );
97 let pos = self.bytes.len();
98 let pad = padding_for(pos, alignment);
99 for _ in 0..pad {
100 self.bytes.push(0);
101 }
102 }
103
104 pub fn write_bytes(&mut self, data: &[u8]) -> Result<(), EncodeError> {
110 self.bytes.extend_from_slice(data);
111 Ok(())
112 }
113
114 pub fn write_u8(&mut self, value: u8) -> Result<(), EncodeError> {
119 self.bytes.push(value);
120 Ok(())
121 }
122
123 pub fn write_u16(&mut self, value: u16) -> Result<(), EncodeError> {
128 self.align(2);
129 self.write_bytes(&self.endianness.write_u16(value))
130 }
131
132 pub fn write_u32(&mut self, value: u32) -> Result<(), EncodeError> {
137 self.align(4);
138 self.write_bytes(&self.endianness.write_u32(value))
139 }
140
141 pub fn write_u64(&mut self, value: u64) -> Result<(), EncodeError> {
146 self.align(8);
147 self.write_bytes(&self.endianness.write_u64(value))
148 }
149
150 pub fn write_string(&mut self, s: &str) -> Result<(), EncodeError> {
157 let bytes = s.as_bytes();
158 let len = u32::try_from(bytes.len().saturating_add(1)).map_err(|_| {
160 EncodeError::ValueOutOfRange {
161 message: "CDR string length exceeds u32::MAX",
162 }
163 })?;
164 self.write_u32(len)?;
165 self.write_bytes(bytes)?;
166 self.write_u8(0)
167 }
168}
169
170#[derive(Debug, Clone)]
176pub struct BufferReader<'a> {
177 bytes: &'a [u8],
178 pos: usize,
179 endianness: Endianness,
180}
181
182impl<'a> BufferReader<'a> {
183 #[must_use]
185 pub fn new(bytes: &'a [u8], endianness: Endianness) -> Self {
186 Self {
187 bytes,
188 pos: 0,
189 endianness,
190 }
191 }
192
193 #[must_use]
195 pub fn endianness(&self) -> Endianness {
196 self.endianness
197 }
198
199 #[must_use]
201 pub fn position(&self) -> usize {
202 self.pos
203 }
204
205 #[must_use]
207 pub fn remaining(&self) -> usize {
208 self.bytes.len().saturating_sub(self.pos)
209 }
210
211 pub fn align(&mut self, alignment: usize) -> Result<(), DecodeError> {
216 debug_assert!(
217 alignment.is_power_of_two(),
218 "alignment must be power of two"
219 );
220 let pad = padding_for(self.pos, alignment);
221 if self.remaining() < pad {
222 return Err(DecodeError::UnexpectedEof {
223 needed: pad,
224 offset: self.pos,
225 });
226 }
227 self.pos += pad;
228 Ok(())
229 }
230
231 pub fn read_bytes(&mut self, n: usize) -> Result<&'a [u8], DecodeError> {
236 if self.remaining() < n {
237 return Err(DecodeError::UnexpectedEof {
238 needed: n,
239 offset: self.pos,
240 });
241 }
242 let slice = &self.bytes[self.pos..self.pos + n];
243 self.pos += n;
244 Ok(slice)
245 }
246
247 pub fn read_u8(&mut self) -> Result<u8, DecodeError> {
252 let slice = self.read_bytes(1)?;
253 Ok(slice[0])
254 }
255
256 pub fn read_u16(&mut self) -> Result<u16, DecodeError> {
261 self.align(2)?;
262 let slice = self.read_bytes(2)?;
263 let mut buf = [0u8; 2];
264 buf.copy_from_slice(slice);
265 Ok(self.endianness.read_u16(buf))
266 }
267
268 pub fn read_u32(&mut self) -> Result<u32, DecodeError> {
273 self.align(4)?;
274 let slice = self.read_bytes(4)?;
275 let mut buf = [0u8; 4];
276 buf.copy_from_slice(slice);
277 Ok(self.endianness.read_u32(buf))
278 }
279
280 pub fn read_u64(&mut self) -> Result<u64, DecodeError> {
285 self.align(8)?;
286 let slice = self.read_bytes(8)?;
287 let mut buf = [0u8; 8];
288 buf.copy_from_slice(slice);
289 Ok(self.endianness.read_u64(buf))
290 }
291
292 #[cfg(feature = "alloc")]
300 pub fn read_string(&mut self) -> Result<alloc::string::String, DecodeError> {
301 use alloc::string::String;
302 let start = self.pos;
303 let len = self.read_u32()? as usize;
304 if len == 0 {
305 return Err(DecodeError::InvalidString {
306 offset: start,
307 reason: "length must be > 0 (null terminator required)",
308 });
309 }
310 let raw = self.read_bytes(len)?;
311 if raw[len - 1] != 0 {
313 return Err(DecodeError::InvalidString {
314 offset: start,
315 reason: "missing null terminator",
316 });
317 }
318 String::from_utf8(raw[..len - 1].to_vec())
319 .map_err(|_| DecodeError::InvalidUtf8 { offset: start + 4 })
320 }
321}
322
323#[must_use]
330pub fn padding_for(pos: usize, alignment: usize) -> usize {
331 let mask = alignment - 1;
332 (alignment - (pos & mask)) & mask
333}
334
335#[cfg(test)]
336mod tests {
337 #![allow(clippy::expect_used, clippy::panic, clippy::unwrap_used)]
338 use super::*;
339
340 #[cfg(feature = "alloc")]
341 extern crate alloc;
342 #[cfg(feature = "alloc")]
343 use alloc::vec;
344
345 #[test]
346 fn padding_for_zero_position_is_zero() {
347 assert_eq!(padding_for(0, 4), 0);
348 assert_eq!(padding_for(0, 8), 0);
349 }
350
351 #[test]
352 fn padding_for_already_aligned_is_zero() {
353 assert_eq!(padding_for(8, 4), 0);
354 assert_eq!(padding_for(16, 8), 0);
355 }
356
357 #[test]
358 fn padding_for_one_byte_to_4_align_is_three() {
359 assert_eq!(padding_for(1, 4), 3);
360 }
361
362 #[test]
363 fn padding_for_three_bytes_to_8_align_is_five() {
364 assert_eq!(padding_for(3, 8), 5);
365 }
366
367 #[cfg(feature = "alloc")]
368 #[test]
369 fn writer_writes_u8_without_padding() {
370 let mut w = BufferWriter::new(Endianness::Little);
371 w.write_u8(0xAB).unwrap();
372 assert_eq!(w.as_bytes(), &[0xAB]);
373 assert_eq!(w.position(), 1);
374 }
375
376 #[cfg(feature = "alloc")]
377 #[test]
378 fn writer_aligns_u32_after_u8() {
379 let mut w = BufferWriter::new(Endianness::Little);
380 w.write_u8(0xAB).unwrap();
381 w.write_u32(0xDEAD_BEEF).unwrap();
382 assert_eq!(w.as_bytes(), &[0xAB, 0, 0, 0, 0xEF, 0xBE, 0xAD, 0xDE]);
384 }
385
386 #[cfg(feature = "alloc")]
387 #[test]
388 fn writer_aligns_u64_after_u8() {
389 let mut w = BufferWriter::new(Endianness::Big);
390 w.write_u8(0x01).unwrap();
391 w.write_u64(0x0203_0405_0607_0809).unwrap();
392 assert_eq!(
394 w.as_bytes(),
395 &[0x01, 0, 0, 0, 0, 0, 0, 0, 2, 3, 4, 5, 6, 7, 8, 9]
396 );
397 }
398
399 #[cfg(feature = "alloc")]
400 #[test]
401 fn writer_with_capacity_preserves_endianness() {
402 let w = BufferWriter::with_capacity(Endianness::Big, 64);
403 assert_eq!(w.endianness(), Endianness::Big);
404 assert_eq!(w.position(), 0);
405 }
406
407 #[cfg(feature = "alloc")]
408 #[test]
409 fn writer_into_bytes_returns_full_buffer() {
410 let mut w = BufferWriter::new(Endianness::Little);
411 w.write_u32(0xCAFE_BABE).unwrap();
412 let bytes = w.into_bytes();
413 assert_eq!(bytes, vec![0xBE, 0xBA, 0xFE, 0xCA]);
414 }
415
416 #[test]
417 fn reader_reads_u8() {
418 let bytes = [0xAB, 0xCD];
419 let mut r = BufferReader::new(&bytes, Endianness::Little);
420 assert_eq!(r.read_u8().unwrap(), 0xAB);
421 assert_eq!(r.position(), 1);
422 }
423
424 #[test]
425 fn reader_aligns_before_u32() {
426 let bytes = [0xAB, 0, 0, 0, 0xEF, 0xBE, 0xAD, 0xDE];
427 let mut r = BufferReader::new(&bytes, Endianness::Little);
428 assert_eq!(r.read_u8().unwrap(), 0xAB);
429 assert_eq!(r.read_u32().unwrap(), 0xDEAD_BEEF);
430 assert_eq!(r.remaining(), 0);
431 }
432
433 #[test]
434 fn reader_aligns_before_u64_be() {
435 let bytes = [0x01, 0, 0, 0, 0, 0, 0, 0, 2, 3, 4, 5, 6, 7, 8, 9];
436 let mut r = BufferReader::new(&bytes, Endianness::Big);
437 assert_eq!(r.read_u8().unwrap(), 0x01);
438 assert_eq!(r.read_u64().unwrap(), 0x0203_0405_0607_0809);
439 }
440
441 #[test]
442 fn reader_unexpected_eof_on_short_read() {
443 let bytes = [0u8; 2];
444 let mut r = BufferReader::new(&bytes, Endianness::Little);
445 let res = r.read_u32();
446 match res {
448 Err(DecodeError::UnexpectedEof {
449 needed: 4,
450 offset: 0,
451 }) => {}
452 other => panic!("expected UnexpectedEof, got {other:?}"),
453 }
454 }
455
456 #[test]
457 fn reader_eof_on_align_overflow() {
458 let bytes = [0u8; 1];
459 let mut r = BufferReader::new(&bytes, Endianness::Little);
460 let _ = r.read_u8().unwrap();
461 let res = r.align(8);
462 assert!(matches!(res, Err(DecodeError::UnexpectedEof { .. })));
463 }
464
465 #[cfg(feature = "alloc")]
466 #[test]
467 fn writer_reader_roundtrip_mixed_primitives() {
468 let mut w = BufferWriter::new(Endianness::Little);
469 w.write_u8(1).unwrap();
470 w.write_u16(0x1234).unwrap();
471 w.write_u32(0x5678_9ABC).unwrap();
472 w.write_u64(0x0102_0304_0506_0708).unwrap();
473 let bytes = w.into_bytes();
474 let mut r = BufferReader::new(&bytes, Endianness::Little);
475 assert_eq!(r.read_u8().unwrap(), 1);
476 assert_eq!(r.read_u16().unwrap(), 0x1234);
477 assert_eq!(r.read_u32().unwrap(), 0x5678_9ABC);
478 assert_eq!(r.read_u64().unwrap(), 0x0102_0304_0506_0708);
479 assert_eq!(r.remaining(), 0);
480 }
481
482 #[cfg(feature = "alloc")]
483 #[test]
484 fn write_read_string_roundtrip_ascii() {
485 let mut w = BufferWriter::new(Endianness::Little);
486 w.write_string("ChatterTopic").unwrap();
487 let bytes = w.into_bytes();
488 assert_eq!(&bytes[0..4], &[13, 0, 0, 0]);
490 assert_eq!(&bytes[4..16], b"ChatterTopic");
491 assert_eq!(bytes[16], 0);
492 let mut r = BufferReader::new(&bytes, Endianness::Little);
493 assert_eq!(r.read_string().unwrap(), "ChatterTopic");
494 }
495
496 #[cfg(feature = "alloc")]
497 #[test]
498 fn write_read_string_empty() {
499 let mut w = BufferWriter::new(Endianness::Little);
501 w.write_string("").unwrap();
502 let bytes = w.into_bytes();
503 assert_eq!(&bytes[..], &[1, 0, 0, 0, 0]);
504 let mut r = BufferReader::new(&bytes, Endianness::Little);
505 assert_eq!(r.read_string().unwrap(), "");
506 }
507
508 #[cfg(feature = "alloc")]
509 #[test]
510 fn write_read_string_utf8_multibyte() {
511 let mut w = BufferWriter::new(Endianness::Little);
512 w.write_string("Zähler").unwrap(); let bytes = w.into_bytes();
514 let mut r = BufferReader::new(&bytes, Endianness::Little);
515 assert_eq!(r.read_string().unwrap(), "Zähler");
516 }
517
518 #[cfg(feature = "alloc")]
519 #[test]
520 fn read_string_rejects_length_zero() {
521 let bytes = [0u8, 0, 0, 0];
522 let mut r = BufferReader::new(&bytes, Endianness::Little);
523 assert!(matches!(
524 r.read_string(),
525 Err(DecodeError::InvalidString { .. })
526 ));
527 }
528
529 #[cfg(feature = "alloc")]
530 #[test]
531 fn read_string_rejects_missing_null_terminator() {
532 let bytes = [4u8, 0, 0, 0, b'A', b'B', b'C', b'D'];
534 let mut r = BufferReader::new(&bytes, Endianness::Little);
535 assert!(matches!(
536 r.read_string(),
537 Err(DecodeError::InvalidString { .. })
538 ));
539 }
540
541 #[cfg(feature = "alloc")]
542 #[test]
543 fn read_string_rejects_invalid_utf8() {
544 let bytes = [3u8, 0, 0, 0, 0xFF, 0xFE, 0];
546 let mut r = BufferReader::new(&bytes, Endianness::Little);
547 assert!(matches!(
548 r.read_string(),
549 Err(DecodeError::InvalidUtf8 { .. })
550 ));
551 }
552
553 #[test]
559 fn reader_endianness_getter_returns_construction_value() {
560 let r_be = BufferReader::new(&[0u8; 4], Endianness::Big);
561 assert_eq!(r_be.endianness(), Endianness::Big);
562 let r_le = BufferReader::new(&[0u8; 4], Endianness::Little);
563 assert_eq!(r_le.endianness(), Endianness::Little);
564 }
565
566 #[test]
569 fn reader_align_succeeds_when_remaining_equals_pad() {
570 let bytes = [0xAA, 0, 0, 0];
576 let mut r = BufferReader::new(&bytes, Endianness::Little);
577 r.read_u8().unwrap();
578 assert!(r.align(4).is_ok());
579 assert_eq!(r.position(), 4);
580 }
581
582 #[test]
585 fn reader_align_advances_position_strictly() {
586 let bytes = [0xAA, 0, 0, 0, 1, 2, 3, 4];
587 let mut r = BufferReader::new(&bytes, Endianness::Little);
588 r.read_u8().unwrap();
589 assert_eq!(r.position(), 1);
590 r.align(4).unwrap();
591 assert_eq!(r.position(), 4);
593 }
594
595 #[test]
598 fn reader_read_bytes_advances_position_strictly() {
599 let bytes = [1, 2, 3, 4, 5, 6, 7, 8];
600 let mut r = BufferReader::new(&bytes, Endianness::Little);
601 let _ = r.read_bytes(3).unwrap();
602 assert_eq!(r.position(), 3);
604 let _ = r.read_bytes(2).unwrap();
605 assert_eq!(r.position(), 5);
607 }
608
609 #[test]
612 fn reader_read_u16_returns_actual_bytes_not_zero() {
613 let bytes = [0x12, 0x34];
614 let mut r = BufferReader::new(&bytes, Endianness::Little);
615 assert_eq!(r.read_u16().unwrap(), 0x3412);
616 let mut r_be = BufferReader::new(&bytes, Endianness::Big);
617 assert_eq!(r_be.read_u16().unwrap(), 0x1234);
618 }
619
620 #[test]
628 fn reader_read_string_invalid_utf8_offset_is_start_plus_four() {
629 let mut bytes = vec![0xAB]; bytes.extend_from_slice(&[0, 0, 0]);
634 bytes.extend_from_slice(&3u32.to_le_bytes());
636 bytes.extend_from_slice(&[0xFF, 0xFE, 0]);
638
639 let mut r = BufferReader::new(&bytes, Endianness::Little);
640 r.read_u8().unwrap();
641 let err = r.read_string().unwrap_err();
642 match err {
645 DecodeError::InvalidUtf8 { offset } => assert_eq!(offset, 5),
646 other => panic!("expected InvalidUtf8, got {other:?}"),
647 }
648 }
649
650 #[test]
653 fn reader_read_string_invalid_utf8_offset_with_start_two() {
654 let mut bytes = vec![0xAB, 0xCD]; bytes.extend_from_slice(&[0, 0]);
657 bytes.extend_from_slice(&3u32.to_le_bytes());
658 bytes.extend_from_slice(&[0xFF, 0xFE, 0]);
659
660 let mut r = BufferReader::new(&bytes, Endianness::Little);
661 r.read_u8().unwrap();
662 r.read_u8().unwrap();
663 let err = r.read_string().unwrap_err();
664 match err {
665 DecodeError::InvalidUtf8 { offset } => assert_eq!(offset, 6),
666 other => panic!("expected InvalidUtf8, got {other:?}"),
667 }
668 }
669}