Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
3 changes: 3 additions & 0 deletions crates/compression-codecs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
26 changes: 14 additions & 12 deletions crates/compression-codecs/src/zstd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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::<WriteBuffer<'static>>(),
size_of::<WriteBufferWrapper<'static>>()
);
assert_eq!(
align_of::<WriteBuffer<'static>>(),
align_of::<WriteBufferWrapper<'static>>()
);
}

// 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,
)
Expand Down Expand Up @@ -95,3 +89,11 @@ impl<C: Operation> OperationExt for Unshared<C> {
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>);
}
26 changes: 16 additions & 10 deletions crates/compression-core/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::cmp;
use core::mem::MaybeUninit;

pub const fn _assert_send<T: Send>() {}
Expand All @@ -18,7 +19,7 @@ impl<B: AsRef<[u8]>> PartialBuffer<B> {
&self.buffer.as_ref()[..self.index]
}

/// Convenient method for `.writen().len()`
/// Convenience method for `.written().len()`.
pub fn written_len(&self) -> usize {
self.index
}
Expand Down Expand Up @@ -75,14 +76,11 @@ impl<B: AsRef<[u8]> + AsMut<[u8]>> From<B> for PartialBuffer<B> {

/// 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> {
Expand Down Expand Up @@ -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()
}
Expand All @@ -118,17 +117,23 @@ 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
}

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
}
Expand All @@ -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]) }
}

Expand Down Expand Up @@ -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`],
Expand All @@ -215,15 +221,15 @@ 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<C: AsRef<[u8]>>(&mut self, other: &mut PartialBuffer<C>) -> usize {
fn inner(this: &mut WriteBuffer<'_>, input: &[u8]) -> usize {
// 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()
Expand Down
Loading