crc_fast/combine.rs
1//! This module provides a function to combine CRCs of two sequences of bytes.
2//!
3//! It is based on the work of Mark Adler and is designed to be used with
4//! different CRC algorithms.
5/*
6 Derived from this excellent answer by Mark Adler on StackOverflow:
7 https://stackoverflow.com/questions/29915764/generic-crc-8-16-32-64-combine-implementation/29928573#29928573
8*/
9
10/* crccomb.c -- generalized combination of CRCs
11 * Copyright (C) 2015 Mark Adler
12 * Version 1.1 29 Apr 2015 Mark Adler
13 */
14
15/*
16 This software is provided 'as-is', without any express or implied
17 warranty. In no event will the author be held liable for any damages
18 arising from the use of this software.
19
20 Permission is granted to anyone to use this software for any purpose,
21 including commercial applications, and to alter it and redistribute it
22 freely, subject to the following restrictions:
23
24 1. The origin of this software must not be misrepresented; you must not
25 claim that you wrote the original software. If you use this software
26 in a product, an acknowledgment in the product documentation would be
27 appreciated but is not required.
28 2. Altered source versions must be plainly marked as such, and must not be
29 misrepresented as being the original software.
30 3. This notice may not be removed or altered from any source distribution.
31
32 Mark Adler
33 madler@alumni.caltech.edu
34*/
35
36/*
37 zlib provides a fast operation to combine the CRCs of two sequences of bytes
38 into a single CRC, which is the CRC of the two sequences concatenated. That
39 operation requires only the two CRC's and the length of the second sequence.
40 The routine in zlib only works on the particular CRC-32 used by zlib. The
41 code provided here generalizes that operation to apply to a wide range of
42 CRCs. The CRC is specified in a series of #defines, based on the
43 parameterization found in Ross William's excellent CRC tutorial here:
44
45 http://www.ross.net/crc/download/crc_v3.txt
46
47 A comprehensive catalogue of known CRCs, their parameters, check values, and
48 references can be found here:
49
50 http://reveng.sourceforge.net/crc-catalogue/all.htm
51*/
52
53use crate::CrcParams;
54
55/* Multiply the GF(2) vector vec by the GF(2) matrix mat, returning the
56resulting vector. The vector is stored as bits in a crc_t. The matrix is
57similarly stored with each column as a crc_t, where the number of columns is
58at least enough to cover the position of the most significant 1 bit in the
59vector (so a dimension parameter is not needed). */
60fn gf2_matrix_times(mat: &[u64; 64], mut vec: u64) -> u64 {
61 let mut sum = 0;
62 let mut idx = 0;
63 while vec > 0 {
64 if vec & 1 == 1 {
65 sum ^= mat[idx];
66 }
67 vec >>= 1;
68 idx += 1;
69 }
70
71 sum
72}
73
74/* Multiply the matrix mat by itself, returning the result in square. WIDTH is
75the dimension of the matrices, i.e., the number of bits in each crc_t
76(rows), and the number of crc_t's (columns). */
77fn gf2_matrix_square(square: &mut [u64; 64], mat: &[u64; 64]) {
78 for n in 0..64 {
79 square[n] = gf2_matrix_times(mat, mat[n]);
80 }
81}
82
83/* Combine the CRCs of two successive sequences, where crc1 is the CRC of the
84first sequence of bytes, crc2 is the CRC of the immediately following
85sequence of bytes, and len2 is the length of the second sequence. The CRC
86of the combined sequence is returned. */
87pub fn checksums(mut crc1: u64, crc2: u64, mut len2: u64, params: &CrcParams) -> u64 {
88 let mut col: u64;
89 let mut even = [0u64; 64]; /* even-power-of-two zeros operator */
90 let mut odd = [0u64; 64]; /* odd-power-of-two zeros operator */
91
92 /* exclusive-or the result with len2 zeros applied to the CRC of an empty
93 sequence */
94 crc1 ^= params.init_algorithm ^ params.xorout;
95
96 /* construct the operator for one zero bit and put in odd[] */
97 if params.refin && params.refout {
98 // use the reflected POLY
99 odd[0] = reflect_poly(params.poly, params.width as u32);
100 col = 1;
101 for n in 1..params.width {
102 odd[n as usize] = col;
103 col <<= 1;
104 }
105 } else if !params.refin && !params.refout {
106 col = 2;
107 for n in 0..params.width - 1 {
108 odd[n as usize] = col;
109 col <<= 1;
110 }
111 // Put poly at the last valid index (width-1)
112 odd[(params.width - 1) as usize] = params.poly;
113 } else {
114 panic!("Unsupported CRC configuration");
115 }
116
117 /* put operator for two zero bits in even */
118 gf2_matrix_square(&mut even, &odd);
119
120 /* put operator for four zero bits in odd */
121 gf2_matrix_square(&mut odd, &even);
122
123 /* apply len2 zeros to crc1 (first square will put the operator for one
124 zero byte, eight zero bits, in even) */
125 loop {
126 /* apply zeros operator for this bit of len2 */
127 gf2_matrix_square(&mut even, &odd);
128 if len2 & 1 == 1 {
129 crc1 = gf2_matrix_times(&even, crc1);
130 }
131 len2 >>= 1;
132
133 /* if no more bits set, then done */
134 if len2 == 0 {
135 break;
136 }
137
138 /* another iteration of the loop with odd and even swapped */
139 gf2_matrix_square(&mut odd, &even);
140 if len2 & 1 == 1 {
141 crc1 = gf2_matrix_times(&odd, crc1);
142 }
143 len2 >>= 1;
144
145 /* if no more bits set, then done */
146 if len2 == 0 {
147 break;
148 }
149 }
150
151 /* return combined crc */
152 crc1 ^= crc2;
153
154 crc1
155}
156
157fn reflect_poly(poly: u64, width: u32) -> u64 {
158 assert!(width <= 64, "Width must be <= 64 bits");
159
160 // First reverse all bits
161 let reversed = bit_reverse(poly);
162
163 // Shift right to get the significant bits in the correct position
164 // For a 32-bit poly, we need to shift right by (64 - 32) = 32 bits
165 let shifted = reversed >> (64 - width);
166
167 // Create mask for the target width
168 let mask = if width == 64 {
169 u64::MAX
170 } else {
171 (1u64 << width) - 1
172 };
173
174 // Apply mask to ensure we only keep the bits we want
175 shifted & mask
176}
177
178fn bit_reverse(mut forward: u64) -> u64 {
179 let mut reversed = 0;
180
181 for _ in 0..64 {
182 reversed <<= 1;
183 reversed |= forward & 1;
184 forward >>= 1;
185 }
186
187 reversed
188}