@@ -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,8 +108,40 @@ fn prepare_buffer(buf: &mut Vec<u8>) {
97108 }
98109}
99110
100- pub fn exec ( bytes : & [ u8 ] ) -> io:: Result < ( ) > {
111+ pub fn exec ( bytes : & [ u8 ] , aligned : bool ) -> io:: Result < ( ) > {
112+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
113+ let _ = aligned;
101114 let stdout = io:: stdout ( ) ;
115+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
116+ let mut stdout = stdout;
117+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
118+ {
119+ use uucore:: pipes:: { pipe, splice, tee} ;
120+ // don't show any error from fast-path and fallback to write for proper message
121+ if let Ok ( ( p_read, mut p_write) ) = pipe ( )
122+ // todo: zero-copy with default size when fcntl failed
123+ && rustix:: pipe:: fcntl_setpipe_size ( & stdout, MAX_ROOTLESS_PIPE_SIZE ) . is_ok ( )
124+ && p_write. write_all ( bytes) . is_ok ( )
125+ {
126+ if aligned && tee ( & p_read, & stdout, PAGE_SIZE ) . is_ok ( ) {
127+ while let Ok ( 1 ..) = tee ( & p_read, & stdout, usize:: MAX ) { }
128+ } else if let Ok ( ( broker_read, broker_write) ) = pipe ( ) {
129+ // tee() cannot control offset and write to non-pipe
130+ ' hybrid: while let Ok ( mut remain) = tee ( & p_read, & broker_write, usize:: MAX ) {
131+ debug_assert ! ( remain == bytes. len( ) , "splice() should cleanup pipe" ) ;
132+ while remain > 0 {
133+ if let Ok ( s) = splice ( & broker_read, & stdout, remain) {
134+ remain -= s;
135+ } else {
136+ // avoid output breakage with reduced remain even if it would not happen
137+ stdout. write_all ( & bytes[ bytes. len ( ) - remain..] ) ?;
138+ break ' hybrid;
139+ }
140+ }
141+ }
142+ }
143+ }
144+ }
102145 let mut stdout = stdout. lock ( ) ;
103146
104147 loop {
@@ -111,6 +154,7 @@ mod tests {
111154 use super :: * ;
112155
113156 #[ test]
157+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ] // Linux uses different buffer size
114158 fn test_prepare_buffer ( ) {
115159 let tests = [
116160 ( 150 , 16350 ) ,
0 commit comments