From e18b166514cb64fdbe1c025896217f198a51fedf Mon Sep 17 00:00:00 2001 From: Avril Date: Thu, 12 Aug 2021 16:22:12 +0100 Subject: [PATCH] `stream`: Move `Sink` and `Source` to seperate files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for chacha20stream's current commit: Small curse − 小凶 --- src/stream.rs | 395 ------------------------------------------- src/stream/mod.rs | 116 +++++++++++++ src/stream/sink.rs | 171 +++++++++++++++++++ src/stream/source.rs | 117 +++++++++++++ 4 files changed, 404 insertions(+), 395 deletions(-) delete mode 100644 src/stream.rs create mode 100644 src/stream/mod.rs create mode 100644 src/stream/sink.rs create mode 100644 src/stream/source.rs diff --git a/src/stream.rs b/src/stream.rs deleted file mode 100644 index a5b327f..0000000 --- a/src/stream.rs +++ /dev/null @@ -1,395 +0,0 @@ -#![allow(dead_code)] - -use super::*; -use key::*; - -use std::io::{self, Write, Read}; -use std::fmt; -use openssl::{ - symm::Crypter, - error::ErrorStack, -}; - -/// Size of the in-structure buffer -#[cfg(feature="smallvec")] -pub const BUFFER_SIZE: usize = 32; - -#[cfg(feature="smallvec")] -type BufferVec = smallvec::SmallVec<[u8; BUFFER_SIZE]>; -#[cfg(not(feature="smallvec"))] -type BufferVec = Vec; - -pub type Error = ErrorStack; - -/// ChaCha Sink -/// -/// # Encryption -/// To create an encrypting wrapper stream: -/// ``` -/// # use chacha20stream::Sink; -/// # use std::io::Write; -/// # let (key, iv) = chacha20stream::keygen(); -/// # let mut backing_stream = Vec::new(); -/// let mut stream = Sink::encrypt(&mut backing_stream, key, iv).expect("Failed to create encryptor"); -/// /* do work with `stream` */ -/// -/// // It is recommended to `flush` the stream to clear out any remaining data in the internal transformation buffer. -/// stream.flush().unwrap(); -/// ``` -/// -/// # Decryption -/// To create a decrypting wrapper stream: -/// ``` -/// # use chacha20stream::Sink; -/// # use std::io::Write; -/// # let (key, iv) = chacha20stream::keygen(); -/// # let mut backing_stream = Vec::new(); -/// let mut stream = Sink::decrypt(&mut backing_stream, key, iv).expect("Failed to create decryptor"); -/// /* do work with `stream` */ -/// -/// // It is recommended to `flush` the stream to clear out any remaining data in the internal transformation buffer. -/// stream.flush().unwrap(); -/// ``` -/// -/// # Note -/// When writing, a temporary buffer stored in the structure is used. This buffer is **not** cleared after a write, for efficiency reasons. This may leave sensitive information in the buffer after the write operation. -/// The `flush()` implementation *does* clear this buffer. -/// You can use the `prune()` function to zero out this buffer manually too. -//#[derive(Debug)] -pub struct Sink -{ - crypter: Crypter, // for chacha, finalize does nothing it seems. we can also call it multiple times. - buffer: BufferVec, // used to buffer the operation - - stream: W, -} - -/// TODO: Document -//#[derive(Debug)] -pub struct Source -{ - crypter: Crypter, - #[cfg(not(feature="reuse-buffer"))] buffer: BufferVec, // When `reuse-buffer` is enabled, this isn't needed. We re-use the output buffer for the initial read of untransformed data from `stream` and the actual transformation of the read bytes. - - stream: R -} - -impl fmt::Debug for Sink -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - write!(f, "Sink({:?}, ({} buffer cap))", &self.stream, self.buffer.capacity()) - } -} - -impl fmt::Debug for Source -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result - { - #[cfg(feature="reuse-buffer")] - return write!(f, "Source({:?}, (unbounded buffer cap))", &self.stream); - #[cfg(not(feature="reuse-buffer"))] - return write!(f, "Source({:?}, ({} buffer cap))", &self.stream, self.buffer.capacity()); - } -} - -impl Source -where R: Read -{ - /// The crypter of this instance - #[inline] pub fn crypter(&self) -> &Crypter - { - &self.crypter - } - - /// The crypter of this instance - #[inline] pub fn crypter_mut(&mut self) -> &mut Crypter - { - &mut self.crypter - } - - /// The inner stream - #[inline] pub fn inner(&self) -> &R - { - &self.stream - } - - /// The inner stream - #[inline] pub fn inner_mut(&mut self) -> &mut R - { - &mut self.stream - } - - #[cfg(not(feature="reuse-buffer"))] - /// Grow the inner buffer to fix this size, if needed. - fn grow_to_fit(&mut self, sz: usize) - { - if sz > self.buffer.len() { - self.buffer.resize(sz, 0); - } - } - - #[cfg(not(feature="reuse-buffer"))] - /// Perform the cipher transform on this input to the inner buffer, returning the number of bytes updated. - fn transform(&mut self, bufsz: usize, output: &mut [u8]) -> Result - { - //self.grow_to_fix(output.len()); - //let bufsz = self.stream.read(&mut self.buffer[..bufsz])?; - let n = self.crypter.update(&self.buffer[..bufsz], &mut output[..])?; - let _f = self.crypter.finalize(&mut output[..n])?; - debug_assert_eq!(_f, 0); - - Ok(n) - /* - if buf.len() > self.buffer.len() { - self.buffer.resize(buf.len(), 0); - } - - let n = self.crypter.update(&buf[..], &mut self.buffer[..])?; - let _f = self.crypter.finalize(&mut self.buffer[..n])?; // I don't know if this is needed. - debug_assert_eq!(_f, 0); - - Ok(n)*/ - } - - #[cfg(not(feature="reuse-buffer"))] - /// Clear the internal buffer while keeping it allocated for further use. - /// - /// This does not affect operations at all, all it does is 0 out the left-over temporary buffer from the last operation(s). - #[inline] - pub fn prune(&mut self) - { - #[cfg(feature="explicit_clear")] - { - bytes::explicit_prune(&mut self.buffer[..]); - return; - } - #[cfg(not(feature="explicit_clear"))] - unsafe { - std::ptr::write_bytes(self.buffer.as_mut_ptr(), 0, self.buffer.len()); - } - } -} - -impl Sink -where W: Write -{ - /// The crypter of this instance - #[inline] pub fn crypter(&self) -> &Crypter - { - &self.crypter - } - - /// The crypter of this instance - #[inline] pub fn crypter_mut(&mut self) -> &mut Crypter - { - &mut self.crypter - } - - /// The inner stream - #[inline] pub fn inner(&self) -> &W - { - &self.stream - } - - /// The inner stream - #[inline] pub fn inner_mut(&mut self) -> &mut W - { - &mut self.stream - } - - /// Perform the cipher transform on this input to the inner buffer, returning the number of bytes updated. - fn transform(&mut self, buf: &[u8]) -> Result - { - if buf.len() > self.buffer.len() { - self.buffer.resize(buf.len(), 0); - } - - let n = self.crypter.update(&buf[..], &mut self.buffer[..])?; - let _f = self.crypter.finalize(&mut self.buffer[..n])?; // I don't know if this is needed. - debug_assert_eq!(_f, 0); - - Ok(n) - } - - /// Clear the internal buffer while keeping it allocated for further use. - /// - /// This does not affect operations at all, all it does is 0 out the left-over temporary buffer from the last operation(s). - #[inline] - pub fn prune(&mut self) - { - #[cfg(feature="explicit_clear")] - { - bytes::explicit_prune(&mut self.buffer[..]); - return; - } - #[cfg(not(feature="explicit_clear"))] - unsafe { - std::ptr::write_bytes(self.buffer.as_mut_ptr(), 0, self.buffer.len()); - } - } -} - -impl Sink -where W: Write -{ - /// Create a new Chacha Sink stream wrapper - #[inline] fn new(stream: W, crypter: Crypter) -> Self - { - Self{stream, crypter, buffer: BufferVec::new()} - } - - /// Create an encrypting Chacha Sink stream wrapper - pub fn encrypt(stream: W, key: Key, iv: IV) -> Result - { - Ok(Self::new(stream, cha::encrypter(key, iv)?)) - } - - /// Create a decrypting Chacha Sink stream wrapper - pub fn decrypt(stream: W, key: Key, iv: IV) -> Result - { - Ok(Self::new(stream, cha::decrypter(key, iv)?)) - } - - - /// Consume into the inner stream - #[inline] pub fn into_inner(self) -> W - { - self.stream - } - - /// Consume into the inner stream and crypter - #[inline] pub fn into_parts(self) -> (W, Crypter) - { - (self.stream, self.crypter) - } - - -} - - -impl Read for Source -where R: Read -{ - fn read(&mut self, buf: &mut [u8]) -> io::Result { - (#[cfg(feature="reuse-buffer")] { - todo!() - }, - #[cfg(not(feature="reuse-buffer"))] { - self.grow_to_fit(buf.len()); - let read = self.stream.read(&mut self.buffer[..buf.len()])?; - Ok(self.transform(read, &mut buf[..read])?) - },).0 - } -} - -impl Write for Sink -{ - #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { - let n = self.transform(buf)?; - - self.stream.write(&self.buffer[..n]) - - } - #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let n = self.transform(buf)?; - - self.stream.write_all(&self.buffer[..n]) - } - #[inline] fn flush(&mut self) -> io::Result<()> { - #[cfg(feature="explicit_clear")] self.prune(); - self.buffer.clear(); - - self.stream.flush() - } -} - -#[cfg(test)] -mod tests -{ - use super::*; - - const INPUT: &'static str = "Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!"; - - fn enc_stream(input: impl AsRef<[u8]>, key: Key, iv: IV) -> Sink> - { - let enc_buffer = Vec::new(); - let input = input.as_ref(); - - eprintln!("(enc) Key: {}, IV: {}, Input: ({}, {})", key, iv, input.len(), input.hex()); - - let mut stream = Sink::encrypt(enc_buffer, key, iv).expect("sink::enc"); - assert_eq!(stream.write(input).unwrap(), input.len()); - stream.flush().unwrap(); - - eprintln!("Output encrypted: {}", stream.inner().hex()); - - stream - } - - #[test] - fn enc() - { - let (key, iv) = cha::keygen(); - - eprintln!("Sink ends: {:?}", enc_stream(INPUT.as_bytes(), key, iv)); - } - - #[test] - fn dec() - { - println!(">>> Sink's size with ref is {}", std::mem::size_of::>>()); - let (key, iv) = cha::keygen(); - eprintln!("Input unencrypted: {}", INPUT.hex()); - - let input = enc_stream(INPUT.as_bytes(), key.clone(), iv.clone()).into_inner(); - - let mut dec_buffer = Vec::new(); - { - let mut stream = Sink::decrypt(&mut dec_buffer, key, iv).expect("sink::dec"); - - stream.write_all(&input[..]).unwrap(); - stream.flush().unwrap(); - - eprintln!("Output decrypted: {}", stream.inner().hex()); - } - assert_eq!(&dec_buffer[..], INPUT.as_bytes()); - } - - /// Checks if explicit clear is actually clearing. - #[cfg(feature="explicit_clear")] - #[test] - fn remainder() - { - let mut dec_buffer = Vec::new(); - - let (buf, off, _s) = { - let (key, iv) = cha::keygen(); - - let input = enc_stream(INPUT.as_bytes(), key.clone(), iv.clone()).into_inner(); - - { - let mut stream = Sink::decrypt(&mut dec_buffer, key, iv).expect("sink::rem"); - - stream.write_all(&input[..]).unwrap(); - - let by = stream.buffer[0]; - //stream.prune(); - stream.flush().unwrap(); - (by, (stream.buffer.as_ptr() as u64), stream) - } - }; - - // Check to see if the buffer remains in our process's memory. - use std::fs::OpenOptions; - use std::io::{Seek, SeekFrom, Read}; - let mut file = OpenOptions::new().read(true).open("/proc/self/mem").unwrap(); - - file.seek(SeekFrom::Start(off)).unwrap(); - let mut chk = [0u8; 10]; - file.read_exact(&mut chk).unwrap(); - assert!(buf != chk[0]); - } -} - - diff --git a/src/stream/mod.rs b/src/stream/mod.rs new file mode 100644 index 0000000..f1e9073 --- /dev/null +++ b/src/stream/mod.rs @@ -0,0 +1,116 @@ +#![allow(dead_code)] + +use super::*; +use key::*; + +use std::io::{self, Write, Read}; +use std::fmt; +use openssl::{ + symm::Crypter, + error::ErrorStack, +}; + +/// Size of the in-structure buffer +#[cfg(feature="smallvec")] +pub const BUFFER_SIZE: usize = 32; + +#[cfg(feature="smallvec")] +type BufferVec = smallvec::SmallVec<[u8; BUFFER_SIZE]>; +#[cfg(not(feature="smallvec"))] +type BufferVec = Vec; + +pub type Error = ErrorStack; + +pub mod sink; +pub mod source; + +pub use sink::Sink; +pub use source::Source; + +#[cfg(test)] +mod tests +{ + use super::*; + + const INPUT: &'static str = "Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!"; + + fn enc_stream(input: impl AsRef<[u8]>, key: Key, iv: IV) -> Sink> + { + let enc_buffer = Vec::new(); + let input = input.as_ref(); + + eprintln!("(enc) Key: {}, IV: {}, Input: ({}, {})", key, iv, input.len(), input.hex()); + + let mut stream = Sink::encrypt(enc_buffer, key, iv).expect("sink::enc"); + assert_eq!(stream.write(input).unwrap(), input.len()); + stream.flush().unwrap(); + + eprintln!("Output encrypted: {}", stream.inner().hex()); + + stream + } + + #[test] + fn enc() + { + let (key, iv) = cha::keygen(); + + eprintln!("Sink ends: {:?}", enc_stream(INPUT.as_bytes(), key, iv)); + } + + #[test] + fn dec() + { + println!(">>> Sink's size with ref is {}", std::mem::size_of::>>()); + let (key, iv) = cha::keygen(); + eprintln!("Input unencrypted: {}", INPUT.hex()); + + let input = enc_stream(INPUT.as_bytes(), key.clone(), iv.clone()).into_inner(); + + let mut dec_buffer = Vec::new(); + { + let mut stream = Sink::decrypt(&mut dec_buffer, key, iv).expect("sink::dec"); + + stream.write_all(&input[..]).unwrap(); + stream.flush().unwrap(); + + eprintln!("Output decrypted: {}", stream.inner().hex()); + } + assert_eq!(&dec_buffer[..], INPUT.as_bytes()); + } + + /// Checks if explicit clear is actually clearing. + #[cfg(feature="explicit_clear")] + #[test] + fn remainder() + { + let mut dec_buffer = Vec::new(); + + let (buf, off, _s) = { + let (key, iv) = cha::keygen(); + + let input = enc_stream(INPUT.as_bytes(), key.clone(), iv.clone()).into_inner(); + + { + let mut stream = Sink::decrypt(&mut dec_buffer, key, iv).expect("sink::rem"); + + stream.write_all(&input[..]).unwrap(); + + let by = stream.buffer[0]; + //stream.prune(); + stream.flush().unwrap(); + (by, (stream.buffer.as_ptr() as u64), stream) + } + }; + + // Check to see if the buffer remains in our process's memory. + use std::fs::OpenOptions; + use std::io::{Seek, SeekFrom, Read}; + let mut file = OpenOptions::new().read(true).open("/proc/self/mem").unwrap(); + + file.seek(SeekFrom::Start(off)).unwrap(); + let mut chk = [0u8; 10]; + file.read_exact(&mut chk).unwrap(); + assert!(buf != chk[0]); + } +} diff --git a/src/stream/sink.rs b/src/stream/sink.rs new file mode 100644 index 0000000..dc4fef6 --- /dev/null +++ b/src/stream/sink.rs @@ -0,0 +1,171 @@ +//! Syncronous stream `Write` componant. +use super::*; + +/// ChaCha Sink +/// +/// # Encryption +/// To create an encrypting wrapper stream: +/// ``` +/// # use chacha20stream::Sink; +/// # use std::io::Write; +/// # let (key, iv) = chacha20stream::keygen(); +/// # let mut backing_stream = Vec::new(); +/// let mut stream = Sink::encrypt(&mut backing_stream, key, iv).expect("Failed to create encryptor"); +/// /* do work with `stream` */ +/// +/// // It is recommended to `flush` the stream to clear out any remaining data in the internal transformation buffer. +/// stream.flush().unwrap(); +/// ``` +/// +/// # Decryption +/// To create a decrypting wrapper stream: +/// ``` +/// # use chacha20stream::Sink; +/// # use std::io::Write; +/// # let (key, iv) = chacha20stream::keygen(); +/// # let mut backing_stream = Vec::new(); +/// let mut stream = Sink::decrypt(&mut backing_stream, key, iv).expect("Failed to create decryptor"); +/// /* do work with `stream` */ +/// +/// // It is recommended to `flush` the stream to clear out any remaining data in the internal transformation buffer. +/// stream.flush().unwrap(); +/// ``` +/// +/// # Note +/// When writing, a temporary buffer stored in the structure is used. This buffer is **not** cleared after a write, for efficiency reasons. This may leave sensitive information in the buffer after the write operation. +/// The `flush()` implementation *does* clear this buffer. +/// You can use the `prune()` function to zero out this buffer manually too. +//#[derive(Debug)] +pub struct Sink +{ + crypter: Crypter, // for chacha, finalize does nothing it seems. we can also call it multiple times. + buffer: BufferVec, // used to buffer the operation + + stream: W, +} + +impl fmt::Debug for Sink +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "Sink({:?}, ({} buffer cap))", &self.stream, self.buffer.capacity()) + } +} + +impl Sink +where W: Write +{ + /// The crypter of this instance + #[inline] pub fn crypter(&self) -> &Crypter + { + &self.crypter + } + + /// The crypter of this instance + #[inline] pub fn crypter_mut(&mut self) -> &mut Crypter + { + &mut self.crypter + } + + /// The inner stream + #[inline] pub fn inner(&self) -> &W + { + &self.stream + } + + /// The inner stream + #[inline] pub fn inner_mut(&mut self) -> &mut W + { + &mut self.stream + } + + /// Perform the cipher transform on this input to the inner buffer, returning the number of bytes updated. + fn transform(&mut self, buf: &[u8]) -> Result + { + if buf.len() > self.buffer.len() { + self.buffer.resize(buf.len(), 0); + } + + let n = self.crypter.update(&buf[..], &mut self.buffer[..])?; + let _f = self.crypter.finalize(&mut self.buffer[..n])?; // I don't know if this is needed. + debug_assert_eq!(_f, 0); + + Ok(n) + } + + /// Clear the internal buffer while keeping it allocated for further use. + /// + /// This does not affect operations at all, all it does is 0 out the left-over temporary buffer from the last operation(s). + #[inline] + pub fn prune(&mut self) + { + #[cfg(feature="explicit_clear")] + { + bytes::explicit_prune(&mut self.buffer[..]); + return; + } + #[cfg(not(feature="explicit_clear"))] + unsafe { + std::ptr::write_bytes(self.buffer.as_mut_ptr(), 0, self.buffer.len()); + } + } +} + +impl Sink +where W: Write +{ + /// Create a new Chacha Sink stream wrapper + #[inline] fn new(stream: W, crypter: Crypter) -> Self + { + Self{stream, crypter, buffer: BufferVec::new()} + } + + /// Create an encrypting Chacha Sink stream wrapper + pub fn encrypt(stream: W, key: Key, iv: IV) -> Result + { + Ok(Self::new(stream, cha::encrypter(key, iv)?)) + } + + /// Create a decrypting Chacha Sink stream wrapper + pub fn decrypt(stream: W, key: Key, iv: IV) -> Result + { + Ok(Self::new(stream, cha::decrypter(key, iv)?)) + } + + + /// Consume into the inner stream + #[inline] pub fn into_inner(self) -> W + { + self.stream + } + + /// Consume into the inner stream and crypter + #[inline] pub fn into_parts(self) -> (W, Crypter) + { + (self.stream, self.crypter) + } + + +} + + +impl Write for Sink +{ + #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { + let n = self.transform(buf)?; + + self.stream.write(&self.buffer[..n]) + + } + #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + let n = self.transform(buf)?; + + self.stream.write_all(&self.buffer[..n]) + } + #[inline] fn flush(&mut self) -> io::Result<()> { + #[cfg(feature="explicit_clear")] self.prune(); + self.buffer.clear(); + + self.stream.flush() + } +} diff --git a/src/stream/source.rs b/src/stream/source.rs new file mode 100644 index 0000000..501ec7f --- /dev/null +++ b/src/stream/source.rs @@ -0,0 +1,117 @@ +//! Syncronous stream `Read` componant. +use super::*; + +/// TODO: Document +//#[derive(Debug)] +pub struct Source +{ + crypter: Crypter, + #[cfg(not(feature="reuse-buffer"))] buffer: BufferVec, // When `reuse-buffer` is enabled, this isn't needed. We re-use the output buffer for the initial read of untransformed data from `stream` and the actual transformation of the read bytes. + + stream: R +} + + +impl fmt::Debug for Source +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + #[cfg(feature="reuse-buffer")] + return write!(f, "Source({:?}, (unbounded buffer cap))", &self.stream); + #[cfg(not(feature="reuse-buffer"))] + return write!(f, "Source({:?}, ({} buffer cap))", &self.stream, self.buffer.capacity()); + } +} + +impl Source +where R: Read +{ + /// The crypter of this instance + #[inline] pub fn crypter(&self) -> &Crypter + { + &self.crypter + } + + /// The crypter of this instance + #[inline] pub fn crypter_mut(&mut self) -> &mut Crypter + { + &mut self.crypter + } + + /// The inner stream + #[inline] pub fn inner(&self) -> &R + { + &self.stream + } + + /// The inner stream + #[inline] pub fn inner_mut(&mut self) -> &mut R + { + &mut self.stream + } + + #[cfg(not(feature="reuse-buffer"))] + /// Grow the inner buffer to fix this size, if needed. + fn grow_to_fit(&mut self, sz: usize) + { + if sz > self.buffer.len() { + self.buffer.resize(sz, 0); + } + } + + #[cfg(not(feature="reuse-buffer"))] + /// Perform the cipher transform on this input to the inner buffer, returning the number of bytes updated. + fn transform(&mut self, bufsz: usize, output: &mut [u8]) -> Result + { + //self.grow_to_fix(output.len()); + //let bufsz = self.stream.read(&mut self.buffer[..bufsz])?; + let n = self.crypter.update(&self.buffer[..bufsz], &mut output[..])?; + let _f = self.crypter.finalize(&mut output[..n])?; + debug_assert_eq!(_f, 0); + + Ok(n) + /* + if buf.len() > self.buffer.len() { + self.buffer.resize(buf.len(), 0); + } + + let n = self.crypter.update(&buf[..], &mut self.buffer[..])?; + let _f = self.crypter.finalize(&mut self.buffer[..n])?; // I don't know if this is needed. + debug_assert_eq!(_f, 0); + + Ok(n)*/ + } + + #[cfg(not(feature="reuse-buffer"))] + /// Clear the internal buffer while keeping it allocated for further use. + /// + /// This does not affect operations at all, all it does is 0 out the left-over temporary buffer from the last operation(s). + #[inline] + pub fn prune(&mut self) + { + #[cfg(feature="explicit_clear")] + { + bytes::explicit_prune(&mut self.buffer[..]); + return; + } + #[cfg(not(feature="explicit_clear"))] + unsafe { + std::ptr::write_bytes(self.buffer.as_mut_ptr(), 0, self.buffer.len()); + } + } +} + +impl Read for Source +where R: Read +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (#[cfg(feature="reuse-buffer")] { + todo!() + }, + #[cfg(not(feature="reuse-buffer"))] { + self.grow_to_fit(buf.len()); + let read = self.stream.read(&mut self.buffer[..buf.len()])?; + Ok(self.transform(read, &mut buf[..read])?) + },).0 + } +}