1const fn crc32_table() -> [u32; 256] {
11 let mut table = [0u32; 256];
12 let mut i = 0;
13 while i < 256 {
14 let mut crc = i as u32;
15 let mut j = 0;
16 while j < 8 {
17 if crc & 1 != 0 {
18 crc = 0xEDB8_8320 ^ (crc >> 1);
19 } else {
20 crc >>= 1;
21 }
22 j += 1;
23 }
24 table[i] = crc;
25 i += 1;
26 }
27 table
28}
29
30pub(crate) fn crc32_update(crc: u32, data: &[u8]) -> u32 {
33 const CRC32_TABLE: [u32; 256] = crc32_table();
34 let mut crc = !crc;
35 for &byte in data {
36 crc = CRC32_TABLE[((crc ^ u32::from(byte)) & 0xFF) as usize] ^ (crc >> 8);
37 }
38 !crc
39}
40
41pub(crate) fn crc32_hash(data: &[u8]) -> u32 {
43 crc32_update(0, data)
44}
45
46pub(crate) fn chrono_lite_now() -> String {
52 use std::time::{SystemTime, UNIX_EPOCH};
53 let duration = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default();
54 format!("{}", duration.as_secs())
55}
56
57pub(crate) fn pad_right(s: &str, width: usize) -> String {
63 if s.len() >= width {
64 s[..width].to_string()
65 } else {
66 format!("{}{}", s, " ".repeat(width - s.len()))
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
79 fn test_crc32_table_length() {
80 let table = crc32_table();
81 assert_eq!(table.len(), 256, "CRC32 table must have exactly 256 entries");
82 }
83
84 #[test]
85 fn test_crc32_table_first_entry_is_zero() {
86 let table = crc32_table();
87 assert_eq!(table[0], 0x0000_0000, "CRC32 table[0] must be 0 (identity)");
88 }
89
90 #[test]
91 fn test_crc32_table_known_entries() {
92 let table = crc32_table();
94 assert_eq!(table[1], 0x7707_3096, "table[1] mismatch for IEEE CRC32");
95 assert_eq!(table[2], 0xEE0E_612C, "table[2] mismatch for IEEE CRC32");
96 assert_eq!(table[3], 0x9909_51BA, "table[3] mismatch for IEEE CRC32");
97 assert_eq!(table[4], 0x076D_C419, "table[4] mismatch for IEEE CRC32");
98 assert_eq!(table[128], 0xEDB8_8320, "table[128] must equal the polynomial");
99 assert_eq!(table[255], 0x2D02_EF8D, "table[255] mismatch for IEEE CRC32");
100 }
101
102 #[test]
103 fn test_crc32_table_no_duplicate_nonzero_entries() {
104 let table = crc32_table();
105 let mut seen = std::collections::HashSet::new();
107 for (i, &val) in table.iter().enumerate() {
108 if val != 0 {
109 assert!(seen.insert(val), "Duplicate nonzero entry 0x{:08X} at index {}", val, i);
110 }
111 }
112 }
113
114 #[test]
115 fn test_crc32_table_polynomial_property() {
116 let table = crc32_table();
120 assert_eq!(table[128], 0xEDB8_8320, "table[128] must equal the reflected CRC32 polynomial");
121 }
122
123 #[test]
124 fn test_crc32_table_is_const_deterministic() {
125 let table1 = crc32_table();
127 let table2 = crc32_table();
128 assert_eq!(table1, table2, "crc32_table() must be deterministic");
129 }
130
131 #[test]
136 fn test_crc32_hash_empty() {
137 let result = crc32_hash(&[]);
139 assert_eq!(result, 0x0000_0000);
140 }
141
142 #[test]
143 fn test_crc32_hash_known_value() {
144 let result = crc32_hash(b"123456789");
146 assert_eq!(result, 0xCBF4_3926);
147 }
148
149 #[test]
150 fn test_crc32_hash_single_byte() {
151 let result = crc32_hash(&[0x00]);
153 assert_ne!(result, 0);
154 assert_eq!(result, 0xD202_EF8D);
156 }
157
158 #[test]
159 fn test_crc32_hash_different_inputs_differ() {
160 let hash_a = crc32_hash(b"hello");
161 let hash_b = crc32_hash(b"world");
162 assert_ne!(hash_a, hash_b, "Different inputs should produce different hashes");
163 }
164
165 #[test]
166 fn test_crc32_hash_deterministic() {
167 let hash1 = crc32_hash(b"test data");
168 let hash2 = crc32_hash(b"test data");
169 assert_eq!(hash1, hash2, "Same input should produce same hash");
170 }
171
172 #[test]
173 fn test_crc32_update_incremental() {
174 let data = b"hello world";
176 let single_pass = crc32_hash(data);
177
178 let part1 = crc32_update(0, b"hello ");
179 let incremental = crc32_update(part1, b"world");
180 assert_eq!(single_pass, incremental, "Incremental CRC32 must match single-pass");
181 }
182
183 #[test]
184 fn test_crc32_update_from_nonzero_initial() {
185 let from_zero = crc32_update(0, b"test");
187 let from_nonzero = crc32_update(0x1234_5678, b"test");
188 assert_ne!(
189 from_zero, from_nonzero,
190 "Different initial CRC should produce different result"
191 );
192 }
193
194 #[test]
195 fn test_crc32_hash_all_zeros() {
196 let result = crc32_hash(&[0u8; 16]);
197 assert_ne!(result, 0, "CRC32 of all-zero data should not be zero");
198 }
199
200 #[test]
201 fn test_crc32_hash_all_ones() {
202 let result = crc32_hash(&[0xFFu8; 16]);
203 assert_ne!(result, 0, "CRC32 of all-0xFF data should not be zero");
204 }
205
206 #[test]
207 fn test_crc32_hash_large_data() {
208 let data: Vec<u8> = (0..1024).map(|i| (i % 256) as u8).collect();
210 let result = crc32_hash(&data);
211 assert_ne!(result, 0);
212 let result2 = crc32_hash(&data);
214 assert_eq!(result, result2);
215 }
216
217 #[test]
222 fn test_chrono_lite_now_is_numeric() {
223 let ts = chrono_lite_now();
224 let parsed: u64 = ts.parse().expect("Timestamp should be a parseable number");
225 assert!(parsed > 0, "Timestamp should be positive");
226 }
227
228 #[test]
229 fn test_chrono_lite_now_is_reasonable() {
230 let ts = chrono_lite_now();
231 let secs: u64 = ts.parse().expect("Should be parseable");
232 assert!(secs > 1_577_836_800, "Timestamp should be after 2020");
234 assert!(secs < 4_102_444_800, "Timestamp should be before 2100");
235 }
236
237 #[test]
238 fn test_chrono_lite_now_monotonic() {
239 let ts1 = chrono_lite_now();
240 let ts2 = chrono_lite_now();
241 let secs1: u64 = ts1.parse().expect("Should be parseable");
242 let secs2: u64 = ts2.parse().expect("Should be parseable");
243 assert!(secs2 >= secs1, "Timestamps should be monotonically non-decreasing");
244 }
245
246 #[test]
251 fn test_pad_right_shorter_than_width() {
252 let result = pad_right("hi", 10);
253 assert_eq!(result, "hi ");
254 assert_eq!(result.len(), 10);
255 }
256
257 #[test]
258 fn test_pad_right_exact_width() {
259 let result = pad_right("exact", 5);
260 assert_eq!(result, "exact");
261 assert_eq!(result.len(), 5);
262 }
263
264 #[test]
265 fn test_pad_right_longer_than_width() {
266 let result = pad_right("toolong", 4);
267 assert_eq!(result, "tool");
268 assert_eq!(result.len(), 4);
269 }
270
271 #[test]
272 fn test_pad_right_empty_string() {
273 let result = pad_right("", 5);
274 assert_eq!(result, " ");
275 assert_eq!(result.len(), 5);
276 }
277
278 #[test]
279 fn test_pad_right_zero_width() {
280 let result = pad_right("test", 0);
281 assert_eq!(result, "");
282 assert_eq!(result.len(), 0);
283 }
284
285 #[test]
286 fn test_pad_right_width_one() {
287 let result = pad_right("abc", 1);
288 assert_eq!(result, "a");
289
290 let result2 = pad_right("x", 1);
291 assert_eq!(result2, "x");
292 }
293
294 #[test]
295 fn test_pad_right_single_char() {
296 let result = pad_right("x", 5);
297 assert_eq!(result, "x ");
298 assert_eq!(result.len(), 5);
299 }
300}