From 404d4511f831d888b37b26fa38958acd7e01a25c Mon Sep 17 00:00:00 2001 From: Avril Date: Thu, 12 Aug 2021 17:06:49 +0100 Subject: [PATCH] Added compile-time distunguishing of internal/external buffer usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `reuse-buffer` feature available if explicitly opting into it. Buffers can be moved from internal<->external buffers. By default, internal buffers are used when `reuse-buffer` is disabled, and external ones are used when it is enabled. Fortune for chacha20stream's current commit: Future small blessing − 末小吉 --- src/lib.rs | 6 ++ src/stream/source.rs | 170 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 148 insertions(+), 28 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0833260..d7cf905 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,12 @@ fn decrypt_message_to(output: &mut W, encrypted: &[u8], key: #[macro_use] mod ext; #[allow(unused_imports)] use ext::*; +mod private +{ + /// This trait cannot be subtraited by downstream crates. + pub trait Sealed{} +} + pub mod key; mod cha; mod stream; diff --git a/src/stream/source.rs b/src/stream/source.rs index 501ec7f..1bbaf26 100644 --- a/src/stream/source.rs +++ b/src/stream/source.rs @@ -1,29 +1,129 @@ //! Syncronous stream `Read` componant. use super::*; +/// How buffers are used. +pub trait BufferKind : private::Sealed +{ + type InternalBuffer; + + fn create_buffer(cap: usize) -> Self::InternalBuffer; + + fn buffer_len(source: &Source) -> usize; + fn buffer_cap(source: &Source) -> usize; + + fn buffer_bytes_mut(source: &mut Self::InternalBuffer) -> &'_ mut [u8]; + fn buffer_bytes(source: &Self::InternalBuffer) -> &'_ [u8]; + + fn buffer_resize(source: &mut Source, to: usize); +} + +/// Use struct-internal buffer for `Read`s +#[derive(Debug)] +pub struct UseBufferInternal; +/// Reuse the output buffer for `Read`s +#[derive(Debug)] +pub struct UseBufferExternal; + +impl private::Sealed for UseBufferInternal{} +impl BufferKind for UseBufferInternal +{ + type InternalBuffer = BufferVec; + + #[inline] fn create_buffer(cap: usize) -> Self::InternalBuffer { + if cap == 0 { + BufferVec::new() + } else { + BufferVec::with_capacity(cap) + } + } + + #[inline(always)] fn buffer_cap(source: &Source) -> usize { + source.buffer.capacity() + } + #[inline(always)] fn buffer_len(source: &Source) -> usize { + source.buffer.len() + } + + #[inline(always)] fn buffer_bytes_mut(source: &mut Self::InternalBuffer) -> &'_ mut [u8] + { + &mut source[..] + } + #[inline(always)] fn buffer_bytes(source: &Self::InternalBuffer) -> &'_ [u8] + { + &source[..] + } + + #[inline(always)] fn buffer_resize(source: &mut Source, to: usize) + { + source.buffer.resize(to, 0); + } +} +impl private::Sealed for UseBufferExternal{} +impl BufferKind for UseBufferExternal +{ + type InternalBuffer = (); + + // -- always used -- + + #[inline(always)] fn create_buffer(_: usize) -> Self::InternalBuffer { + () + } + #[inline(always)] fn buffer_cap(_: &Source) -> usize { + 0 + } + + // -- conditional -- + + #[cold] + #[inline(never)] fn buffer_len(_: &Source) -> usize { + panic!("Phantom buffer length cannot be checked") + } + #[cold] + #[inline(never)] fn buffer_bytes_mut(_: &mut Self::InternalBuffer) -> &'_ mut [u8] + { + panic!("Cannot mutref non-existent ibuf.") + } + #[cold] + #[inline(never)] fn buffer_bytes(_: &Self::InternalBuffer) -> &'_ [u8] + { + panic!("Cannot ref non-existent ibuf.") + } + #[cold] + #[inline(never)] fn buffer_resize(_: &mut Source, _: usize) + { + panic!("Cannot resize non-existent ibuf.") + } +} + +#[cfg(not(feature="reuse-buffer"))] +pub type DefaultBuffer = UseBufferInternal; +#[cfg(feature="reuse-buffer")] +pub type DefaultBuffer = UseBufferExternal; + /// TODO: Document //#[derive(Debug)] -pub struct Source +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. + buffer: Buffer::InternalBuffer, // 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 +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()); + use std::any::type_name; + write!(f, "Source", type_name::(), type_name::())?; + match K::buffer_cap(self) { + 0 => write!(f, "({:?}, (unbounded buffer cap))", &self.stream), + cap => write!(f, "({:?}, ({} buffer cap))", &self.stream, cap), + } } } -impl Source +impl Source where R: Read { /// The crypter of this instance @@ -50,39 +150,26 @@ where R: Read &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); + if sz > K::buffer_len(self) { + K::buffer_resize(self, sz); } } - #[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 n = self.crypter.update(& K::buffer_bytes(&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). @@ -91,17 +178,44 @@ where R: Read { #[cfg(feature="explicit_clear")] { - bytes::explicit_prune(&mut self.buffer[..]); + bytes::explicit_prune(K::buffer_bytes_mut(self)); return; } #[cfg(not(feature="explicit_clear"))] unsafe { - std::ptr::write_bytes(self.buffer.as_mut_ptr(), 0, self.buffer.len()); + std::ptr::write_bytes(K::buffer_bytes_mut(&mut self.buffer).as_mut_ptr(), 0, K::buffer_len(self)); + } + } + +} + +impl Source +{ + /// Convert this instance to use external buffer (instead of internal.) + pub fn with_reused_buffer(self) -> Source + { + Source { + buffer: UseBufferInternal::create_buffer(UseBufferExternal::buffer_cap(&self)), + crypter: self.crypter, + stream: self.stream, + } + } +} + +impl Source +{ + /// Convert this instance to use external buffer (instead of internal.) + pub fn with_reused_buffer(self) -> Source + { + Source { + buffer: UseBufferExternal::create_buffer(UseBufferInternal::buffer_cap(&self)), + crypter: self.crypter, + stream: self.stream, } } } -impl Read for Source +impl Read for Source where R: Read { fn read(&mut self, buf: &mut [u8]) -> io::Result {