diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2023-03-22 14:21:24 +0100 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2023-03-23 11:19:38 +0100 |
commit | 28f50d69bb1bd84275dc9a17d25428eb1ff46772 (patch) | |
tree | d999466231f3eda06d306b3a29d8d2c73f13a0ba /buffered-reader | |
parent | 0d0e785f66b3c7a57e842202019857c7cfbd6ce0 (diff) |
buffered-reader: Implement BufferedReader::copy.
- This is like io::copy, but more efficient as it avoids an extra
copy, and it will try to copy all the data the reader has already
buffered.
- Fixes #974.
Diffstat (limited to 'buffered-reader')
-rw-r--r-- | buffered-reader/NEWS | 4 | ||||
-rw-r--r-- | buffered-reader/src/lib.rs | 55 |
2 files changed, 59 insertions, 0 deletions
diff --git a/buffered-reader/NEWS b/buffered-reader/NEWS index 327bf83a..36c08ce6 100644 --- a/buffered-reader/NEWS +++ b/buffered-reader/NEWS @@ -2,6 +2,10 @@ #+TITLE: buffered-reader NEWS – history of user-visible changes #+STARTUP: content hidestars +* Changes in 1.2.0 +** Notable changes + - BufferedReader::copy is like std::io::copy, but more efficient. + * Changes in 1.1.2 ** Notable changes - The generic buffered reader now correctly handles end-of-file diff --git a/buffered-reader/src/lib.rs b/buffered-reader/src/lib.rs index 5752a508..b8c51ff9 100644 --- a/buffered-reader/src/lib.rs +++ b/buffered-reader/src/lib.rs @@ -862,6 +862,37 @@ pub trait BufferedReader<C> : io::Read + fmt::Debug + fmt::Display + Send + Sync Ok(at_least_one_byte) } + /// Copies data to the given writer returning the copied amount. + /// + /// This is like using [`std::io::copy`], but more efficient as it + /// avoids an extra copy, and it will try to copy all the data the + /// reader has already buffered. + /// + /// On success, returns the amount of data (in bytes) that has + /// been copied. + /// + /// Note: this function reads and copies the data a chunk at a + /// time. A consequence of this is that an error may occur after + /// we have consumed some of the data. + fn copy(&mut self, sink: &mut dyn io::Write) -> io::Result<u64> { + let buf_size = default_buf_size(); + let mut total = 0; + loop { + let data = self.data(buf_size)?; + sink.write_all(data)?; + + let n = data.len(); + total += n as u64; + self.consume(n); + if n < buf_size { + // EOF. + break; + } + } + + Ok(total) + } + /// A helpful debugging aid to pretty print a Buffered Reader stack. /// /// Uses the Buffered Readers' `fmt::Display` implementations. @@ -1084,6 +1115,10 @@ fn buffered_reader_test_data_check<'a, T: BufferedReader<C> + 'a, C: fmt::Debug } #[cfg(test)] +const BUFFERED_READER_TEST_DATA: &[u8] = + include_bytes!("buffered-reader-test.txt"); + +#[cfg(test)] mod test { use super::*; @@ -1228,4 +1263,24 @@ mod test { // Matches EOF. assert!(reader.drop_through(b"def", true).unwrap().0.is_none()); } + + #[test] + fn copy() -> io::Result<()> { + // The memory reader has all the data buffered, copying it + // will issue a single write. + let mut bio = Memory::new(BUFFERED_READER_TEST_DATA); + let mut sink = Vec::new(); + let amount = bio.copy(&mut sink)?; + assert_eq!(amount, 50_000); + assert_eq!(&sink[..], BUFFERED_READER_TEST_DATA); + + // The generic reader uses buffers of the given chunk size, + // copying it will issue multiple writes. + let mut bio = Generic::new(BUFFERED_READER_TEST_DATA, Some(64)); + let mut sink = Vec::new(); + let amount = bio.copy(&mut sink)?; + assert_eq!(amount, 50_000); + assert_eq!(&sink[..], BUFFERED_READER_TEST_DATA); + Ok(()) + } } |