@@ -12,7 +12,14 @@ use uucore::error::{UResult, USimpleError, strip_errno};
1212use uucore:: format_usage;
1313use uucore:: translate;
1414
15+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
16+ const PAGE_SIZE : usize = 4096 ;
17+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
18+ use uucore:: pipes:: MAX_ROOTLESS_PIPE_SIZE ;
19+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
20+ const BUF_SIZE : usize = MAX_ROOTLESS_PIPE_SIZE ;
1521// it's possible that using a smaller or larger buffer might provide better performance
22+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
1623const BUF_SIZE : usize = 16 * 1024 ;
1724
1825#[ uucore:: main]
@@ -21,9 +28,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
2128
2229 #[ allow( clippy:: unwrap_used, reason = "clap provides 'y' by default" ) ]
2330 let mut buffer = args_into_buffer ( matches. get_many :: < OsString > ( "STRING" ) . unwrap ( ) ) ?;
24- prepare_buffer ( & mut buffer) ;
31+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
32+ let aligned = PAGE_SIZE . is_multiple_of ( buffer. len ( ) ) ;
33+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
34+ let aligned = false ;
2535
26- match exec ( & buffer) {
36+ prepare_buffer ( & mut buffer) ;
37+ match exec ( & buffer, aligned) {
2738 Ok ( ( ) ) => Ok ( ( ) ) ,
2839 // On Windows, silently handle broken pipe since there's no SIGPIPE
2940 #[ cfg( windows) ]
@@ -97,10 +108,38 @@ fn prepare_buffer(buf: &mut Vec<u8>) {
97108 }
98109}
99110
100- pub fn exec ( bytes : & [ u8 ] ) -> io:: Result < ( ) > {
101- let stdout = io:: stdout ( ) ;
111+ pub fn exec ( bytes : & [ u8 ] , aligned : bool ) -> io:: Result < ( ) > {
112+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
113+ let _ = aligned;
114+ let mut stdout = io:: stdout ( ) ;
115+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
116+ {
117+ use uucore:: pipes:: { pipe, splice, tee} ;
118+ // don't catch any error at fast-path and fallback to write for proper message
119+ if let Ok ( ( p_read, mut p_write) ) = pipe ( )
120+ && p_write. write_all ( bytes) . is_ok ( )
121+ {
122+ if aligned && tee ( & p_read, & stdout, PAGE_SIZE ) . is_ok ( ) {
123+ let _ = rustix:: pipe:: fcntl_setpipe_size ( & stdout, MAX_ROOTLESS_PIPE_SIZE ) ;
124+ while let Ok ( 1 ..) = tee ( & p_read, & stdout, usize:: MAX ) { }
125+ } else if let Ok ( ( broker_read, broker_write) ) = pipe ( ) {
126+ // tee() cannot control offset and write to non-pipe
127+ ' hybrid: while let Ok ( mut remain) = tee ( & p_read, & broker_write, usize:: MAX ) {
128+ debug_assert ! ( remain == bytes. len( ) , "splice() should cleanup pipe" ) ;
129+ while remain > 0 {
130+ if let Ok ( s) = splice ( & broker_read, & stdout, remain) {
131+ remain -= s;
132+ } else {
133+ // avoid output breakage with reduced remain even if it would not happen
134+ stdout. write_all ( & bytes[ BUF_SIZE - remain..] ) ?;
135+ break ' hybrid;
136+ }
137+ }
138+ }
139+ }
140+ }
141+ }
102142 let mut stdout = stdout. lock ( ) ;
103-
104143 loop {
105144 stdout. write_all ( bytes) ?;
106145 }
@@ -111,6 +150,7 @@ mod tests {
111150 use super :: * ;
112151
113152 #[ test]
153+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ] // Linux uses different buffer size
114154 fn test_prepare_buffer ( ) {
115155 let tests = [
116156 ( 150 , 16350 ) ,
0 commit comments