summaryrefslogtreecommitdiffstats
path: root/buffered-reader
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2023-03-22 14:21:24 +0100
committerJustus Winter <justus@sequoia-pgp.org>2023-03-23 11:19:38 +0100
commit28f50d69bb1bd84275dc9a17d25428eb1ff46772 (patch)
treed999466231f3eda06d306b3a29d8d2c73f13a0ba /buffered-reader
parent0d0e785f66b3c7a57e842202019857c7cfbd6ce0 (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/NEWS4
-rw-r--r--buffered-reader/src/lib.rs55
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(())
+ }
}