@@ -153,22 +153,67 @@ impl MultiWriter {
153153 /// Copies all bytes from the input buffer to the output buffer
154154 /// without buffering which is POSIX requirement.
155155 pub fn copy_unbuffered ( & mut self ) -> Result < ( ) > {
156- // todo: support splice() and tee() fast-path at here
157156 #[ cfg( not( any( unix, target_os = "wasi" ) ) ) ]
158157 use std:: io:: Read as _;
159158 const BUF_SIZE : usize = 32 * 1024 ;
159+ #[ cfg( any( unix, target_os = "wasi" ) ) ]
160+ let input = rustix:: stdio:: stdin ( ) ;
161+ #[ cfg( not( any( unix, target_os = "wasi" ) ) ) ]
162+ let mut input = std:: io:: stdin ( ) ;
163+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
164+ macro_rules! splice_or_detach {
165+ ( $pipe: expr, $writer: expr, $len: expr) => {
166+ if let Err ( e) = uucore:: pipes:: drain_pipe( $pipe, $writer, $len) {
167+ if let Err ( e) =
168+ process_error( self . output_error_mode, e, $writer, & mut self . ignored_errors)
169+ {
170+ self . aborted. get_or_insert( e) ;
171+ }
172+ $writer. name. clear( ) ; //mark as exited
173+ }
174+ } ;
175+ }
176+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
177+ {
178+ use rustix:: pipe:: fcntl_setpipe_size;
179+ use uucore:: pipes:: MAX_ROOTLESS_PIPE_SIZE ;
180+ let ( pipe_read, pipe_write) = std:: io:: pipe ( ) ?; // needed to duplicate input
181+ let ( pipe2_read, pipe2_write) = std:: io:: pipe ( ) ?; // force-tee() even output is not pipe
182+ // improve throughput. 2nd pipe should be larger than 1st one for proper tee() length.
183+ if fcntl_setpipe_size ( & pipe2_read, MAX_ROOTLESS_PIPE_SIZE ) . is_ok ( ) {
184+ let _ = fcntl_setpipe_size ( & pipe_read, MAX_ROOTLESS_PIPE_SIZE ) ;
185+ let _ = fcntl_setpipe_size ( & self . writers [ 0 ] , MAX_ROOTLESS_PIPE_SIZE ) ;
186+ }
187+ while let Ok ( s) = uucore:: pipes:: splice ( & input, & pipe_write, MAX_ROOTLESS_PIPE_SIZE ) {
188+ if s == 0 {
189+ return Ok ( ( ) ) ;
190+ }
191+ let ( last, others) = self
192+ . writers
193+ . split_last_mut ( )
194+ . ok_or_else ( || Error :: from ( ErrorKind :: Other ) ) ?; // all writers exited
195+ for other in others {
196+ // do not consume input
197+ let tee_res = uucore:: pipes:: tee ( & pipe_read, & pipe2_write, s) ;
198+ assert_eq ! ( tee_res, Ok ( s) , "2nd pipe should have enough spare" ) ;
199+ splice_or_detach ! ( & pipe2_read, other, s) ;
200+ }
201+ // last one consumes input
202+ splice_or_detach ! ( & pipe_read, last, s) ;
203+ self . writers . retain ( |w| !w. name . is_empty ( ) ) ;
204+ self . aborted . take ( ) . map_or ( Ok ( ( ) ) , Err ) ?;
205+ }
206+ }
207+
160208 #[ cfg( any( unix, target_os = "wasi" ) ) ]
161209 let mut buf = [ std:: mem:: MaybeUninit :: < u8 > :: uninit ( ) ; BUF_SIZE ] ;
162210 // todo: avoid cost by 0-fill keeping throughput
163211 #[ cfg( not( any( unix, target_os = "wasi" ) ) ) ]
164212 let mut buf = [ 0u8 ; BUF_SIZE ] ;
165213
166- let input = std:: io:: stdin ( ) ;
167- #[ cfg( not( any( unix, target_os = "wasi" ) ) ) ]
168- let mut input = input;
169214 loop {
170215 #[ cfg( any( unix, target_os = "wasi" ) ) ]
171- let res = rustix:: io:: read ( & input, & mut buf)
216+ let res = rustix:: io:: read ( input, & mut buf)
172217 . map ( |f| f. 0 )
173218 . map_err ( Error :: from) ;
174219 #[ cfg( not( any( unix, target_os = "wasi" ) ) ) ]
@@ -276,3 +321,13 @@ struct NamedWriter {
276321 inner : Writer ,
277322 pub name : OsString ,
278323}
324+
325+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
326+ impl rustix:: fd:: AsFd for NamedWriter {
327+ fn as_fd ( & self ) -> rustix:: fd:: BorrowedFd < ' _ > {
328+ match & self . inner {
329+ Writer :: File ( f) => f. as_fd ( ) ,
330+ Writer :: Stdout ( s) => s. 0 ,
331+ }
332+ }
333+ }
0 commit comments