zus_common/
codec_utils.rs

1//! Shared codec utilities for ZUS RPC protocols
2//!
3//! This module provides common utilities used by both the standard 40-byte protocol
4//! and the mobile 16-byte protocol:
5//! - CRC calculations (header CRC16, data CRC32/CRC16)
6//! - Common flag definitions
7
8/// Calculate header CRC16 using truncating addition
9///
10/// This is NOT a standard CRC algorithm. It's a simple sum where each field
11/// is truncated to 16 bits before adding. This matches the Java/C++ implementations.
12///
13/// # Algorithm
14/// ```text
15/// crc = 0
16/// for each field in header:
17///     crc = (crc + (field & 0xFFFF)) & 0xFFFF  // Truncating addition
18/// ```
19#[inline]
20pub fn calculate_header_crc16(fields: &[u64]) -> u16 {
21  let mut crc: u16 = 0;
22  for &field in fields {
23    crc = crc.wrapping_add(field as u16);
24  }
25  crc
26}
27
28/// Calculate data CRC32 using IEEE polynomial (ISO-HDLC)
29///
30/// This is the standard CRC32 used for body data validation.
31/// Matches Java's `java.util.zip.CRC32` and C++ CRC implementations.
32#[inline]
33pub fn calculate_data_crc32(data: &[u8]) -> u32 {
34  let crc = crc::Crc::<u32>::new(&crc::CRC_32_ISO_HDLC);
35  let mut digest = crc.digest();
36  digest.update(data);
37  digest.finalize()
38}
39
40/// Calculate data CRC16 (lower 16 bits of CRC32)
41///
42/// Mobile protocol uses lower 16 bits of the CRC32 for data validation.
43/// This saves 2 bytes in the header while still providing reasonable error detection.
44#[inline]
45pub fn calculate_data_crc16(data: &[u8]) -> u16 {
46  calculate_data_crc32(data) as u16
47}
48
49/// Common flag bit definitions
50pub mod flags {
51  /// Data is compressed
52  pub const COMPRESSED: u8 = 0x01;
53  /// Data is encrypted
54  pub const ENCRYPTED: u8 = 0x02;
55  /// Has extra header data (standard protocol only)
56  pub const EXTRA_DATA: u8 = 0x04;
57  /// Data fragment flag (mobile protocol only)
58  pub const DATA_FRAG: u8 = 0x04;
59}
60
61/// Message types shared between protocols
62pub mod msg_types {
63  /// Request message
64  pub const REQUEST: u8 = 1;
65  /// Response message
66  pub const RESPONSE: u8 = 2;
67  /// Notification message (no response expected)
68  pub const NOTIFY: u8 = 3;
69  /// System response (error when server didn't complete)
70  pub const SYS_RESPONSE: u8 = 4;
71}
72
73#[cfg(test)]
74mod tests {
75  use super::*;
76
77  #[test]
78  fn test_header_crc16_calculation() {
79    // Test with known values
80    let fields: &[u64] = &[0xD3A7, 0x0000, 0x12345678, 0x0708, 0x01, 0x00, 0x03E8, 0x1388, 0x03E7];
81    let crc = calculate_header_crc16(fields);
82    assert!(crc > 0);
83
84    // Test wrapping behavior
85    let fields_large: &[u64] = &[0xFFFF, 0xFFFF];
86    let crc_wrap = calculate_header_crc16(fields_large);
87    assert_eq!(crc_wrap, 0xFFFE); // 0xFFFF + 0xFFFF wraps to 0xFFFE
88  }
89
90  #[test]
91  fn test_data_crc32() {
92    let data = b"hello world";
93    let crc = calculate_data_crc32(data);
94    assert!(crc > 0);
95
96    // Same data should produce same CRC
97    let crc2 = calculate_data_crc32(data);
98    assert_eq!(crc, crc2);
99
100    // Different data should produce different CRC
101    let crc3 = calculate_data_crc32(b"hello World");
102    assert_ne!(crc, crc3);
103  }
104
105  #[test]
106  fn test_data_crc16() {
107    let data = b"test data for crc";
108    let crc32 = calculate_data_crc32(data);
109    let crc16 = calculate_data_crc16(data);
110
111    // CRC16 should be lower 16 bits of CRC32
112    assert_eq!(crc16, crc32 as u16);
113  }
114
115  #[test]
116  fn test_empty_data_crc() {
117    let empty: &[u8] = &[];
118    let crc32 = calculate_data_crc32(empty);
119    let crc16 = calculate_data_crc16(empty);
120
121    // Empty data has specific CRC value
122    assert_eq!(crc32, 0); // IEEE CRC32 of empty is 0
123    assert_eq!(crc16, 0);
124  }
125}