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::structs::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 ^ 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}