diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 9051f70ab4b..7485555e0cb 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -24,6 +24,7 @@ jobs: matrix: benchmark-target: - { package: uu_base64 } + - { package: uu_cksum } - { package: uu_cp } - { package: uu_cut } - { package: uu_du } diff --git a/Cargo.lock b/Cargo.lock index b23860c1899..87966afbb2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3259,8 +3259,10 @@ name = "uu_cksum" version = "0.3.0" dependencies = [ "clap", + "codspeed-divan-compat", "fluent", "hex", + "tempfile", "uucore", ] diff --git a/src/uu/cksum/Cargo.toml b/src/uu/cksum/Cargo.toml index 0eb4d28541d..01ca5cb16b5 100644 --- a/src/uu/cksum/Cargo.toml +++ b/src/uu/cksum/Cargo.toml @@ -23,6 +23,15 @@ uucore = { workspace = true, features = ["checksum", "encoding", "sum"] } hex = { workspace = true } fluent = { workspace = true } +[dev-dependencies] +divan = { workspace = true } +tempfile = { workspace = true } +uucore = { workspace = true, features = ["benchmark"] } + [[bin]] name = "cksum" path = "src/main.rs" + +[[bench]] +name = "cksum_bench" +harness = false diff --git a/src/uu/cksum/benches/cksum_bench.rs b/src/uu/cksum/benches/cksum_bench.rs new file mode 100644 index 00000000000..9c38042a91c --- /dev/null +++ b/src/uu/cksum/benches/cksum_bench.rs @@ -0,0 +1,146 @@ +// This file is part of the uutils coreutils package. +// +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use divan::{Bencher, black_box}; +use uu_cksum::uumain; +use uucore::benchmark::{run_util_function, setup_test_file, text_data}; + +// Macro to generate benchmarks for each algorithm +macro_rules! bench_algorithm { + ($algo_name:ident, $algo_str:expr) => { + #[divan::bench] + fn $algo_name(bencher: Bencher) { + let data = text_data::generate_by_size(100, 80); + let file_path = setup_test_file(&data); + + bencher.bench(|| { + black_box(run_util_function( + uumain, + &["--algorithm", $algo_str, file_path.to_str().unwrap()], + )); + }); + } + }; + ($algo_name:ident, $algo_str:expr, length) => { + #[divan::bench] + fn $algo_name(bencher: Bencher) { + let data = text_data::generate_by_size(100, 80); + let file_path = setup_test_file(&data); + + bencher.bench(|| { + black_box(run_util_function( + uumain, + &[ + "--algorithm", + $algo_str, + "--length", + "1048576", + file_path.to_str().unwrap(), + ], + )); + }); + } + }; +} + +// Special macro for SHAKE algorithms that require length parameter +// Since SHAKE algorithms have fundamental --length parameter conflicts in cksum, +// we implement them using direct digest calculation for meaningful benchmarks +macro_rules! bench_shake_algorithm { + ($algo_name:ident, $algo_str:expr, $shake_type:ty) => { + #[divan::bench] + fn $algo_name(bencher: Bencher) { + use uucore::sum::{Digest, Shake128, Shake256}; + + let data = text_data::generate_by_size(100, 80); + + bencher.bench(|| { + let mut shake = <$shake_type>::new(); + shake.hash_update(&data); + + // SHAKE algorithms can output any length, use 256 bits (32 bytes) for meaningful comparison + let mut output = [0u8; 32]; + shake.hash_finalize(&mut output); + + black_box(output); + }); + } + }; +} + +// Generate benchmarks for all supported algorithms +bench_algorithm!(cksum_sysv, "sysv"); +bench_algorithm!(cksum_bsd, "bsd"); +bench_algorithm!(cksum_crc, "crc"); +bench_algorithm!(cksum_crc32b, "crc32b"); +bench_algorithm!(cksum_md5, "md5"); +bench_algorithm!(cksum_sha1, "sha1"); +bench_algorithm!(cksum_sha2, "sha2", length); +bench_algorithm!(cksum_sha3, "sha3", length); +bench_algorithm!(cksum_blake2b, "blake2b"); +bench_algorithm!(cksum_sm3, "sm3"); +bench_algorithm!(cksum_sha224, "sha224"); +bench_algorithm!(cksum_sha256, "sha256"); +bench_algorithm!(cksum_sha384, "sha384"); +bench_algorithm!(cksum_sha512, "sha512"); +bench_algorithm!(cksum_blake3, "blake3"); +bench_shake_algorithm!(cksum_shake128, "shake128", Shake128); +bench_shake_algorithm!(cksum_shake256, "shake256", Shake256); + +/// Benchmark cksum with default CRC algorithm +#[divan::bench] +fn cksum_default(bencher: Bencher) { + let data = text_data::generate_by_size(100, 80); + let file_path = setup_test_file(&data); + + bencher.bench(|| { + black_box(run_util_function(uumain, &[file_path.to_str().unwrap()])); + }); +} + +/// Benchmark cksum with raw output format +#[divan::bench] +fn cksum_raw_output(bencher: Bencher) { + let data = text_data::generate_by_size(100, 80); + let file_path = setup_test_file(&data); + + bencher.bench(|| { + black_box(run_util_function( + uumain, + &["--raw", file_path.to_str().unwrap()], + )); + }); +} + +/// Benchmark cksum processing multiple files +#[divan::bench] +fn cksum_multiple_files(bencher: Bencher) { + bencher + .with_inputs(|| { + let data1 = text_data::generate_by_size(50, 80); + let data2 = text_data::generate_by_size(50, 80); + let data3 = text_data::generate_by_size(50, 80); + + let file1 = setup_test_file(&data1); + let file2 = setup_test_file(&data2); + let file3 = setup_test_file(&data3); + + (file1, file2, file3) + }) + .bench_values(|(file1, file2, file3)| { + black_box(run_util_function( + uumain, + &[ + file1.to_str().unwrap(), + file2.to_str().unwrap(), + file3.to_str().unwrap(), + ], + )); + }); +} + +fn main() { + divan::main(); +} diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index 37b9165feb3..972163c1d45 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -15,9 +15,9 @@ use std::path::Path; use uucore::checksum::{ ALGORITHM_OPTIONS_BLAKE2B, ALGORITHM_OPTIONS_BSD, ALGORITHM_OPTIONS_CRC, ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SHA2, ALGORITHM_OPTIONS_SHA3, - ALGORITHM_OPTIONS_SYSV, ChecksumError, ChecksumOptions, ChecksumVerbose, HashAlgorithm, - LEGACY_ALGORITHMS, SUPPORTED_ALGORITHMS, calculate_blake2b_length, detect_algo, digest_reader, - perform_checksum_validation, + ALGORITHM_OPTIONS_SHAKE128, ALGORITHM_OPTIONS_SHAKE256, ALGORITHM_OPTIONS_SYSV, ChecksumError, + ChecksumOptions, ChecksumVerbose, HashAlgorithm, LEGACY_ALGORITHMS, SUPPORTED_ALGORITHMS, + calculate_blake2b_length, detect_algo, digest_reader, perform_checksum_validation, }; use uucore::translate; @@ -387,9 +387,20 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { // Length for sha2 and sha3 should be saved, it will be validated // afterwards if necessary. (Some(len), ALGORITHM_OPTIONS_SHA2 | ALGORITHM_OPTIONS_SHA3) => Some(*len), - (None | Some(0), _) => None, // Length for Blake2b if saved only if it's not zero. (Some(len), ALGORITHM_OPTIONS_BLAKE2B) => calculate_blake2b_length(*len)?, + // Length for SHAKE algorithms is required and must be > 0 + (Some(len), ALGORITHM_OPTIONS_SHAKE128 | ALGORITHM_OPTIONS_SHAKE256) => { + if *len == 0 { + return Err(ChecksumError::LengthRequiredForShake.into()); + } + Some(*len) + } + // SHAKE algorithms require --length parameter + (None, ALGORITHM_OPTIONS_SHAKE128 | ALGORITHM_OPTIONS_SHAKE256) => { + return Err(ChecksumError::LengthRequiredForShake.into()); + } + (None | Some(0), _) => None, // a --length flag set with any other algorithm is an error. _ => { return Err(ChecksumError::LengthOnlyForBlake2bSha2Sha3.into()); diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 30eccc254ca..09f8d708c11 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -228,6 +228,8 @@ pub enum ChecksumError { InvalidLengthFor(String), #[error("--length is only supported with --algorithm blake2b, sha2, or sha3")] LengthOnlyForBlake2bSha2Sha3, + #[error("--length is required for SHAKE algorithms")] + LengthRequiredForShake, #[error("the --binary and --text options are meaningless when verifying checksums")] BinaryTextConflict, #[error("--text mode is only supported with --untagged")]