@@ -9,15 +9,14 @@ use clap::{crate_version, Arg, ArgAction, ArgMatches, Command};
99use std:: ffi:: OsString ;
1010#[ cfg( unix) ]
1111use std:: fs:: File ;
12- use std:: io:: { self , BufWriter , Read , Seek , SeekFrom , Write } ;
12+ use std:: io:: { self , Read , Seek , SeekFrom , Write } ;
1313use std:: num:: TryFromIntError ;
1414#[ cfg( unix) ]
1515use std:: os:: fd:: { AsRawFd , FromRawFd } ;
1616use thiserror:: Error ;
1717use uucore:: display:: Quotable ;
1818use uucore:: error:: { FromIo , UError , UResult } ;
1919use uucore:: line_ending:: LineEnding ;
20- use uucore:: lines:: lines;
2120use uucore:: { format_usage, help_about, help_usage, show} ;
2221
2322const BUF_SIZE : usize = 65536 ;
@@ -37,7 +36,8 @@ mod options {
3736
3837mod parse;
3938mod take;
40- use take:: take_all_but;
39+ use take:: copy_all_but_bytes;
40+ use take:: copy_all_but_lines;
4141use take:: take_lines;
4242
4343#[ derive( Error , Debug ) ]
@@ -274,7 +274,8 @@ fn read_n_lines(input: &mut impl std::io::BufRead, n: u64, separator: u8) -> std
274274 let mut reader = take_lines ( input, n, separator) ;
275275
276276 // Write those bytes to `stdout`.
277- let mut stdout = std:: io:: stdout ( ) ;
277+ let stdout = std:: io:: stdout ( ) ;
278+ let mut stdout = stdout. lock ( ) ;
278279
279280 let bytes_written = io:: copy ( & mut reader, & mut stdout) . map_err ( wrap_in_stdout_error) ?;
280281
@@ -296,43 +297,37 @@ fn catch_too_large_numbers_in_backwards_bytes_or_lines(n: u64) -> Option<usize>
296297 }
297298}
298299
299- fn read_but_last_n_bytes ( input : impl std :: io :: BufRead , n : u64 ) -> std:: io:: Result < u64 > {
300- let mut bytes_written = 0 ;
300+ fn read_but_last_n_bytes ( mut input : impl Read , n : u64 ) -> std:: io:: Result < u64 > {
301+ let mut bytes_written: u64 = 0 ;
301302 if let Some ( n) = catch_too_large_numbers_in_backwards_bytes_or_lines ( n) {
302303 let stdout = std:: io:: stdout ( ) ;
303- let stdout = stdout. lock ( ) ;
304- // Even though stdout is buffered, it will flush on each newline in the
305- // input stream. This can be costly, so add an extra layer of buffering
306- // over the top. This gives a significant speedup (approx 4x).
307- let mut writer = BufWriter :: with_capacity ( BUF_SIZE , stdout) ;
308- for byte in take_all_but ( input. bytes ( ) , n) {
309- writer. write_all ( & [ byte?] ) . map_err ( wrap_in_stdout_error) ?;
310- bytes_written += 1 ;
311- }
304+ let mut stdout = stdout. lock ( ) ;
305+
306+ bytes_written = copy_all_but_bytes ( & mut input, & mut stdout, n)
307+ . map_err ( wrap_in_stdout_error) ?
308+ . try_into ( )
309+ . unwrap ( ) ;
310+
312311 // Make sure we finish writing everything to the target before
313312 // exiting. Otherwise, when Rust is implicitly flushing, any
314313 // error will be silently ignored.
315- writer . flush ( ) . map_err ( wrap_in_stdout_error) ?;
314+ stdout . flush ( ) . map_err ( wrap_in_stdout_error) ?;
316315 }
317316 Ok ( bytes_written)
318317}
319318
320- fn read_but_last_n_lines (
321- input : impl std:: io:: BufRead ,
322- n : u64 ,
323- separator : u8 ,
324- ) -> std:: io:: Result < u64 > {
319+ fn read_but_last_n_lines ( mut input : impl Read , n : u64 , separator : u8 ) -> std:: io:: Result < u64 > {
320+ let stdout = std:: io:: stdout ( ) ;
321+ let mut stdout = stdout. lock ( ) ;
322+ if n == 0 {
323+ return io:: copy ( & mut input, & mut stdout) . map_err ( wrap_in_stdout_error) ;
324+ }
325325 let mut bytes_written: u64 = 0 ;
326326 if let Some ( n) = catch_too_large_numbers_in_backwards_bytes_or_lines ( n) {
327- let stdout = std:: io:: stdout ( ) ;
328- let mut stdout = stdout. lock ( ) ;
329-
330- for bytes in take_all_but ( lines ( input, separator) , n) {
331- let bytes = bytes?;
332- bytes_written += u64:: try_from ( bytes. len ( ) ) . unwrap ( ) ;
333-
334- stdout. write_all ( & bytes) . map_err ( wrap_in_stdout_error) ?;
335- }
327+ bytes_written = copy_all_but_lines ( input, & mut stdout, n, separator)
328+ . map_err ( wrap_in_stdout_error) ?
329+ . try_into ( )
330+ . unwrap ( ) ;
336331 // Make sure we finish writing everything to the target before
337332 // exiting. Otherwise, when Rust is implicitly flushing, any
338333 // error will be silently ignored.
@@ -434,10 +429,9 @@ fn head_backwards_without_seek_file(
434429 input : & mut std:: fs:: File ,
435430 options : & HeadOptions ,
436431) -> std:: io:: Result < u64 > {
437- let reader = std:: io:: BufReader :: with_capacity ( BUF_SIZE , & * input) ;
438432 match options. mode {
439- Mode :: AllButLastBytes ( n) => read_but_last_n_bytes ( reader , n) ,
440- Mode :: AllButLastLines ( n) => read_but_last_n_lines ( reader , n, options. line_ending . into ( ) ) ,
433+ Mode :: AllButLastBytes ( n) => read_but_last_n_bytes ( input , n) ,
434+ Mode :: AllButLastLines ( n) => read_but_last_n_lines ( input , n, options. line_ending . into ( ) ) ,
441435 _ => unreachable ! ( ) ,
442436 }
443437}
@@ -452,28 +446,20 @@ fn head_backwards_on_seekable_file(
452446 if n >= size {
453447 Ok ( 0 )
454448 } else {
455- read_n_bytes (
456- & mut std:: io:: BufReader :: with_capacity ( BUF_SIZE , input) ,
457- size - n,
458- )
449+ read_n_bytes ( input, size - n)
459450 }
460451 }
461452 Mode :: AllButLastLines ( n) => {
462453 let found = find_nth_line_from_end ( input, n, options. line_ending . into ( ) ) ?;
463- read_n_bytes (
464- & mut std:: io:: BufReader :: with_capacity ( BUF_SIZE , input) ,
465- found,
466- )
454+ read_n_bytes ( input, found)
467455 }
468456 _ => unreachable ! ( ) ,
469457 }
470458}
471459
472460fn head_file ( input : & mut std:: fs:: File , options : & HeadOptions ) -> std:: io:: Result < u64 > {
473461 match options. mode {
474- Mode :: FirstBytes ( n) => {
475- read_n_bytes ( & mut std:: io:: BufReader :: with_capacity ( BUF_SIZE , input) , n)
476- }
462+ Mode :: FirstBytes ( n) => read_n_bytes ( input, n) ,
477463 Mode :: FirstLines ( n) => read_n_lines (
478464 & mut std:: io:: BufReader :: with_capacity ( BUF_SIZE , input) ,
479465 n,
0 commit comments