@@ -24,7 +24,7 @@ use thiserror::Error;
2424use uucore:: display:: Quotable ;
2525use uucore:: error:: UResult ;
2626use 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" ) ) ]
@@ -35,59 +35,45 @@ const ABOUT: &str = help_about!("cat.md");
3535
3636struct LineNumber {
3737 buf : Vec < u8 > ,
38+ print_start : usize ,
39+ num_start : usize ,
40+ num_end : usize ,
3841}
3942
4043// Logic to store a string for the line number. Manually incrementing the value
4144// represented in a buffer like this is significantly faster than storing
4245// a `usize` and using the standard Rust formatting macros to format a `usize`
4346// 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.
47+ // Buffer is initialized to " 1\t" and incremented each time `increment` is
48+ // called, using uucore's fast_inc function that operates on strings.
4749impl LineNumber {
4850 fn new ( ) -> Self {
51+ // 1024-digit long line number should be enough to run `cat` for the lifetime of the universe.
52+ let size = 1024 ;
53+ let mut buf = vec ! [ b'0' ; size] ;
54+
55+ let init_str = " 1\t " ;
56+ let print_start = buf. len ( ) - init_str. len ( ) ;
57+ let num_start = buf. len ( ) - 2 ;
58+ let num_end = buf. len ( ) - 1 ;
59+
60+ buf[ print_start..] . copy_from_slice ( init_str. as_bytes ( ) ) ;
61+
4962 LineNumber {
50- // Initialize buf to b" 1\t"
51- buf : Vec :: from ( b" 1\t " ) ,
63+ buf,
64+ print_start,
65+ num_start,
66+ num_end,
5267 }
5368 }
5469
5570 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- }
71+ self . num_start = fast_inc_one ( self . buf . as_mut_slice ( ) , self . num_start , self . num_end ) ;
72+ self . print_start = self . print_start . min ( self . num_start ) ;
8773 }
8874
8975 fn write ( & self , writer : & mut impl Write ) -> io:: Result < ( ) > {
90- writer. write_all ( & self . buf )
76+ writer. write_all ( & self . buf [ self . print_start .. ] )
9177 }
9278}
9379
@@ -804,21 +790,36 @@ mod tests {
804790 #[ test]
805791 fn test_incrementing_string ( ) {
806792 let mut incrementing_string = super :: LineNumber :: new ( ) ;
807- assert_eq ! ( b" 1\t " , incrementing_string. buf. as_slice( ) ) ;
793+ assert_eq ! (
794+ b" 1\t " ,
795+ & incrementing_string. buf[ incrementing_string. print_start..]
796+ ) ;
808797 incrementing_string. increment ( ) ;
809- assert_eq ! ( b" 2\t " , incrementing_string. buf. as_slice( ) ) ;
798+ assert_eq ! (
799+ b" 2\t " ,
800+ & incrementing_string. buf[ incrementing_string. print_start..]
801+ ) ;
810802 // Run through to 100
811803 for _ in 3 ..=100 {
812804 incrementing_string. increment ( ) ;
813805 }
814- assert_eq ! ( b" 100\t " , incrementing_string. buf. as_slice( ) ) ;
806+ assert_eq ! (
807+ b" 100\t " ,
808+ & incrementing_string. buf[ incrementing_string. print_start..]
809+ ) ;
815810 // Run through until we overflow the original size.
816811 for _ in 101 ..=1_000_000 {
817812 incrementing_string. increment ( ) ;
818813 }
819- // Confirm that the buffer expands when we overflow the original size.
820- assert_eq ! ( b"1000000\t " , incrementing_string. buf. as_slice( ) ) ;
814+ // Confirm that the start position moves when we overflow the original size.
815+ assert_eq ! (
816+ b"1000000\t " ,
817+ & incrementing_string. buf[ incrementing_string. print_start..]
818+ ) ;
821819 incrementing_string. increment ( ) ;
822- assert_eq ! ( b"1000001\t " , incrementing_string. buf. as_slice( ) ) ;
820+ assert_eq ! (
821+ b"1000001\t " ,
822+ & incrementing_string. buf[ incrementing_string. print_start..]
823+ ) ;
823824 }
824825}
0 commit comments