Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ jobs:
components: rustfmt, clippy
cache-key: ${{ matrix.os }}-${{ matrix.rust-toolchain }}
- name: Check
run: cargo check
run: cargo check --all-features
- name: Architecture check
run: cargo run --bin arch-check
run: cargo run --features cli --bin arch-check
- if: ${{ matrix.rust-toolchain != 'nightly' }}
name: Format
run: cargo fmt -- --check
- if: ${{ matrix.rust-toolchain != 'nightly' }}
name: Clippy
run: cargo clippy
run: cargo clippy --all-features
- name: Test
run: cargo test
run: cargo test --features cli

test-x86_64:
name: Test x86_64
Expand All @@ -56,17 +56,17 @@ jobs:
components: rustfmt, clippy
cache-key: ${{ matrix.os }}-${{ matrix.rust-toolchain }}
- name: Check
run: cargo check
run: cargo check --all-features
- name: Architecture check
run: cargo run --bin arch-check
run: cargo run --features cli --bin arch-check
- if: ${{ matrix.rust-toolchain != 'nightly' }}
name: Format
run: cargo fmt -- --check
- if: ${{ matrix.rust-toolchain != 'nightly' }}
name: Clippy
run: cargo clippy
run: cargo clippy --all-features
- name: Test
run: cargo test
run: cargo test --features cli

test-x86:
name: Test x86
Expand All @@ -88,11 +88,11 @@ jobs:
- name: Set up cross
run: cargo install cross --locked --version 0.2.5
- name: Check
run: cross check --target ${{ matrix.target }}
run: cross check --all-features --target ${{ matrix.target }}
- name: Architecture check
run: cross run --bin arch-check --target ${{ matrix.target }}
run: cross run --features cli --bin arch-check --target ${{ matrix.target }}
- name: Test
run: cross test --target ${{ matrix.target }}
run: cross test --features cli --target ${{ matrix.target }}

test-software:
name: Test software fallback
Expand All @@ -114,8 +114,8 @@ jobs:
- name: Set up cross
run: cargo install cross --locked --version 0.2.5
- name: Check
run: cross check --target ${{ matrix.target }}
run: cross check --all-features --target ${{ matrix.target }}
- name: Architecture check
run: cross run --bin arch-check --target ${{ matrix.target }}
run: cross run --features cli --bin arch-check --target ${{ matrix.target }}
- name: Test
run: cross test --target ${{ matrix.target }}
run: cross test --features cli --target ${{ matrix.target }}
33 changes: 30 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ bench = true
[dependencies]
crc = "3"
digest = { version = "0.10", features = ["alloc"] }
rand = "0.9"
regex = "1.12"

# will be removed once Rust 1.89 is the minimum supported version
rustversion = "1.0"
Expand All @@ -36,6 +34,8 @@ indexmap = { version = ">=2.11.0, <2.12.0", optional = true }
[dev-dependencies]
criterion = "0.7"
cbindgen = "0.29"
rand = "0.9"
regex = "1.12"

# lto=true has a big improvement in performance
[profile.release]
Expand All @@ -44,11 +44,29 @@ strip = true
codegen-units = 1
opt-level = 3

[[bin]]
name = "checksum"
path = "src/bin/checksum.rs"
required-features = ["cli"]

[[bin]]
name = "arch-check"
path = "src/bin/arch-check.rs"
required-features = ["cli"]

[[bin]]
name = "get-custom-params"
path = "src/bin/get-custom-params.rs"
required-features = ["cli"]

[[bench]]
name = "benchmark"
harness = false

[features]
default = ["std"]
std = []
cli = ["std"]
alloc = []

# the features below are deprecated, aren't in use, and will be removed in the next MAJOR version (v2)
Expand All @@ -60,4 +78,13 @@ optimize_crc32_neon_v3s4x2e_v2 = [] # deprecated
optimize_crc32_neon_blended = [] # deprecated
optimize_crc32_avx512_vpclmulqdq_v3x2 = [] # deprecated
optimize_crc32_avx512_v4s3x3 = [] # deprecated
optimize_crc32_sse_v4s3x3 = [] # deprecated
optimize_crc32_sse_v4s3x3 = [] # deprecated

[package.metadata.docs.rs]
features = ["std"]
rustdoc-args = ["--cfg", "docsrs"]

[[test]]
name = "checksum_integration_tests"
path = "tests/checksum_integration_tests.rs"
required-features = ["cli"]
24 changes: 21 additions & 3 deletions src/bin/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
//! This is a simple program to calculate a checksum from the command line

use crc_fast::{checksum, checksum_file, CrcAlgorithm};
use rand::RngCore;
use std::env;
use std::process::ExitCode;
use std::str::FromStr;
Expand Down Expand Up @@ -151,6 +150,22 @@ impl BenchmarkResult {
}
}

/// Fills a buffer with pseudo-random data using xorshift64* algorithm.
/// This is a deterministic PRNG that's tiny, fast, and suitable for benchmark data generation.
/// The seed is derived from the buffer size to provide some variability.
/// This solves the problem of having to use a full-featured PRNG like rand for benchmark data generation.
#[inline]
fn fill_pseudo_random(buf: &mut [u8], seed: u64) {
let mut x = seed;
for b in buf {
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
let r = x.wrapping_mul(0x2545_F491_4F6C_DD1D);
*b = r as u8;
}
}

fn generate_random_data(size: usize) -> Result<Vec<u8>, String> {
// Check for reasonable size limits to prevent memory issues
if size > 1_073_741_824 {
Expand All @@ -160,8 +175,11 @@ fn generate_random_data(size: usize) -> Result<Vec<u8>, String> {

// Use vec! macro to avoid clippy warning about slow initialization
let mut buf = vec![0u8; size];
let mut rng = rand::rng();
rng.fill_bytes(&mut buf);

// Derive seed from size for deterministic but varied data
let seed = 0x9E37_79B9_7F4A_7C15_u64.wrapping_add(size as u64);
fill_pseudo_random(&mut buf, seed);

Ok(buf)
}

Expand Down
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
// Copyright 2025 Don MacAskill. Licensed under MIT or Apache-2.0.
// Future proofing for no_std support
#![cfg_attr(not(feature = "std"), no_std)]

//! `crc-fast`
//! ===========
Expand Down Expand Up @@ -144,7 +146,10 @@ use crate::crc64::consts::{
use crate::structs::Calculator;
use crate::traits::CrcCalculator;
use digest::{DynDigest, InvalidBufferSize};

#[cfg(feature = "std")]
use std::fs::File;
#[cfg(feature = "std")]
use std::io::{Read, Write};

mod algorithm;
Expand Down Expand Up @@ -545,6 +550,7 @@ impl Digest {
}
}

#[cfg(feature = "std")]
impl Write for Digest {
#[inline(always)]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Expand Down Expand Up @@ -643,6 +649,7 @@ pub fn checksum_with_params(params: CrcParams, buf: &[u8]) -> u64 {
///
/// assert_eq!(checksum.unwrap(), 0xcbf43926);
/// ```
#[cfg(feature = "std")]
#[inline(always)]
pub fn checksum_file(
algorithm: CrcAlgorithm,
Expand Down Expand Up @@ -685,6 +692,7 @@ pub fn checksum_file(
///
/// assert_eq!(checksum.unwrap(), 0xcbf43926);
/// ```
#[cfg(feature = "std")]
pub fn checksum_file_with_params(
params: CrcParams,
path: &str,
Expand All @@ -698,6 +706,7 @@ pub fn checksum_file_with_params(
/// # Errors
///
/// This function will return an error if the file cannot be read.
#[cfg(feature = "std")]
fn checksum_file_with_digest(
mut digest: Digest,
path: &str,
Expand Down
32 changes: 31 additions & 1 deletion tests/checksum_integration_tests.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
// Copyright 2025 Don MacAskill. Licensed under MIT or Apache-2.0.

#![cfg(feature = "cli")]

use std::fs;
use std::process::Command;

#[test]
fn test_benchmark_flag_parsing() {
let output = Command::new("cargo")
.args(&["run", "--bin", "checksum", "--", "-a", "CRC-32/ISCSI", "-b"])
.args(&[
"run",
"--features",
"cli",
"--bin",
"checksum",
"--",
"-a",
"CRC-32/ISCSI",
"-b",
])
.output()
.expect("Failed to execute command");

Expand All @@ -25,6 +37,8 @@ fn test_benchmark_with_size_parameter() {
let output = Command::new("cargo")
.args(&[
"run",
"--features",
"cli",
"--bin",
"checksum",
"--",
Expand All @@ -47,6 +61,8 @@ fn test_benchmark_with_duration_parameter() {
let output = Command::new("cargo")
.args(&[
"run",
"--features",
"cli",
"--bin",
"checksum",
"--",
Expand All @@ -69,6 +85,8 @@ fn test_benchmark_invalid_size() {
let output = Command::new("cargo")
.args(&[
"run",
"--features",
"cli",
"--bin",
"checksum",
"--",
Expand All @@ -91,6 +109,8 @@ fn test_benchmark_invalid_duration() {
let output = Command::new("cargo")
.args(&[
"run",
"--features",
"cli",
"--bin",
"checksum",
"--",
Expand All @@ -117,6 +137,8 @@ fn test_benchmark_with_file_input() {
let output = Command::new("cargo")
.args(&[
"run",
"--features",
"cli",
"--bin",
"checksum",
"--",
Expand Down Expand Up @@ -144,6 +166,8 @@ fn test_benchmark_with_string_input() {
let output = Command::new("cargo")
.args(&[
"run",
"--features",
"cli",
"--bin",
"checksum",
"--",
Expand Down Expand Up @@ -171,6 +195,8 @@ fn test_benchmark_different_algorithms() {
let output = Command::new("cargo")
.args(&[
"run",
"--features",
"cli",
"--bin",
"checksum",
"--",
Expand Down Expand Up @@ -198,6 +224,8 @@ fn test_benchmark_size_without_benchmark_flag() {
let output = Command::new("cargo")
.args(&[
"run",
"--features",
"cli",
"--bin",
"checksum",
"--",
Expand All @@ -219,6 +247,8 @@ fn test_benchmark_nonexistent_file() {
let output = Command::new("cargo")
.args(&[
"run",
"--features",
"cli",
"--bin",
"checksum",
"--",
Expand Down
Loading