diff --git a/Cargo.toml b/Cargo.toml index 7b3ef5db..ec1e2d05 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,6 @@ pin-project-lite = "0.2" rust_2018_idioms = "deny" missing_debug_implementations = { level = "deny", priority = -1 } # missing_docs = { level = "deny", priority = -1 } + +[workspace.lints.clippy] +undocumented_unsafe_blocks = "deny" diff --git a/crates/compression-codecs/Cargo.toml b/crates/compression-codecs/Cargo.toml index cdeaa332..d51e986f 100644 --- a/crates/compression-codecs/Cargo.toml +++ b/crates/compression-codecs/Cargo.toml @@ -56,5 +56,8 @@ liblzma = { version = "0.4.5", optional = true } memchr = { version = "2", optional = true } zstd-safe = { version = "7", optional = true, default-features = false } +[dev-dependencies] +static_assertions = "1" + [lints] workspace = true diff --git a/crates/compression-codecs/src/zstd/mod.rs b/crates/compression-codecs/src/zstd/mod.rs index c93af350..a81fd5a3 100644 --- a/crates/compression-codecs/src/zstd/mod.rs +++ b/crates/compression-codecs/src/zstd/mod.rs @@ -14,16 +14,20 @@ use std::io; #[repr(transparent)] struct WriteBufferWrapper<'a>(WriteBuffer<'a>); +/// SAFETY: all methods implemented as pass-through safe/unsafe methods on `WriteBuffer`. unsafe impl WriteBuf for WriteBufferWrapper<'_> { fn as_slice(&self) -> &[u8] { self.0.written() } + fn capacity(&self) -> usize { self.0.capacity() } + fn as_mut_ptr(&mut self) -> *mut u8 { self.0.as_mut_ptr() } + unsafe fn filled_until(&mut self, n: usize) { self.0.set_written_and_initialized_len(n); } @@ -35,21 +39,11 @@ trait WriteBufExt { impl WriteBufExt for WriteBuffer<'_> { fn get_out_buf(&mut self) -> OutBuffer<'_, WriteBufferWrapper<'_>> { - { - use std::mem::{align_of, size_of}; - assert_eq!( - size_of::>(), - size_of::>() - ); - assert_eq!( - align_of::>(), - align_of::>() - ); - } - // Pass written_len to avoid overwriting existing data in buffer. let written_len = self.written_len(); + OutBuffer::around_pos( + // SAFETY: zstd_safe expects partially a initialized input and handles is correctly unsafe { &mut *(self as *mut _ as *mut WriteBufferWrapper<'_>) }, written_len, ) @@ -95,3 +89,11 @@ impl OperationExt for Unshared { Ok(self.get_mut().finish(&mut output.get_out_buf(), true)? == 0) } } + +#[cfg(test)] +mod tests { + use super::*; + + static_assertions::assert_eq_size!(WriteBuffer<'static>, WriteBufferWrapper<'static>); + static_assertions::assert_eq_align!(WriteBuffer<'static>, WriteBufferWrapper<'static>); +} diff --git a/crates/compression-core/src/util.rs b/crates/compression-core/src/util.rs index f2073bc3..60448da2 100644 --- a/crates/compression-core/src/util.rs +++ b/crates/compression-core/src/util.rs @@ -1,3 +1,4 @@ +use core::cmp; use core::mem::MaybeUninit; pub const fn _assert_send() {} @@ -18,7 +19,7 @@ impl> PartialBuffer { &self.buffer.as_ref()[..self.index] } - /// Convenient method for `.writen().len()` + /// Convenience method for `.written().len()`. pub fn written_len(&self) -> usize { self.index } @@ -75,14 +76,11 @@ impl + AsMut<[u8]>> From for PartialBuffer { /// Write buffer for compression-codecs. /// -/// Currently it only supports initialized buffer, but will support uninitialized -/// buffer soon. -/// /// # Layout /// /// ```text -/// | buffer | -/// | written and initialized | unwritten but initialized | unwritten and uninitialized +/// | buffer | +/// | written and initialized | unwritten but initialized | unwritten and uninitialized | /// ``` #[derive(Debug)] pub struct WriteBuffer<'a> { @@ -110,6 +108,7 @@ impl<'a> WriteBuffer<'a> { } } + /// Returns entire buffer capacity, including space which is not yet initialized. pub fn capacity(&self) -> usize { self.buffer.len() } @@ -118,6 +117,9 @@ impl<'a> WriteBuffer<'a> { self.buffer.as_mut_ptr() as *mut _ } + /// Returns size of buffer's initialized portion. + /// + /// This will always be at least [`written_len`](Self::written_len). pub fn initialized_len(&self) -> usize { self.initialized } @@ -125,10 +127,13 @@ impl<'a> WriteBuffer<'a> { pub fn written(&self) -> &[u8] { debug_assert!(self.index <= self.initialized); + // SAFETY: slice up to `index` is always initialized unsafe { &*(&self.buffer[..self.index] as *const _ as *const [u8]) } } - /// Convenient method for `.writen().len()` + /// Returns size of buffer's written portion. + /// + /// This will always be at most [`initialized_len`](Self::initialized_len). pub fn written_len(&self) -> usize { self.index } @@ -148,6 +153,7 @@ impl<'a> WriteBuffer<'a> { }); self.initialized = self.buffer.len(); + // SAFETY: slice up to `index` is always initialized unsafe { &mut *(&mut self.buffer[self.index..] as *mut _ as *mut [u8]) } } @@ -202,7 +208,7 @@ impl<'a> WriteBuffer<'a> { debug_assert!(self.index + n <= self.buffer.len()); self.index += n; - self.initialized = self.initialized.max(self.index); + self.initialized = cmp::max(self.initialized, self.index); } /// Convenient function combining [`WriteBuffer::assume_init`] and [`WriteBuffer::advance`], @@ -215,7 +221,7 @@ impl<'a> WriteBuffer<'a> { debug_assert!(n <= self.buffer.len()); self.index = n; - self.initialized = self.initialized.max(n); + self.initialized = cmp::max(self.initialized, n); } pub fn copy_unwritten_from>(&mut self, other: &mut PartialBuffer) -> usize { @@ -223,7 +229,7 @@ impl<'a> WriteBuffer<'a> { // Safety: We will never ever write uninitialized bytes into it let out = unsafe { this.unwritten_mut() }; - let len = out.len().min(input.len()); + let len = cmp::min(out.len(), input.len()); out[..len] .iter_mut()