@@ -33,8 +33,68 @@ mod splice;
3333const USAGE : & str = help_usage ! ( "cat.md" ) ;
3434const ABOUT : & str = help_about ! ( "cat.md" ) ;
3535
36+ /// Fast code path increment function.
37+ ///
38+ /// Add inc to the string val[start..end]. This operates on ASCII digits, assuming
39+ /// val and inc are well formed.
40+ ///
41+ /// Returns the new value for start (can be less that the original value if we
42+ /// have a carry or if inc > start).
43+ ///
44+ /// We also assume that there is enough space in val to expand if start needs
45+ /// to be updated.
46+ #[ inline]
47+ fn fast_inc ( val : & mut [ u8 ] , start : usize , end : usize , inc : & [ u8 ] ) -> usize {
48+ // To avoid a lot of casts to signed integers, we make sure to decrement pos
49+ // as late as possible, so that it does not ever go negative.
50+ let mut pos = end;
51+ let mut carry = 0u8 ;
52+
53+ // First loop, add all digits of inc into val.
54+ for inc_pos in ( 0 ..inc. len ( ) ) . rev ( ) {
55+ pos -= 1 ;
56+
57+ let mut new_val = inc[ inc_pos] + carry;
58+ // Be careful here, only add existing digit of val.
59+ if pos >= start {
60+ new_val += val[ pos] - b'0' ;
61+ }
62+ if new_val > b'9' {
63+ carry = 1 ;
64+ new_val -= 10 ;
65+ } else {
66+ carry = 0 ;
67+ }
68+ val[ pos] = new_val;
69+ }
70+
71+ // Done, now, if we have a carry, add that to the upper digits of val.
72+ if carry == 0 {
73+ return start. min ( pos) ;
74+ }
75+ while pos > start {
76+ pos -= 1 ;
77+
78+ if val[ pos] == b'9' {
79+ // 9+1 = 10. Carry propagating, keep going.
80+ val[ pos] = b'0' ;
81+ } else {
82+ // Carry stopped propagating, return unchanged start.
83+ val[ pos] += 1 ;
84+ return start;
85+ }
86+ }
87+
88+ // The carry propagated so far that a new digit was added.
89+ val[ start - 1 ] = b'1' ;
90+ start - 1
91+ }
92+
3693struct LineNumber {
3794 buf : Vec < u8 > ,
95+ print_start : usize ,
96+ start : usize ,
97+ num_end : usize ,
3898}
3999
40100// Logic to store a string for the line number. Manually incrementing the value
@@ -46,48 +106,31 @@ struct LineNumber {
46106// prepended and the counting continues.
47107impl LineNumber {
48108 fn new ( ) -> Self {
109+ let size = 1024 ;
110+ let mut buf = vec ! [ b'0' ; size] ;
111+
112+ let init_str = " 1\t " ;
113+ let print_start = buf. len ( ) - init_str. len ( ) ;
114+ let start = buf. len ( ) - 2 ;
115+ let num_end = buf. len ( ) - 1 ;
116+
117+ buf[ print_start..] . copy_from_slice ( init_str. as_bytes ( ) ) ;
118+
49119 LineNumber {
50- // Initialize buf to b" 1\t"
51- buf : Vec :: from ( b" 1\t " ) ,
120+ buf,
121+ print_start,
122+ start,
123+ num_end,
52124 }
53125 }
54126
55127 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- }
128+ self . start = fast_inc ( self . buf . as_mut_slice ( ) , self . start , self . num_end , & [ b'1' ] ) ;
129+ self . print_start = self . print_start . min ( self . start ) ;
87130 }
88131
89132 fn write ( & self , writer : & mut impl Write ) -> std:: io:: Result < ( ) > {
90- writer. write_all ( & self . buf )
133+ writer. write_all ( & self . buf [ self . print_start .. ] )
91134 }
92135}
93136
0 commit comments