commit 0d02e98749b10b418bef2b13566f38d65c57a5b4 Author: Jonathan Strong Date: Wed Oct 5 12:46:04 2022 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1a1e473 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/Cargo.lock +*.swp diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..de0fb64 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "const-crc32" +version = "1.0.0" +edition = "2021" +authors = ["Jonathan Strong "] +license = "MIT" +description = "A `const fn` implementation of crc32 checksum algorithm" +repository = "https://git.shipyard.rs/jstrong/const-crc32" +keywords = ["checksum", "crc", "crc32", "const"] + +[dev-dependencies] +crc32fast = "1.2" +rand = "0.8" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1a5a8bc --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Jonathan Strong + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4a9a992 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,93 @@ +//! A `const fn` crc32 checksum implementation. +//! +//! # Examples +//! +//! ``` +//! const BYTES: &[u8] = "The quick brown fox jumps over the lazy dog".as_bytes(); +//! assert_eq!(const_crc32::crc32(BYTES), 0x414fa339_u32); +//! ``` + +/// typically crc32 implementations set up a [u32; 256] lookup table. this computes +/// the table on demand for a given "index" `i` +const fn table_fn(i: u32) -> u32 { + let mut out = i; + + out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; + out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; + out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; + out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; + out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; + out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; + out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; + out = if out & 1 == 1 { 0xedb88320 ^ (out >> 1) } else { out >> 1 }; + + out +} + +/// A `const fn` crc32 checksum implementation. +/// +/// Note: this is a naive implementation that should be expected to have poor performance +/// if used on dynamic data at runtime. Usage should generally be restricted to declaring +/// `const` variables based on `static` or `const` data available at build time. +pub const fn crc32(buf: &[u8]) -> u32 { + let mut out = !0u32; + let mut i = 0; + loop { + if i >= buf.len() { break } + + out = (out >> 8) ^ table_fn((out & 0xff) ^ (buf[i] as u32)); + + i += 1; + } + !out +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::prelude::*; + + fn crc32_compute_table() -> [u32; 256] { + let mut crc32_table = [0; 256]; + + for n in 0..256 { + crc32_table[n as usize] = (0..8).fold(n as u32, |acc, _| { + match acc & 1 { + 1 => 0xedb88320 ^ (acc >> 1), + _ => acc >> 1, + } + }); + } + + crc32_table + } + + #[test] + fn check_table_fn_against_example_code() { + let table = crc32_compute_table(); + for i in 0..256{ + assert_eq!(table[i], table_fn(i as u32)); + } + } + + #[test] + fn simple_test() { + const BYTES: &[u8] = "The quick brown fox jumps over the lazy dog".as_bytes(); + assert_eq!(crc32(BYTES), 0x414fa339_u32); + assert_eq!(crc32(BYTES), crc32fast::hash(BYTES)); + } + + #[test] + fn check_random_inputs_against_crc32_fast() { + const N_ITER: usize = 100; + const BUFSIZE: usize = 4096; + + let mut buf = [0u8; BUFSIZE]; + let mut rng = thread_rng(); + + for _ in 0..N_ITER { + rng.fill(&mut buf[..]); + assert_eq!(crc32(&buf[..]), crc32fast::hash(&buf[..])); + } + } +}