1use std::fmt;
20
21const CRC32_TABLE: [u32; 256] = {
26 let mut table = [0u32; 256];
27 let mut i = 0u32;
28 while i < 256 {
29 let mut crc = i;
30 let mut j = 0;
31 while j < 8 {
32 if crc & 1 != 0 {
33 crc = (crc >> 1) ^ 0xEDB8_8320;
34 } else {
35 crc >>= 1;
36 }
37 j += 1;
38 }
39 table[i as usize] = crc;
40 i += 1;
41 }
42 table
43};
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum CodecError {
52 BufferTooSmall,
54 MessageTooLarge,
56 InvalidLength,
58 ChecksumMismatch,
60}
61
62impl fmt::Display for CodecError {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 match self {
65 Self::BufferTooSmall => write!(f, "buffer too small"),
66 Self::MessageTooLarge => write!(f, "message too large"),
67 Self::InvalidLength => write!(f, "invalid length prefix"),
68 Self::ChecksumMismatch => write!(f, "checksum mismatch"),
69 }
70 }
71}
72
73impl std::error::Error for CodecError {}
74
75#[derive(Debug, Clone)]
81pub struct CodecConfig {
82 pub max_message_size: usize,
84 pub use_checksum: bool,
86 pub length_prefix_bytes: usize,
88}
89
90impl Default for CodecConfig {
91 fn default() -> Self {
92 Self {
93 max_message_size: 16_777_216, use_checksum: true,
95 length_prefix_bytes: 4,
96 }
97 }
98}
99
100#[derive(Debug, Clone)]
106pub struct EncodedMessage {
107 pub data: Vec<u8>,
109 pub original_size: usize,
111 pub checksum: Option<u32>,
113}
114
115#[derive(Debug, Clone)]
121pub struct CodecStats {
122 pub messages_encoded: u64,
124 pub messages_decoded: u64,
126 pub bytes_encoded: u64,
128 pub bytes_decoded: u64,
130}
131
132pub struct PeerMessageCodec {
149 config: CodecConfig,
150 messages_encoded: std::cell::Cell<u64>,
151 messages_decoded: std::cell::Cell<u64>,
152 bytes_encoded: std::cell::Cell<u64>,
153 bytes_decoded: std::cell::Cell<u64>,
154}
155
156impl fmt::Debug for PeerMessageCodec {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 f.debug_struct("PeerMessageCodec")
159 .field("config", &self.config)
160 .field("messages_encoded", &self.messages_encoded.get())
161 .field("messages_decoded", &self.messages_decoded.get())
162 .finish()
163 }
164}
165
166impl PeerMessageCodec {
167 pub fn new(config: CodecConfig) -> Self {
169 Self {
170 config,
171 messages_encoded: std::cell::Cell::new(0),
172 messages_decoded: std::cell::Cell::new(0),
173 bytes_encoded: std::cell::Cell::new(0),
174 bytes_decoded: std::cell::Cell::new(0),
175 }
176 }
177
178 pub fn encode(&self, payload: &[u8]) -> Result<Vec<u8>, CodecError> {
187 if payload.len() > self.config.max_message_size {
188 return Err(CodecError::MessageTooLarge);
189 }
190
191 let total = self.estimate_encoded_size(payload.len());
192 let mut buf = Vec::with_capacity(total);
193
194 let len_u32 = payload.len() as u32;
196 buf.extend_from_slice(&len_u32.to_be_bytes());
197
198 buf.extend_from_slice(payload);
200
201 if self.config.use_checksum {
203 let crc = Self::crc32(payload);
204 buf.extend_from_slice(&crc.to_be_bytes());
205 }
206
207 self.messages_encoded.set(self.messages_encoded.get() + 1);
209 self.bytes_encoded
210 .set(self.bytes_encoded.get() + payload.len() as u64);
211
212 Ok(buf)
213 }
214
215 pub fn encode_with_metadata(&self, payload: &[u8]) -> Result<EncodedMessage, CodecError> {
217 if payload.len() > self.config.max_message_size {
218 return Err(CodecError::MessageTooLarge);
219 }
220
221 let checksum = if self.config.use_checksum {
222 Some(Self::crc32(payload))
223 } else {
224 None
225 };
226
227 let data = self.encode(payload)?;
228 self.messages_encoded.set(self.messages_encoded.get() - 1);
232 self.bytes_encoded
233 .set(self.bytes_encoded.get() - payload.len() as u64);
234
235 self.messages_encoded.set(self.messages_encoded.get() + 1);
237 self.bytes_encoded
238 .set(self.bytes_encoded.get() + payload.len() as u64);
239
240 Ok(EncodedMessage {
241 data,
242 original_size: payload.len(),
243 checksum,
244 })
245 }
246
247 pub fn decode(&self, data: &[u8]) -> Result<Vec<u8>, CodecError> {
259 if data.len() < 4 {
260 return Err(CodecError::BufferTooSmall);
261 }
262
263 let payload_len = self.decode_length(data)?;
264
265 if payload_len > self.config.max_message_size {
266 return Err(CodecError::MessageTooLarge);
267 }
268
269 let checksum_size = if self.config.use_checksum { 4 } else { 0 };
270 let expected_total = 4 + payload_len + checksum_size;
271
272 if data.len() < expected_total {
273 return Err(CodecError::InvalidLength);
274 }
275
276 let payload = &data[4..4 + payload_len];
277
278 if self.config.use_checksum {
279 let stored_bytes: [u8; 4] = data[4 + payload_len..4 + payload_len + 4]
280 .try_into()
281 .map_err(|_| CodecError::InvalidLength)?;
282 let stored_crc = u32::from_be_bytes(stored_bytes);
283 let computed_crc = Self::crc32(payload);
284 if stored_crc != computed_crc {
285 return Err(CodecError::ChecksumMismatch);
286 }
287 }
288
289 self.messages_decoded.set(self.messages_decoded.get() + 1);
290 self.bytes_decoded
291 .set(self.bytes_decoded.get() + payload_len as u64);
292
293 Ok(payload.to_vec())
294 }
295
296 pub fn crc32(data: &[u8]) -> u32 {
298 let mut crc: u32 = 0xFFFF_FFFF;
299 for &byte in data {
300 let index = ((crc ^ u32::from(byte)) & 0xFF) as usize;
301 crc = (crc >> 8) ^ CRC32_TABLE[index];
302 }
303 crc ^ 0xFFFF_FFFF
304 }
305
306 pub fn estimate_encoded_size(&self, payload_size: usize) -> usize {
310 let checksum_overhead = if self.config.use_checksum { 4 } else { 0 };
311 4 + payload_size + checksum_overhead
312 }
313
314 pub fn decode_length(&self, data: &[u8]) -> Result<usize, CodecError> {
325 if data.len() < 4 {
326 return Err(CodecError::BufferTooSmall);
327 }
328 let bytes: [u8; 4] = data[..4]
329 .try_into()
330 .map_err(|_| CodecError::BufferTooSmall)?;
331 Ok(u32::from_be_bytes(bytes) as usize)
332 }
333
334 pub fn stats(&self) -> CodecStats {
336 CodecStats {
337 messages_encoded: self.messages_encoded.get(),
338 messages_decoded: self.messages_decoded.get(),
339 bytes_encoded: self.bytes_encoded.get(),
340 bytes_decoded: self.bytes_decoded.get(),
341 }
342 }
343
344 pub fn config(&self) -> &CodecConfig {
346 &self.config
347 }
348}
349
350#[cfg(test)]
355mod tests {
356 use super::*;
357
358 fn default_codec() -> PeerMessageCodec {
359 PeerMessageCodec::new(CodecConfig::default())
360 }
361
362 fn no_checksum_codec() -> PeerMessageCodec {
363 PeerMessageCodec::new(CodecConfig {
364 use_checksum: false,
365 ..Default::default()
366 })
367 }
368
369 #[test]
372 fn roundtrip_basic() {
373 let codec = default_codec();
374 let payload = b"hello, ipfrs";
375 let wire = codec.encode(payload).expect("encode failed");
376 let decoded = codec.decode(&wire).expect("decode failed");
377 assert_eq!(decoded, payload);
378 }
379
380 #[test]
381 fn roundtrip_empty_payload() {
382 let codec = default_codec();
383 let wire = codec.encode(b"").expect("encode empty");
384 let decoded = codec.decode(&wire).expect("decode empty");
385 assert!(decoded.is_empty());
386 }
387
388 #[test]
389 fn roundtrip_no_checksum() {
390 let codec = no_checksum_codec();
391 let payload = b"no checksum";
392 let wire = codec.encode(payload).expect("encode");
393 let decoded = codec.decode(&wire).expect("decode");
394 assert_eq!(decoded, payload);
395 }
396
397 #[test]
398 fn roundtrip_large_payload() {
399 let codec = default_codec();
400 let payload = vec![0xAB_u8; 65_536];
401 let wire = codec.encode(&payload).expect("encode large");
402 let decoded = codec.decode(&wire).expect("decode large");
403 assert_eq!(decoded, payload);
404 }
405
406 #[test]
407 fn roundtrip_binary_data() {
408 let codec = default_codec();
409 let payload: Vec<u8> = (0..=255).collect();
410 let wire = codec.encode(&payload).expect("encode binary");
411 let decoded = codec.decode(&wire).expect("decode binary");
412 assert_eq!(decoded, payload);
413 }
414
415 #[test]
416 fn roundtrip_single_byte() {
417 let codec = default_codec();
418 let wire = codec.encode(&[0x42]).expect("encode");
419 let decoded = codec.decode(&wire).expect("decode");
420 assert_eq!(decoded, vec![0x42]);
421 }
422
423 #[test]
426 fn crc32_known_values() {
427 let crc = PeerMessageCodec::crc32(b"123456789");
429 assert_eq!(crc, 0xCBF4_3926);
430 }
431
432 #[test]
433 fn crc32_empty() {
434 let crc = PeerMessageCodec::crc32(b"");
435 assert_eq!(crc, 0x0000_0000);
436 }
437
438 #[test]
439 fn corrupted_payload_detected() {
440 let codec = default_codec();
441 let mut wire = codec.encode(b"integrity").expect("encode");
442 wire[5] ^= 0xFF;
444 let result = codec.decode(&wire);
445 assert_eq!(result, Err(CodecError::ChecksumMismatch));
446 }
447
448 #[test]
449 fn corrupted_checksum_detected() {
450 let codec = default_codec();
451 let mut wire = codec.encode(b"test").expect("encode");
452 let last = wire.len() - 1;
454 wire[last] ^= 0x01;
455 let result = codec.decode(&wire);
456 assert_eq!(result, Err(CodecError::ChecksumMismatch));
457 }
458
459 #[test]
460 fn no_checksum_no_verification() {
461 let codec = no_checksum_codec();
462 let mut wire = codec.encode(b"test").expect("encode");
463 wire[5] ^= 0xFF;
465 let result = codec.decode(&wire);
466 assert!(result.is_ok());
467 }
468
469 #[test]
472 fn message_too_large() {
473 let codec = PeerMessageCodec::new(CodecConfig {
474 max_message_size: 100,
475 ..Default::default()
476 });
477 let payload = vec![0u8; 101];
478 assert_eq!(codec.encode(&payload), Err(CodecError::MessageTooLarge));
479 }
480
481 #[test]
482 fn message_exactly_max_size() {
483 let codec = PeerMessageCodec::new(CodecConfig {
484 max_message_size: 100,
485 ..Default::default()
486 });
487 let payload = vec![0u8; 100];
488 let wire = codec.encode(&payload).expect("encode at limit");
489 let decoded = codec.decode(&wire).expect("decode at limit");
490 assert_eq!(decoded, payload);
491 }
492
493 #[test]
494 fn decode_rejects_oversized_length() {
495 let codec = PeerMessageCodec::new(CodecConfig {
496 max_message_size: 10,
497 ..Default::default()
498 });
499 let mut wire = vec![0, 0, 0, 100];
501 wire.extend_from_slice(&[0u8; 100]);
502 wire.extend_from_slice(&[0u8; 4]); assert_eq!(codec.decode(&wire), Err(CodecError::MessageTooLarge));
504 }
505
506 #[test]
509 fn decode_empty_buffer() {
510 let codec = default_codec();
511 assert_eq!(codec.decode(&[]), Err(CodecError::BufferTooSmall));
512 }
513
514 #[test]
515 fn decode_short_buffer() {
516 let codec = default_codec();
517 assert_eq!(codec.decode(&[0, 0, 1]), Err(CodecError::BufferTooSmall));
518 }
519
520 #[test]
521 fn decode_truncated_payload() {
522 let codec = default_codec();
523 let wire = vec![0, 0, 0, 10, 0xAA, 0xBB];
525 assert_eq!(codec.decode(&wire), Err(CodecError::InvalidLength));
526 }
527
528 #[test]
529 fn decode_truncated_checksum() {
530 let codec = default_codec();
531 let mut wire = vec![0, 0, 0, 4];
533 wire.extend_from_slice(&[1, 2, 3, 4]);
534 wire.extend_from_slice(&[0, 0]);
536 assert_eq!(codec.decode(&wire), Err(CodecError::InvalidLength));
537 }
538
539 #[test]
542 fn length_prefix_correctness() {
543 let codec = default_codec();
544 let payload = vec![0u8; 300];
545 let wire = codec.encode(&payload).expect("encode");
546 assert_eq!(&wire[..4], &[0, 0, 1, 44]); }
549
550 #[test]
551 fn decode_length_peek() {
552 let codec = default_codec();
553 let payload = b"peek test";
554 let wire = codec.encode(payload).expect("encode");
555 let peeked = codec.decode_length(&wire).expect("peek");
556 assert_eq!(peeked, payload.len());
557 }
558
559 #[test]
560 fn decode_length_too_short() {
561 let codec = default_codec();
562 assert_eq!(
563 codec.decode_length(&[0, 1]),
564 Err(CodecError::BufferTooSmall)
565 );
566 }
567
568 #[test]
571 fn estimate_with_checksum() {
572 let codec = default_codec();
573 assert_eq!(codec.estimate_encoded_size(100), 4 + 100 + 4);
574 }
575
576 #[test]
577 fn estimate_without_checksum() {
578 let codec = no_checksum_codec();
579 assert_eq!(codec.estimate_encoded_size(100), 4 + 100);
580 }
581
582 #[test]
583 fn estimate_matches_actual() {
584 let codec = default_codec();
585 let payload = b"size check";
586 let wire = codec.encode(payload).expect("encode");
587 assert_eq!(wire.len(), codec.estimate_encoded_size(payload.len()));
588 }
589
590 #[test]
591 fn estimate_zero_payload() {
592 let codec = default_codec();
593 let wire = codec.encode(b"").expect("encode empty");
594 assert_eq!(wire.len(), codec.estimate_encoded_size(0));
595 }
596
597 #[test]
600 fn stats_initial_zero() {
601 let codec = default_codec();
602 let s = codec.stats();
603 assert_eq!(s.messages_encoded, 0);
604 assert_eq!(s.messages_decoded, 0);
605 assert_eq!(s.bytes_encoded, 0);
606 assert_eq!(s.bytes_decoded, 0);
607 }
608
609 #[test]
610 fn stats_encode_tracked() {
611 let codec = default_codec();
612 let _ = codec.encode(b"abc");
613 let _ = codec.encode(b"defgh");
614 let s = codec.stats();
615 assert_eq!(s.messages_encoded, 2);
616 assert_eq!(s.bytes_encoded, 8); }
618
619 #[test]
620 fn stats_decode_tracked() {
621 let codec = default_codec();
622 let w1 = codec.encode(b"one").expect("e1");
623 let w2 = codec.encode(b"two").expect("e2");
624 let _ = codec.decode(&w1);
625 let _ = codec.decode(&w2);
626 let s = codec.stats();
627 assert_eq!(s.messages_decoded, 2);
628 assert_eq!(s.bytes_decoded, 6);
629 }
630
631 #[test]
632 fn stats_failed_encode_not_counted() {
633 let codec = PeerMessageCodec::new(CodecConfig {
634 max_message_size: 5,
635 ..Default::default()
636 });
637 let _ = codec.encode(&[0u8; 10]); assert_eq!(codec.stats().messages_encoded, 0);
639 }
640
641 #[test]
642 fn stats_failed_decode_not_counted() {
643 let codec = default_codec();
644 let _ = codec.decode(&[]); assert_eq!(codec.stats().messages_decoded, 0);
646 }
647
648 #[test]
651 fn encode_with_metadata_checksum_present() {
652 let codec = default_codec();
653 let em = codec.encode_with_metadata(b"meta").expect("meta encode");
654 assert_eq!(em.original_size, 4);
655 assert!(em.checksum.is_some());
656 let decoded = codec.decode(&em.data).expect("meta decode");
658 assert_eq!(decoded, b"meta");
659 }
660
661 #[test]
662 fn encode_with_metadata_no_checksum() {
663 let codec = no_checksum_codec();
664 let em = codec.encode_with_metadata(b"no_crc").expect("meta encode");
665 assert_eq!(em.original_size, 6);
666 assert!(em.checksum.is_none());
667 }
668
669 #[test]
672 fn codec_error_display() {
673 assert_eq!(
674 format!("{}", CodecError::BufferTooSmall),
675 "buffer too small"
676 );
677 assert_eq!(
678 format!("{}", CodecError::MessageTooLarge),
679 "message too large"
680 );
681 assert_eq!(
682 format!("{}", CodecError::InvalidLength),
683 "invalid length prefix"
684 );
685 assert_eq!(
686 format!("{}", CodecError::ChecksumMismatch),
687 "checksum mismatch"
688 );
689 }
690
691 #[test]
692 fn codec_debug_impl() {
693 let codec = default_codec();
694 let dbg = format!("{:?}", codec);
695 assert!(dbg.contains("PeerMessageCodec"));
696 }
697
698 #[test]
699 fn config_default_values() {
700 let cfg = CodecConfig::default();
701 assert_eq!(cfg.max_message_size, 16_777_216);
702 assert!(cfg.use_checksum);
703 assert_eq!(cfg.length_prefix_bytes, 4);
704 }
705}