Skip to content

Commit 42c55a4

Browse files
authored
Merge branch 'main' into fix/id-human-readable-output
2 parents fe75a6d + a423041 commit 42c55a4

File tree

21 files changed

+807
-362
lines changed

21 files changed

+807
-362
lines changed

.github/workflows/ignore-intermittent.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ tests/timeout/timeout
33
tests/rm/rm1
44
tests/misc/stdbuf
55
tests/misc/usage_vs_getopt
6+
tests/misc/tee

Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/uu/cat/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ path = "src/cat.rs"
2121
clap = { workspace = true }
2222
memchr = { workspace = true }
2323
thiserror = { workspace = true }
24-
uucore = { workspace = true, features = ["fs", "pipes"] }
24+
uucore = { workspace = true, features = ["fast-inc", "fs", "pipes"] }
2525

2626
[target.'cfg(unix)'.dependencies]
2727
nix = { workspace = true }

src/uu/cat/src/cat.rs

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use thiserror::Error;
2424
use uucore::display::Quotable;
2525
use uucore::error::UResult;
2626
use uucore::fs::FileInformation;
27-
use uucore::{format_usage, help_about, help_usage};
27+
use uucore::{fast_inc::fast_inc_one, format_usage, help_about, help_usage};
2828

2929
/// Linux splice support
3030
#[cfg(any(target_os = "linux", target_os = "android"))]
@@ -33,61 +33,55 @@ mod splice;
3333
const USAGE: &str = help_usage!("cat.md");
3434
const ABOUT: &str = help_about!("cat.md");
3535

36+
// Allocate 32 digits for the line number.
37+
// An estimate is that we can print about 1e8 lines/seconds, so 32 digits
38+
// would be enough for billions of universe lifetimes.
39+
const LINE_NUMBER_BUF_SIZE: usize = 32;
40+
3641
struct LineNumber {
37-
buf: Vec<u8>,
42+
buf: [u8; LINE_NUMBER_BUF_SIZE],
43+
print_start: usize,
44+
num_start: usize,
45+
num_end: usize,
3846
}
3947

4048
// Logic to store a string for the line number. Manually incrementing the value
4149
// represented in a buffer like this is significantly faster than storing
4250
// a `usize` and using the standard Rust formatting macros to format a `usize`
4351
// to a string each time it's needed.
44-
// String is initialized to " 1\t" and incremented each time `increment` is
45-
// called. When the value overflows the range storable in the buffer, a b'1' is
46-
// prepended and the counting continues.
52+
// Buffer is initialized to " 1\t" and incremented each time `increment` is
53+
// called, using uucore's fast_inc function that operates on strings.
4754
impl LineNumber {
4855
fn new() -> Self {
56+
let mut buf = [b'0'; LINE_NUMBER_BUF_SIZE];
57+
58+
let init_str = " 1\t";
59+
let print_start = buf.len() - init_str.len();
60+
let num_start = buf.len() - 2;
61+
let num_end = buf.len() - 1;
62+
63+
buf[print_start..].copy_from_slice(init_str.as_bytes());
64+
4965
LineNumber {
50-
// Initialize buf to b" 1\t"
51-
buf: Vec::from(b" 1\t"),
66+
buf,
67+
print_start,
68+
num_start,
69+
num_end,
5270
}
5371
}
5472

5573
fn increment(&mut self) {
56-
// skip(1) to avoid the \t in the last byte.
57-
for ascii_digit in self.buf.iter_mut().rev().skip(1) {
58-
// Working from the least-significant digit, increment the number in the buffer.
59-
// If we hit anything other than a b'9' we can break since the next digit is
60-
// unaffected.
61-
// Also note that if we hit a b' ', we can think of that as a 0 and increment to b'1'.
62-
// If/else here is faster than match (as measured with some benchmarking Apr-2025),
63-
// probably since we can prioritize most likely digits first.
64-
if (b'0'..=b'8').contains(ascii_digit) {
65-
*ascii_digit += 1;
66-
break;
67-
} else if b'9' == *ascii_digit {
68-
*ascii_digit = b'0';
69-
} else {
70-
assert_eq!(*ascii_digit, b' ');
71-
*ascii_digit = b'1';
72-
break;
73-
}
74-
}
75-
if self.buf[0] == b'0' {
76-
// This implies we've overflowed. In this case the buffer will be
77-
// [b'0', b'0', ..., b'0', b'\t'].
78-
// For debugging, the following logic would assert that to be the case.
79-
// assert_eq!(*self.buf.last().unwrap(), b'\t');
80-
// for ascii_digit in self.buf.iter_mut().rev().skip(1) {
81-
// assert_eq!(*ascii_digit, b'0');
82-
// }
83-
84-
// All we need to do is prepend a b'1' and we're good.
85-
self.buf.insert(0, b'1');
86-
}
74+
fast_inc_one(&mut self.buf, &mut self.num_start, self.num_end);
75+
self.print_start = self.print_start.min(self.num_start);
76+
}
77+
78+
#[inline]
79+
fn to_str(&self) -> &[u8] {
80+
&self.buf[self.print_start..]
8781
}
8882

8983
fn write(&self, writer: &mut impl Write) -> io::Result<()> {
90-
writer.write_all(&self.buf)
84+
writer.write_all(self.to_str())
9185
}
9286
}
9387

@@ -804,21 +798,21 @@ mod tests {
804798
#[test]
805799
fn test_incrementing_string() {
806800
let mut incrementing_string = super::LineNumber::new();
807-
assert_eq!(b" 1\t", incrementing_string.buf.as_slice());
801+
assert_eq!(b" 1\t", incrementing_string.to_str());
808802
incrementing_string.increment();
809-
assert_eq!(b" 2\t", incrementing_string.buf.as_slice());
803+
assert_eq!(b" 2\t", incrementing_string.to_str());
810804
// Run through to 100
811805
for _ in 3..=100 {
812806
incrementing_string.increment();
813807
}
814-
assert_eq!(b" 100\t", incrementing_string.buf.as_slice());
808+
assert_eq!(b" 100\t", incrementing_string.to_str());
815809
// Run through until we overflow the original size.
816810
for _ in 101..=1_000_000 {
817811
incrementing_string.increment();
818812
}
819-
// Confirm that the buffer expands when we overflow the original size.
820-
assert_eq!(b"1000000\t", incrementing_string.buf.as_slice());
813+
// Confirm that the start position moves when we overflow the original size.
814+
assert_eq!(b"1000000\t", incrementing_string.to_str());
821815
incrementing_string.increment();
822-
assert_eq!(b"1000001\t", incrementing_string.buf.as_slice());
816+
assert_eq!(b"1000001\t", incrementing_string.to_str());
823817
}
824818
}

0 commit comments

Comments
 (0)