77
88use clap:: { crate_version, Arg , ArgAction , ArgMatches , Command } ;
99use std:: ffi:: OsString ;
10- use std:: io:: { self , ErrorKind , Read , Seek , SeekFrom , Write } ;
10+ use std:: io:: { self , BufWriter , Read , Seek , SeekFrom , Write } ;
1111use std:: num:: TryFromIntError ;
1212use thiserror:: Error ;
1313use uucore:: display:: Quotable ;
@@ -239,10 +239,7 @@ impl HeadOptions {
239239 }
240240}
241241
242- fn read_n_bytes < R > ( input : R , n : u64 ) -> std:: io:: Result < ( ) >
243- where
244- R : Read ,
245- {
242+ fn read_n_bytes ( input : impl Read , n : u64 ) -> std:: io:: Result < ( ) > {
246243 // Read the first `n` bytes from the `input` reader.
247244 let mut reader = input. take ( n) ;
248245
@@ -287,48 +284,22 @@ fn catch_too_large_numbers_in_backwards_bytes_or_lines(n: u64) -> Option<usize>
287284 }
288285}
289286
290- /// Print to stdout all but the last `n` bytes from the given reader.
291- fn read_but_last_n_bytes ( input : & mut impl std:: io:: BufRead , n : u64 ) -> std:: io:: Result < ( ) > {
292- if n == 0 {
293- //prints everything
294- return read_n_bytes ( input, u64:: MAX ) ;
295- }
296-
287+ fn read_but_last_n_bytes ( input : impl std:: io:: BufRead , n : u64 ) -> std:: io:: Result < ( ) > {
297288 if let Some ( n) = catch_too_large_numbers_in_backwards_bytes_or_lines ( n) {
298289 let stdout = std:: io:: stdout ( ) ;
299- let mut stdout = stdout. lock ( ) ;
300-
301- let mut ring_buffer = Vec :: new ( ) ;
302-
303- let mut buffer = [ 0u8 ; BUF_SIZE ] ;
304- let mut total_read = 0 ;
305-
306- loop {
307- let read = match input. read ( & mut buffer) {
308- Ok ( 0 ) => break ,
309- Ok ( read) => read,
310- Err ( e) => match e. kind ( ) {
311- ErrorKind :: Interrupted => continue ,
312- _ => return Err ( e) ,
313- } ,
314- } ;
315-
316- total_read += read;
317-
318- if total_read <= n {
319- // Fill the ring buffer without exceeding n bytes
320- let overflow = n - total_read;
321- ring_buffer. extend_from_slice ( & buffer[ ..read - overflow] ) ;
322- } else {
323- // Write the ring buffer and the part of the buffer that exceeds n
324- stdout. write_all ( & ring_buffer) ?;
325- stdout. write_all ( & buffer[ ..read - n + ring_buffer. len ( ) ] ) ?;
326- ring_buffer. clear ( ) ;
327- ring_buffer. extend_from_slice ( & buffer[ read - n + ring_buffer. len ( ) ..read] ) ;
328- }
290+ let stdout = stdout. lock ( ) ;
291+ // Even though stdout is buffered, it will flush on each newline in the
292+ // input stream. This can be costly, so add an extra layer of buffering
293+ // over the top. This gives a significant speedup (approx 4x).
294+ let mut writer = BufWriter :: with_capacity ( BUF_SIZE , stdout) ;
295+ for byte in take_all_but ( input. bytes ( ) , n) {
296+ writer. write_all ( & [ byte?] ) ?;
329297 }
298+ // Make sure we finish writing everything to the target before
299+ // exiting. Otherwise, when Rust is implicitly flushing, any
300+ // error will be silently ignored.
301+ writer. flush ( ) ?;
330302 }
331-
332303 Ok ( ( ) )
333304}
334305
@@ -343,6 +314,10 @@ fn read_but_last_n_lines(
343314 for bytes in take_all_but ( lines ( input, separator) , n) {
344315 stdout. write_all ( & bytes?) ?;
345316 }
317+ // Make sure we finish writing everything to the target before
318+ // exiting. Otherwise, when Rust is implicitly flushing, any
319+ // error will be silently ignored.
320+ stdout. flush ( ) ?;
346321 }
347322 Ok ( ( ) )
348323}
@@ -440,8 +415,7 @@ fn head_backwards_without_seek_file(
440415 input : & mut std:: fs:: File ,
441416 options : & HeadOptions ,
442417) -> std:: io:: Result < ( ) > {
443- let reader = & mut std:: io:: BufReader :: with_capacity ( BUF_SIZE , & * input) ;
444-
418+ let reader = std:: io:: BufReader :: with_capacity ( BUF_SIZE , & * input) ;
445419 match options. mode {
446420 Mode :: AllButLastBytes ( n) => read_but_last_n_bytes ( reader, n) ?,
447421 Mode :: AllButLastLines ( n) => read_but_last_n_lines ( reader, n, options. line_ending . into ( ) ) ?,
0 commit comments