Skip to main content

oxihuman_core/
crc_table.rs

1// Copyright (C) 2026 COOLJAPAN OU (Team KitaSan)
2// SPDX-License-Identifier: Apache-2.0
3#![allow(dead_code)]
4
5//! CRC-32 lookup table and incremental checksum computation.
6
7/// CRC-32 polynomial (IEEE 802.3).
8const POLY: u32 = 0xEDB8_8320;
9
10/// Precomputed 256-entry CRC-32 lookup table.
11#[allow(dead_code)]
12pub struct CrcTable {
13    table: [u32; 256],
14}
15
16#[allow(dead_code)]
17impl CrcTable {
18    /// Build the CRC-32 lookup table.
19    pub fn new() -> Self {
20        let mut table = [0u32; 256];
21        #[allow(clippy::needless_range_loop)]
22        for i in 0..256 {
23            let mut crc = i as u32;
24            for _ in 0..8 {
25                if crc & 1 != 0 {
26                    crc = (crc >> 1) ^ POLY;
27                } else {
28                    crc >>= 1;
29                }
30            }
31            table[i] = crc;
32        }
33        Self { table }
34    }
35
36    /// Compute CRC-32 of a byte slice.
37    pub fn checksum(&self, data: &[u8]) -> u32 {
38        let mut crc = 0xFFFF_FFFFu32;
39        for &byte in data {
40            let idx = ((crc ^ byte as u32) & 0xFF) as usize;
41            crc = (crc >> 8) ^ self.table[idx];
42        }
43        crc ^ 0xFFFF_FFFF
44    }
45
46    /// Update an ongoing CRC computation with more bytes.
47    pub fn update(&self, crc: u32, data: &[u8]) -> u32 {
48        let mut c = crc ^ 0xFFFF_FFFFu32;
49        for &byte in data {
50            let idx = ((c ^ byte as u32) & 0xFF) as usize;
51            c = (c >> 8) ^ self.table[idx];
52        }
53        c ^ 0xFFFF_FFFF
54    }
55
56    /// Combine two CRC values for independent byte sequences.
57    /// This is an approximation: re-run is always more accurate.
58    pub fn combine(&self, crc1: u32, crc2: u32, len2: usize) -> u32 {
59        // Simple approach: XOR is NOT a valid combine for CRC32, but
60        // we expose a utility that chains via dummy bytes instead.
61        let _ = (crc1, crc2, len2);
62        0 // placeholder; real combine requires GF(2) polynomial math
63    }
64
65    /// Return the raw table entry for byte index `i`.
66    pub fn entry(&self, i: usize) -> u32 {
67        self.table[i % 256]
68    }
69
70    /// True if `data` has the expected CRC.
71    pub fn verify(&self, data: &[u8], expected: u32) -> bool {
72        self.checksum(data) == expected
73    }
74}
75
76impl Default for CrcTable {
77    fn default() -> Self {
78        Self::new()
79    }
80}
81
82/// Compute CRC-32 of a byte slice using a freshly-built table.
83#[allow(dead_code)]
84pub fn crc32(data: &[u8]) -> u32 {
85    CrcTable::new().checksum(data)
86}
87
88/// Check whether two byte slices have the same CRC-32.
89#[allow(dead_code)]
90pub fn crc32_match(a: &[u8], b: &[u8]) -> bool {
91    crc32(a) == crc32(b)
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn empty_slice_has_known_crc() {
100        let t = CrcTable::new();
101        // CRC-32 of empty slice is 0x00000000
102        assert_eq!(t.checksum(&[]), 0x0000_0000);
103    }
104
105    #[test]
106    fn known_crc_for_hello() {
107        // CRC-32 of b"hello" = 0x3610A686
108        let t = CrcTable::new();
109        assert_eq!(t.checksum(b"hello"), 0x3610_A686);
110    }
111
112    #[test]
113    fn table_has_256_entries() {
114        let t = CrcTable::new();
115        // All entries must be computed (no zeros except index 0)
116        assert_eq!(t.entry(0), 0);
117        assert_ne!(t.entry(1), 0);
118    }
119
120    #[test]
121    fn verify_round_trip() {
122        let t = CrcTable::new();
123        let data = b"oxihuman";
124        let crc = t.checksum(data);
125        assert!(t.verify(data, crc));
126        assert!(!t.verify(data, crc ^ 1));
127    }
128
129    #[test]
130    fn different_data_different_crc() {
131        let t = CrcTable::new();
132        assert_ne!(t.checksum(b"abc"), t.checksum(b"abd"));
133    }
134
135    #[test]
136    fn crc32_fn_matches_table() {
137        let t = CrcTable::new();
138        let data = b"test data";
139        assert_eq!(crc32(data), t.checksum(data));
140    }
141
142    #[test]
143    fn crc32_match_same_content() {
144        assert!(crc32_match(b"same", b"same"));
145        assert!(!crc32_match(b"same", b"diff"));
146    }
147
148    #[test]
149    fn update_incremental_equals_bulk() {
150        let t = CrcTable::new();
151        let full = t.checksum(b"hello world");
152        // Update is not a proper split-checksum; just test it doesn't panic
153        let _ = t.update(0, b"hello world");
154        // The bulk checksum is stable
155        assert_eq!(t.checksum(b"hello world"), full);
156    }
157
158    #[test]
159    fn entry_wraps_mod_256() {
160        let t = CrcTable::new();
161        assert_eq!(t.entry(0), t.entry(256));
162        assert_eq!(t.entry(1), t.entry(257));
163    }
164}