@@ -13,7 +13,7 @@ use memchr::memchr2;
1313use std:: ffi:: OsString ;
1414use std:: fs:: { File , metadata} ;
1515use std:: io:: { self , BufWriter , ErrorKind , IsTerminal , Read , Write } ;
16- #[ cfg( unix) ]
16+ #[ cfg( any ( unix, target_os = "wasi" ) ) ]
1717use std:: os:: fd:: AsFd ;
1818#[ cfg( unix) ]
1919use std:: os:: unix:: fs:: FileTypeExt ;
@@ -103,7 +103,7 @@ enum CatError {
103103
104104type CatResult < T > = Result < T , CatError > ;
105105
106- #[ cfg( any( target_os = "linux" , target_os = "android " ) ) ]
106+ #[ cfg( any( unix , target_os = "wasi " ) ) ]
107107impl From < rustix:: io:: Errno > for CatError {
108108 fn from ( value : rustix:: io:: Errno ) -> Self {
109109 Self :: Io ( value. into ( ) )
@@ -170,14 +170,14 @@ struct OutputState {
170170 one_blank_kept : bool ,
171171}
172172
173- #[ cfg( unix) ]
173+ #[ cfg( any ( unix, target_os = "wasi" ) ) ]
174174trait FdReadable : Read + AsFd { }
175- #[ cfg( not( unix) ) ]
175+ #[ cfg( not( any ( unix, target_os = "wasi" ) ) ) ]
176176trait FdReadable : Read { }
177177
178- #[ cfg( unix) ]
178+ #[ cfg( any ( unix, target_os = "wasi" ) ) ]
179179impl < T > FdReadable for T where T : Read + AsFd { }
180- #[ cfg( not( unix) ) ]
180+ #[ cfg( not( any ( unix, target_os = "wasi" ) ) ) ]
181181impl < T > FdReadable for T where T : Read { }
182182
183183/// Represents an open file handle, stream, or other device
@@ -493,32 +493,52 @@ fn print_fast<R: FdReadable>(handle: &mut InputHandle<R>) -> CatResult<()> {
493493 }
494494 // If we're not on Linux or Android, or the splice() call failed,
495495 // fall back on slower writing.
496- print_slow ( handle, stdout)
496+ print_unbuffered ( handle, stdout)
497497}
498498
499499#[ cfg_attr( any( target_os = "linux" , target_os = "android" ) , inline( never) ) ] // splice fast-path does not require this allocation
500- #[ cfg_attr( not( any( target_os = "linux" , target_os = "android" ) ) , inline) ]
501- fn print_slow < R : FdReadable > ( handle : & mut InputHandle < R > , stdout : io:: Stdout ) -> CatResult < ( ) > {
500+ #[ cfg( any( unix, target_os = "wasi" ) ) ]
501+ fn print_unbuffered < R : FdReadable > (
502+ handle : & mut InputHandle < R > ,
503+ stdout : io:: Stdout ,
504+ ) -> CatResult < ( ) > {
505+ // todo: since there is no cost by 0-fill, we could use larger heap buffer for throughput
506+ let mut buf = [ std:: mem:: MaybeUninit :: < u8 > :: uninit ( ) ; 1024 * 64 ] ;
507+ // use raw syscall to remove buffering
508+ loop {
509+ match rustix:: io:: read ( & handle. reader , & mut buf) . map ( |( f, _) | f) {
510+ Ok ( [ ] ) => return Ok ( ( ) ) ,
511+ Ok ( filled) => {
512+ uucore:: io:: write_all_raw ( & stdout, filled) . inspect_err ( handle_broken_pipe) ?;
513+ }
514+ Err ( e) if e. kind ( ) != ErrorKind :: Interrupted => return Err ( e. into ( ) ) ,
515+ _ => { }
516+ }
517+ }
518+ }
519+
520+ #[ cfg( not( any( unix, target_os = "wasi" ) ) ) ]
521+ fn print_unbuffered < R : FdReadable > (
522+ handle : & mut InputHandle < R > ,
523+ stdout : io:: Stdout ,
524+ ) -> CatResult < ( ) > {
502525 let mut stdout = stdout. lock ( ) ;
503526 let mut buf = [ 0 ; 1024 * 64 ] ;
504527 loop {
505528 match handle. reader . read ( & mut buf) {
506- Ok ( 0 ) => break ,
507- Ok ( n) => stdout
508- . write_all ( & buf[ ..n] )
509- . inspect_err ( handle_broken_pipe) ?,
529+ Ok ( 0 ) => return Ok ( ( ) ) ,
530+ Ok ( n) => {
531+ stdout
532+ . write_all ( & buf[ ..n] )
533+ . inspect_err ( handle_broken_pipe) ?;
534+ // we cannot use rustix::io on Windows
535+ // really bad workaround for unbuffered write <https://github.com/uutils/coreutils/issues/12188>
536+ stdout. flush ( ) . inspect_err ( handle_broken_pipe) ?;
537+ }
510538 Err ( e) if e. kind ( ) != ErrorKind :: Interrupted => return Err ( e. into ( ) ) ,
511539 _ => { }
512540 }
513541 }
514-
515- // If the splice() call failed and there has been some data written to
516- // stdout via while loop above AND there will be second splice() call
517- // that will succeed, data pushed through splice will be output before
518- // the data buffered in stdout.lock. Therefore additional explicit flush
519- // is required here.
520- stdout. flush ( ) . inspect_err ( handle_broken_pipe) ?;
521- Ok ( ( ) )
522542}
523543
524544/// Outputs file contents to stdout in a line-by-line fashion,
0 commit comments