OXYGENHUB
◆ carried-not-removed oxygen / vendor / crc-fast / src / combine.rs
6.1 KiB · Rust · last changed Strata: the cloud stores send through ureq, so every release target links 1 month ago source blame history raw
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
 [email protected]
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
53
use crate::CrcParams;
54
55
/* Multiply the GF(2) vector vec by the GF(2) matrix mat, returning the
56
resulting vector.  The vector is stored as bits in a crc_t.  The matrix is
57
similarly stored with each column as a crc_t, where the number of columns is
58
at least enough to cover the position of the most significant 1 bit in the
59
vector (so a dimension parameter is not needed). */
60
fn 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
75
the dimension of the matrices, i.e., the number of bits in each crc_t
76
(rows), and the number of crc_t's (columns). */
77
fn 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
84
first sequence of bytes, crc2 is the CRC of the immediately following
85
sequence of bytes, and len2 is the length of the second sequence.  The CRC
86
of the combined sequence is returned. */
87
pub 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
157
fn 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
178
fn 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
}