aboutsummaryrefslogtreecommitdiff
path: root/src/de/read.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/read.rs')
-rw-r--r--src/de/read.rs202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/de/read.rs b/src/de/read.rs
new file mode 100644
index 0000000..08b76ad
--- /dev/null
+++ b/src/de/read.rs
@@ -0,0 +1,202 @@
+use error::Result;
+use serde;
+use std::io;
+
+/// An optional Read trait for advanced Bincode usage.
+///
+/// It is highly recommended to use bincode with `io::Read` or `&[u8]` before
+/// implementing a custom `BincodeRead`.
+///
+/// The forward_read_* methods are necessary because some byte sources want
+/// to pass a long-lived borrow to the visitor and others want to pass a
+/// transient slice.
+pub trait BincodeRead<'storage>: io::Read {
+ /// Check that the next `length` bytes are a valid string and pass
+ /// it on to the serde reader.
+ fn forward_read_str<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
+ where
+ V: serde::de::Visitor<'storage>;
+
+ /// Transfer ownership of the next `length` bytes to the caller.
+ fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>>;
+
+ /// Pass a slice of the next `length` bytes on to the serde reader.
+ fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
+ where
+ V: serde::de::Visitor<'storage>;
+}
+
+/// A BincodeRead implementation for byte slices
+pub struct SliceReader<'storage> {
+ slice: &'storage [u8],
+}
+
+/// A BincodeRead implementation for `io::Read`ers
+pub struct IoReader<R> {
+ reader: R,
+ temp_buffer: Vec<u8>,
+}
+
+impl<'storage> SliceReader<'storage> {
+ /// Constructs a slice reader
+ pub(crate) fn new(bytes: &'storage [u8]) -> SliceReader<'storage> {
+ SliceReader { slice: bytes }
+ }
+
+ #[inline(always)]
+ fn get_byte_slice(&mut self, length: usize) -> Result<&'storage [u8]> {
+ if length > self.slice.len() {
+ return Err(SliceReader::unexpected_eof());
+ }
+ let (read_slice, remaining) = self.slice.split_at(length);
+ self.slice = remaining;
+ Ok(read_slice)
+ }
+
+ pub(crate) fn is_finished(&self) -> bool {
+ self.slice.is_empty()
+ }
+}
+
+impl<R> IoReader<R> {
+ /// Constructs an IoReadReader
+ pub(crate) fn new(r: R) -> IoReader<R> {
+ IoReader {
+ reader: r,
+ temp_buffer: vec![],
+ }
+ }
+}
+
+impl<'storage> io::Read for SliceReader<'storage> {
+ #[inline(always)]
+ fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
+ if out.len() > self.slice.len() {
+ return Err(io::ErrorKind::UnexpectedEof.into());
+ }
+ let (read_slice, remaining) = self.slice.split_at(out.len());
+ out.copy_from_slice(read_slice);
+ self.slice = remaining;
+
+ Ok(out.len())
+ }
+
+ #[inline(always)]
+ fn read_exact(&mut self, out: &mut [u8]) -> io::Result<()> {
+ self.read(out).map(|_| ())
+ }
+}
+
+impl<R: io::Read> io::Read for IoReader<R> {
+ #[inline(always)]
+ fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
+ self.reader.read(out)
+ }
+ #[inline(always)]
+ fn read_exact(&mut self, out: &mut [u8]) -> io::Result<()> {
+ self.reader.read_exact(out)
+ }
+}
+
+impl<'storage> SliceReader<'storage> {
+ #[inline(always)]
+ fn unexpected_eof() -> Box<::ErrorKind> {
+ Box::new(::ErrorKind::Io(io::Error::new(
+ io::ErrorKind::UnexpectedEof,
+ "",
+ )))
+ }
+}
+
+impl<'storage> BincodeRead<'storage> for SliceReader<'storage> {
+ #[inline(always)]
+ fn forward_read_str<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
+ where
+ V: serde::de::Visitor<'storage>,
+ {
+ use ErrorKind;
+ let string = match ::std::str::from_utf8(self.get_byte_slice(length)?) {
+ Ok(s) => s,
+ Err(e) => return Err(ErrorKind::InvalidUtf8Encoding(e).into()),
+ };
+ visitor.visit_borrowed_str(string)
+ }
+
+ #[inline(always)]
+ fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>> {
+ self.get_byte_slice(length).map(|x| x.to_vec())
+ }
+
+ #[inline(always)]
+ fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
+ where
+ V: serde::de::Visitor<'storage>,
+ {
+ visitor.visit_borrowed_bytes(self.get_byte_slice(length)?)
+ }
+}
+
+impl<R> IoReader<R>
+where
+ R: io::Read,
+{
+ fn fill_buffer(&mut self, length: usize) -> Result<()> {
+ self.temp_buffer.resize(length, 0);
+
+ self.reader.read_exact(&mut self.temp_buffer)?;
+
+ Ok(())
+ }
+}
+
+impl<'a, R> BincodeRead<'a> for IoReader<R>
+where
+ R: io::Read,
+{
+ fn forward_read_str<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
+ where
+ V: serde::de::Visitor<'a>,
+ {
+ self.fill_buffer(length)?;
+
+ let string = match ::std::str::from_utf8(&self.temp_buffer[..]) {
+ Ok(s) => s,
+ Err(e) => return Err(::ErrorKind::InvalidUtf8Encoding(e).into()),
+ };
+
+ visitor.visit_str(string)
+ }
+
+ fn get_byte_buffer(&mut self, length: usize) -> Result<Vec<u8>> {
+ self.fill_buffer(length)?;
+ Ok(::std::mem::replace(&mut self.temp_buffer, Vec::new()))
+ }
+
+ fn forward_read_bytes<V>(&mut self, length: usize, visitor: V) -> Result<V::Value>
+ where
+ V: serde::de::Visitor<'a>,
+ {
+ self.fill_buffer(length)?;
+ visitor.visit_bytes(&self.temp_buffer[..])
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::IoReader;
+
+ #[test]
+ fn test_fill_buffer() {
+ let buffer = vec![0u8; 64];
+ let mut reader = IoReader::new(buffer.as_slice());
+
+ reader.fill_buffer(20).unwrap();
+ assert_eq!(20, reader.temp_buffer.len());
+
+ reader.fill_buffer(30).unwrap();
+ assert_eq!(30, reader.temp_buffer.len());
+
+ reader.fill_buffer(5).unwrap();
+ assert_eq!(5, reader.temp_buffer.len());
+ }
+}