@@ -13,8 +13,13 @@ use uucore::error::{UResult, USimpleError, strip_errno};
1313use uucore:: format_usage;
1414use uucore:: translate;
1515
16- // it's possible that using a smaller or larger buffer might provide better performance on some
17- // systems, but honestly this is good enough
16+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
17+ const PAGE_SIZE : usize = 4096 ;
18+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
19+ use uucore:: pipes:: MAX_ROOTLESS_PIPE_SIZE ;
20+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
21+ const BUF_SIZE : usize = MAX_ROOTLESS_PIPE_SIZE ;
22+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
1823const BUF_SIZE : usize = 16 * 1024 ;
1924
2025#[ uucore:: main]
@@ -24,9 +29,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
2429 let mut buffer = Vec :: with_capacity ( BUF_SIZE ) ;
2530 #[ allow( clippy:: unwrap_used, reason = "clap provides 'y' by default" ) ]
2631 let _ = args_into_buffer ( & mut buffer, matches. get_many :: < OsString > ( "STRING" ) . unwrap ( ) ) ;
27- prepare_buffer ( & mut buffer) ;
32+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
33+ let aligned = PAGE_SIZE . is_multiple_of ( buffer. len ( ) ) ;
34+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
35+ let aligned = false ;
2836
29- match exec ( & buffer) {
37+ prepare_buffer ( & mut buffer) ;
38+ match exec ( & buffer, aligned) {
3039 Ok ( ( ) ) => Ok ( ( ) ) ,
3140 // On Windows, silently handle broken pipe since there's no SIGPIPE
3241 #[ cfg( windows) ]
@@ -103,10 +112,35 @@ fn prepare_buffer(buf: &mut Vec<u8>) {
103112 }
104113}
105114
106- pub fn exec ( bytes : & [ u8 ] ) -> io:: Result < ( ) > {
115+ pub fn exec ( bytes : & [ u8 ] , aligned : bool ) -> io:: Result < ( ) > {
107116 let stdout = io:: stdout ( ) ;
117+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
118+ {
119+ use uucore:: pipes:: { pipe, splice, tee} ;
120+ // don't catch any error at fast-path and fallback to write for proper message
121+ if let Ok ( ( p_read, mut p_write) ) = pipe ( )
122+ && p_write. write_all ( bytes) . is_ok ( )
123+ {
124+ if aligned && tee ( & p_read, & stdout, PAGE_SIZE ) . is_ok ( ) {
125+ let _ = rustix:: pipe:: fcntl_setpipe_size ( & stdout, MAX_ROOTLESS_PIPE_SIZE ) ;
126+ while let Ok ( 1 ..) = tee ( & p_read, & stdout, usize:: MAX ) { }
127+ } else if let Ok ( ( broker_read, broker_write) ) = pipe ( ) {
128+ // tee() cannot control offset and write to non-pipe
129+ ' hybrid: while let Ok ( mut remain) = tee ( & p_read, & broker_write, usize:: MAX ) {
130+ debug_assert ! ( remain == bytes. len( ) , "splice() should cleanup pipe" ) ;
131+ while remain > 0 {
132+ if let Ok ( s) = splice ( & broker_read, & stdout, remain) {
133+ remain -= s;
134+ } else {
135+ break ' hybrid;
136+ }
137+ }
138+ }
139+ }
140+ }
141+ }
142+ let _ = aligned; // used only on Linux
108143 let mut stdout = stdout. lock ( ) ;
109-
110144 loop {
111145 stdout. write_all ( bytes) ?;
112146 }
@@ -117,6 +151,7 @@ mod tests {
117151 use super :: * ;
118152
119153 #[ test]
154+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ] // Linux uses different buffer size
120155 fn test_prepare_buffer ( ) {
121156 let tests = [
122157 ( 150 , 16350 ) ,
0 commit comments