diff --git a/Cargo.lock b/Cargo.lock index b4ccd2cc8..911e4cc60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4112,6 +4112,7 @@ dependencies = [ "divan", "fluent", "libc", + "memchr", "nix", "tempfile", "thiserror 2.0.16", diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 1f0c111c6..b7a3ddb72 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -1631,6 +1631,7 @@ dependencies = [ "clap", "fluent", "libc", + "memchr", "nix", "thiserror", "unicode-width", @@ -1710,7 +1711,6 @@ version = "0.2.2" dependencies = [ "proc-macro2", "quote", - "uuhelp_parser", ] [[package]] @@ -1725,10 +1725,6 @@ dependencies = [ "uucore", ] -[[package]] -name = "uuhelp_parser" -version = "0.2.2" - [[package]] name = "version_check" version = "0.9.5" diff --git a/src/uu/wc/Cargo.toml b/src/uu/wc/Cargo.toml index aff31fac3..26fcb3375 100644 --- a/src/uu/wc/Cargo.toml +++ b/src/uu/wc/Cargo.toml @@ -21,6 +21,7 @@ path = "src/wc.rs" clap = { workspace = true } uucore = { workspace = true, features = ["parser", "pipes", "quoting-style"] } bytecount = { workspace = true, features = ["runtime-dispatch-simd"] } +memchr = { workspace = true } thiserror = { workspace = true } unicode-width = { workspace = true } fluent = { workspace = true } diff --git a/src/uu/wc/src/count_fast.rs b/src/uu/wc/src/count_fast.rs index 9a473401e..8ab931b02 100644 --- a/src/uu/wc/src/count_fast.rs +++ b/src/uu/wc/src/count_fast.rs @@ -229,6 +229,63 @@ pub(crate) fn count_bytes_chars_and_lines_fast< const COUNT_LINES: bool, >( handle: &mut R, +) -> (WordCount, Option) { + // Use specialized implementations for common cases + match (COUNT_BYTES, COUNT_CHARS, COUNT_LINES) { + // Lines only - use memchr for fastest line counting + (false, false, true) => count_lines_only_fast(handle), + // Bytes + Lines - optimize using bytecount and avoid double counting + (true, false, true) => count_bytes_and_lines_fast(handle), + // Default implementation for other cases + _ => { + count_bytes_chars_and_lines_generic::(handle) + } + } +} + +/// Specialized fast line counting using memchr +fn count_lines_only_fast(handle: &mut R) -> (WordCount, Option) { + let mut total = WordCount::default(); + let buf: &mut [u8] = &mut AlignedBuffer::default().data; + + loop { + match handle.read(buf) { + Ok(0) => return (total, None), + Ok(n) => { + total.lines += memchr::memchr_iter(b'\n', &buf[..n]).count(); + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => (), + Err(e) => return (total, Some(e)), + } + } +} + +/// Specialized fast byte and line counting using bytecount +fn count_bytes_and_lines_fast(handle: &mut R) -> (WordCount, Option) { + let mut total = WordCount::default(); + let buf: &mut [u8] = &mut AlignedBuffer::default().data; + + loop { + match handle.read(buf) { + Ok(0) => return (total, None), + Ok(n) => { + total.bytes += n; + total.lines += bytecount::count(&buf[..n], b'\n'); + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => (), + Err(e) => return (total, Some(e)), + } + } +} + +/// Generic implementation for mixed counting +fn count_bytes_chars_and_lines_generic< + R: Read, + const COUNT_BYTES: bool, + const COUNT_CHARS: bool, + const COUNT_LINES: bool, +>( + handle: &mut R, ) -> (WordCount, Option) { let mut total = WordCount::default(); let buf: &mut [u8] = &mut AlignedBuffer::default().data;