///|
/// Precomputed 256-entry table for the reflected IEEE CRC-32 polynomial
/// (0xEDB88320): entry `n` is the CRC of the single byte `n`.
let crc32_table : FixedArray[UInt] = crc32_make_table()
///|
fn crc32_make_table() -> FixedArray[UInt] {
let table : FixedArray[UInt] = FixedArray::make(256, 0)
for n in 0..<256 {
let mut c = n.reinterpret_as_uint()
for _ in 0..<=7 {
if (c & 1) != 0 {
c = (c >> 1) ^ 0xEDB88320
} else {
c = c >> 1
}
}
table[n] = c
}
table
}
///|
/// Computes the CRC-32 (IEEE, as used by ZIP) of a byte view.
pub fn crc32(bytes : BytesView) -> UInt {
let mut crc : UInt = 0xFFFFFFFF
for b in bytes {
crc = crc32_table[((crc ^ b.to_uint()) & 0xFF).reinterpret_as_int()] ^
(crc >> 8)
}
crc ^ 0xFFFFFFFF
}
///|
/// Computes ZIP CRC-32 while polling `cancelled` at bounded byte intervals.
/// This is intended for archive verification on untrusted, size-bounded input;
/// callers that do not need cooperative cancellation can use `crc32`.
pub fn crc32_cancellable(
bytes : BytesView,
cancelled? : () -> Bool = () => false,
) -> UInt raise ZipError {
check_zip_cancelled(cancelled)
let mut crc : UInt = 0xFFFFFFFF
let mut index = 0
while index < bytes.length() {
if (index & 4095) == 0 {
check_zip_cancelled(cancelled)
}
let byte = bytes[index]
crc = crc32_table[((crc ^ byte.to_uint()) & 0xFF).reinterpret_as_int()] ^
(crc >> 8)
index += 1
}
check_zip_cancelled(cancelled)
crc ^ 0xFFFFFFFF
}
///|
test "cancellable CRC matches CRC-32 and polls inside long input" {
let bytes = Bytes::make(16 * 1024, b'x')
assert_eq(crc32_cancellable(bytes), crc32(bytes))
let checks = [0]
try
crc32_cancellable(bytes, cancelled=() => {
checks[0] += 1
checks[0] >= 4
})
catch {
ReadCancelled => assert_true(checks[0] >= 4)
_ => fail("expected CRC cancellation")
} noraise {
_ => fail("expected CRC cancellation")
}
}