@@ -178,16 +178,48 @@ impl Num {
178178 }
179179}
180180
181+ // cannot put cfg in where... (https://github.com/rust-lang/rust/issues/115590)
182+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
183+ trait DdReader : Read + AsFd { }
184+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
185+ trait DdReader : Read { }
186+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
187+ impl < T : Read + AsFd > DdReader for T { }
188+ #[ cfg( not( any( target_os = "linux" , target_os = "android" ) ) ) ]
189+ impl < T : Read > DdReader for T { }
181190/// Read and discard `n` bytes from `reader` using a buffer of size `buf_size`.
182191///
183192/// This is more efficient than `io::copy` with `BufReader` because it reads
184193/// directly in `buf_size`-sized chunks, matching GNU dd's behavior.
185194/// Returns the total number of bytes actually read.
186- fn read_and_discard < R : Read > ( reader : & mut R , n : u64 , buf_size : usize ) -> io:: Result < u64 > {
187- // todo: consider splice()ing to /dev/null on Linux
188- let mut buf = Vec :: with_capacity ( buf_size) ;
195+ fn read_and_discard < R : DdReader > ( reader : & mut R , n : u64 , buf_size : usize ) -> io:: Result < u64 > {
189196 let mut total = 0u64 ;
190197 let mut remaining = n;
198+ #[ cfg( any( target_os = "linux" , target_os = "android" ) ) ]
199+ {
200+ //fast-path
201+ use std:: sync:: OnceLock ;
202+ use uucore:: pipes:: { MAX_ROOTLESS_PIPE_SIZE , dev_null, splice} ;
203+ static INIT : OnceLock < Option < File > > = OnceLock :: new ( ) ;
204+ let null = INIT . get_or_init ( || {
205+ dev_null ( ) . inspect ( |_| {
206+ let _ = rustix:: pipe:: fcntl_setpipe_size ( & reader, MAX_ROOTLESS_PIPE_SIZE ) ;
207+ } )
208+ } ) ;
209+ if let Some ( null) = null {
210+ while remaining > 0 {
211+ match splice ( & reader, & null, remaining as usize ) {
212+ Ok ( 0 ) => return Ok ( total) , // no need to allocate buf
213+ Ok ( bytes_read) => {
214+ total += bytes_read as u64 ;
215+ remaining -= bytes_read as u64 ;
216+ }
217+ Err ( _) => break ,
218+ }
219+ }
220+ }
221+ }
222+ let mut buf = Vec :: with_capacity ( buf_size) ;
191223 while remaining > 0 {
192224 let to_read = cmp:: min ( remaining, buf_size as u64 ) ;
193225 buf. clear ( ) ;
0 commit comments