diff --git a/src/uu/dd/src/dd.rs b/src/uu/dd/src/dd.rs index 6c7ce253d42..30e47e8a2e6 100644 --- a/src/uu/dd/src/dd.rs +++ b/src/uu/dd/src/dd.rs @@ -520,13 +520,22 @@ impl Input<'_> { /// Fills a given buffer. /// Reads in increments of 'self.ibs'. /// The start of each ibs-sized read follows the previous one. - fn fill_consecutive(&mut self, buf: &mut Vec) -> io::Result { + fn fill_consecutive(&mut self, buf: &mut Vec, bsize: usize) -> io::Result { let mut reads_complete = 0; let mut reads_partial = 0; let mut bytes_total = 0; - - for chunk in buf.chunks_mut(self.settings.ibs) { - match self.read(chunk)? { + buf.clear(); + let spare = buf.spare_capacity_mut(); + let spare_len = spare.len(); + let target_area = &mut spare[..bsize.min(spare_len)]; + + for chunk in target_area.chunks_mut(self.settings.ibs) { + // std cannot fix https://github.com/uutils/coreutils/issues/11544 without unsafe yet + // SAFETY: write-only to reserved area of buf and becomes unaccessible at self.read(slice)? soon + let slice = unsafe { + std::slice::from_raw_parts_mut(chunk.as_mut_ptr().cast::(), chunk.len()) + }; + match self.read(slice)? { rlen if rlen == self.settings.ibs => { bytes_total += rlen; reads_complete += 1; @@ -534,11 +543,14 @@ impl Input<'_> { rlen if rlen > 0 => { bytes_total += rlen; reads_partial += 1; + break; } - _ => break, + _ => break, // end of file } } - buf.truncate(bytes_total); + // todo: use wrapper to extend buf with read at same time without unsafe + // SAFETY: we just checked that bytes_total of spare was filled + unsafe { buf.set_len(bytes_total) }; Ok(ReadStat { reads_complete, reads_partial, @@ -551,7 +563,10 @@ impl Input<'_> { /// Fills a given buffer. /// Reads in increments of 'self.ibs'. /// The start of each ibs-sized read is aligned to multiples of ibs; remaining space is filled with the 'pad' byte. - fn fill_blocks(&mut self, buf: &mut Vec, pad: u8) -> io::Result { + fn fill_blocks(&mut self, buf: &mut Vec, bsize: usize, pad: u8) -> io::Result { + // Resize the buffer to the bsize. Any garbage data in the buffer is overwritten or truncated, so there is no need to fill with BUF_INIT_BYTE first. + // resizing buf cause serious performance drop https://github.com/uutils/coreutils/issues/11544 + buf.resize(bsize, BUF_INIT_BYTE); let mut reads_complete = 0; let mut reads_partial = 0; let mut base_idx = 0; @@ -1366,13 +1381,10 @@ fn read_helper(i: &mut Input, buf: &mut Vec, bsize: usize) -> io::Result i.fill_blocks(buf, ch)?, - _ => i.fill_consecutive(buf)?, + Some(ch) => i.fill_blocks(buf, bsize, ch)?, + _ => i.fill_consecutive(buf, bsize)?, }; // Return early if no data if rstat.reads_complete == 0 && rstat.reads_partial == 0 {