From b61392e247f39533172a6e6498ce4d8162bc20b2 Mon Sep 17 00:00:00 2001 From: Antoine Rouaze Date: Wed, 1 Apr 2026 16:34:09 -0400 Subject: [PATCH 1/7] feat: Add `output: &mut [MaybeUninit] support This commit adds new methods to support encoding and decoding with a `&mut [MaybeUninit]` as output. It only supports the raw APIs. --- src/bytes.rs | 15 +++---- src/compress.rs | 108 ++++++++++++++++++++++++++++++---------------- src/decompress.rs | 85 +++++++++++++++++++++++++++--------- 3 files changed, 142 insertions(+), 66 deletions(-) diff --git a/src/bytes.rs b/src/bytes.rs index 4f198c6..c03f963 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -1,5 +1,6 @@ use std::convert::TryInto; use std::io; +use std::mem::MaybeUninit; /// Read a u16 in little endian format from the beginning of the given slice. /// This panics if the slice has length less than 2. @@ -28,13 +29,11 @@ pub fn io_read_u32_le(mut rdr: R) -> io::Result { Ok(u32::from_le_bytes(buf)) } -/// Write a u16 in little endian format to the beginning of the given slice. -/// This panics if the slice has length less than 2. -pub fn write_u16_le(n: u16, slice: &mut [u8]) { +pub fn write_u16_le(n: u16, slice: &mut [MaybeUninit]) { assert!(slice.len() >= 2); let bytes = n.to_le_bytes(); - slice[0] = bytes[0]; - slice[1] = bytes[1]; + slice[0].write(bytes[0]); + slice[1].write(bytes[1]); } /// Write a u24 (given as a u32 where the most significant 8 bits are ignored) @@ -58,14 +57,14 @@ pub fn write_u32_le(n: u32, slice: &mut [u8]) { } /// https://developers.google.com/protocol-buffers/docs/encoding#varints -pub fn write_varu64(data: &mut [u8], mut n: u64) -> usize { +pub fn write_varu64(data: &mut [MaybeUninit], mut n: u64) -> usize { let mut i = 0; while n >= 0b1000_0000 { - data[i] = (n as u8) | 0b1000_0000; + data[i].write((n as u8) | 0b1000_0000); n >>= 7; i += 1; } - data[i] = n as u8; + data[i].write(n as u8); i + 1 } diff --git a/src/compress.rs b/src/compress.rs index 1a6638d..1a2f9bb 100644 --- a/src/compress.rs +++ b/src/compress.rs @@ -1,6 +1,7 @@ -use std::fmt; +use std::mem::MaybeUninit; use std::ops::{Deref, DerefMut}; -use std::ptr; +use std::{mem, ptr}; +use std::{fmt, slice}; use crate::bytes; use crate::error::{Error, Result}; @@ -98,8 +99,61 @@ impl Encoder { /// * `output` has length less than `max_compress_len(input.len())`. pub fn compress( &mut self, - mut input: &[u8], + input: &[u8], output: &mut [u8], + ) -> Result { + // SAFETY: `output` is guaranteed to be valid for reads and writes of `output.len()` bytes. + let output: &mut [MaybeUninit] = unsafe { + slice::from_raw_parts_mut( + output.as_mut_ptr() as *mut MaybeUninit, + output.len(), + ) + }; + self.compress_uninit(input, output) + } + + /// Compresses all bytes in `input` into a freshly allocated `Vec`. + /// + /// This is just like the `compress` method, except it allocates a `Vec` + /// with the right size for you. (This is intended to be a convenience + /// method.) + /// + /// This method returns an error under the same circumstances that + /// `compress` does. + pub fn compress_vec(&mut self, input: &[u8]) -> Result> { + let mut buf = Box::new_uninit_slice(max_compress_len(input.len())).into_vec(); + let n = self.compress_uninit(input, &mut buf)?; + buf.truncate(n); + // SAFETY: The buffer is initialized up to the length returned by decompress_uninit. + // decompress_uninit guarantees that the buffer is initialized up to the returned length. + let buf = unsafe { mem::transmute::>, Vec>(buf) }; + Ok(buf) + } + + /// Compresses all bytes in `input` into `output`. + /// + /// `input` can be any arbitrary sequence of bytes. + /// + /// `output` must be large enough to hold the maximum possible compressed + /// size of `input`, which can be computed using `max_compress_len`. + /// + /// On success, this returns the number of bytes written to `output`. + /// + /// The data written to `output` is guaranteed to be initialized to the + /// number of bytes written returned by the method. Data outside that + /// bound is not initialized and reading from it is UB. It's the callee's + /// responsibility to finalize the state of `output`. + /// + /// # Errors + /// + /// This method returns an error in the following circumstances: + /// + /// * The total number of bytes to compress exceeds `2^32 - 1`. + /// * `output` has length less than `max_compress_len(input.len())`. + pub fn compress_uninit( + &mut self, + mut input: &[u8], + output: &mut [MaybeUninit], ) -> Result { match max_compress_len(input.len()) { 0 => { @@ -120,7 +174,7 @@ impl Encoder { if input.is_empty() { // Encodes a varint of 0, denoting the total size of uncompressed // bytes. - output[0] = 0; + output[0].write(0); return Ok(1); } // Write the Snappy header, which is just the total number of @@ -151,21 +205,7 @@ impl Encoder { d = block.d; } Ok(d) - } - /// Compresses all bytes in `input` into a freshly allocated `Vec`. - /// - /// This is just like the `compress` method, except it allocates a `Vec` - /// with the right size for you. (This is intended to be a convenience - /// method.) - /// - /// This method returns an error under the same circumstances that - /// `compress` does. - pub fn compress_vec(&mut self, input: &[u8]) -> Result> { - let mut buf = vec![0; max_compress_len(input.len())]; - let n = self.compress(input, &mut buf)?; - buf.truncate(n); - Ok(buf) } } @@ -173,22 +213,15 @@ struct Block<'s, 'd> { src: &'s [u8], s: usize, s_limit: usize, - dst: &'d mut [u8], + dst: &'d mut [MaybeUninit], d: usize, next_emit: usize, } impl<'s, 'd> Block<'s, 'd> { #[inline(always)] - fn new(src: &'s [u8], dst: &'d mut [u8], d: usize) -> Block<'s, 'd> { - Block { - src: src, - s: 0, - s_limit: src.len(), - dst: dst, - d: d, - next_emit: 0, - } + fn new(src: &'s [u8], dst: &'d mut [MaybeUninit], d: usize) -> Self { + Self { src, s: 0, s_limit: src.len(), dst, d, next_emit: 0 } } #[inline(always)] @@ -346,10 +379,11 @@ impl<'s, 'd> Block<'s, 'd> { } // If we can squeeze the last copy into a copy 1 operation, do it. if len <= 11 && offset <= 2047 { - self.dst[self.d] = (((offset >> 8) as u8) << 5) + let tag = (((offset >> 8) as u8) << 5) | (((len - 4) as u8) << 2) | (Tag::Copy1 as u8); - self.dst[self.d + 1] = offset as u8; + self.dst[self.d].write(tag); + self.dst[self.d + 1].write(offset as u8); self.d += 2; } else { self.emit_copy2(offset, len); @@ -363,7 +397,7 @@ impl<'s, 'd> Block<'s, 'd> { fn emit_copy2(&mut self, offset: usize, len: usize) { debug_assert!(1 <= offset && offset <= 65535); debug_assert!(1 <= len && len <= 64); - self.dst[self.d] = (((len - 1) as u8) << 2) | (Tag::Copy2 as u8); + self.dst[self.d].write((((len - 1) as u8) << 2) | (Tag::Copy2 as u8)); bytes::write_u16_le(offset as u16, &mut self.dst[self.d + 1..]); self.d += 3; } @@ -435,7 +469,7 @@ impl<'s, 'd> Block<'s, 'd> { let len = lit_end - lit_start; let n = len.checked_sub(1).unwrap(); if n <= 59 { - self.dst[self.d] = ((n as u8) << 2) | (Tag::Literal as u8); + self.dst[self.d].write(((n as u8) << 2) | (Tag::Literal as u8)); self.d += 1; if len <= 16 && lit_start + 16 <= self.src.len() { // SAFETY: lit_start is equivalent to self.next_emit, which is @@ -448,16 +482,16 @@ impl<'s, 'd> Block<'s, 'd> { // an extra 32 bytes, which exceeds the 16 byte copy here. let srcp = self.src.as_ptr().add(lit_start); let dstp = self.dst.as_mut_ptr().add(self.d); - ptr::copy_nonoverlapping(srcp, dstp, 16); + ptr::copy_nonoverlapping(srcp, dstp as *mut u8, 16); self.d += len; return; } } else if n < 256 { - self.dst[self.d] = (60 << 2) | (Tag::Literal as u8); - self.dst[self.d + 1] = n as u8; + self.dst[self.d].write((60 << 2) | (Tag::Literal as u8)); + self.dst[self.d + 1].write(n as u8); self.d += 2; } else { - self.dst[self.d] = (61 << 2) | (Tag::Literal as u8); + self.dst[self.d].write((61 << 2) | (Tag::Literal as u8)); bytes::write_u16_le(n as u16, &mut self.dst[self.d + 1..]); self.d += 3; } @@ -469,7 +503,7 @@ impl<'s, 'd> Block<'s, 'd> { // must be guaranteed by the caller and is why this method is unsafe. let srcp = self.src.as_ptr().add(lit_start); let dstp = self.dst.as_mut_ptr().add(self.d); - ptr::copy_nonoverlapping(srcp, dstp, len); + ptr::copy_nonoverlapping(srcp, dstp as *mut u8, len); self.d += len; } } diff --git a/src/decompress.rs b/src/decompress.rs index 2f4add2..2f84565 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -1,4 +1,5 @@ -use std::ptr; +use std::mem::MaybeUninit; +use std::{mem, ptr, slice}; use crate::bytes; use crate::error::{Error, Result}; @@ -77,21 +78,14 @@ impl Decoder { input: &[u8], output: &mut [u8], ) -> Result { - if input.is_empty() { - return Err(Error::Empty); - } - let hdr = Header::read(input)?; - if hdr.decompress_len > output.len() { - return Err(Error::BufferTooSmall { - given: output.len() as u64, - min: hdr.decompress_len as u64, - }); - } - let dst = &mut output[..hdr.decompress_len]; - let mut dec = - Decompress { src: &input[hdr.len..], s: 0, dst: dst, d: 0 }; - dec.decompress()?; - Ok(dec.dst.len()) + // SAFETY: `output` is guaranteed to be valid for reads and writes of `output.len()` bytes. + let output: &mut [MaybeUninit] = unsafe { + slice::from_raw_parts_mut( + output.as_mut_ptr() as *mut MaybeUninit, + output.len(), + ) + }; + self.decompress_uninit(input, output) } /// Decompresses all bytes in `input` into a freshly allocated `Vec`. @@ -103,11 +97,60 @@ impl Decoder { /// This method returns an error under the same circumstances that /// `decompress` does. pub fn decompress_vec(&mut self, input: &[u8]) -> Result> { - let mut buf = vec![0; decompress_len(input)?]; - let n = self.decompress(input, &mut buf)?; + let mut buf: Vec> = + Box::new_uninit_slice(decompress_len(input)?).into_vec(); + let n = self.decompress_uninit(input, &mut buf)?; buf.truncate(n); + // SAFETY: The buffer is initialized up to the length returned by decompress_uninit. + // decompress_uninit guarantees that the buffer is initialized up to the returned length. + let buf = + unsafe { mem::transmute::>, Vec>(buf) }; Ok(buf) } + + /// Decompresses all bytes in `input` into `output`. + /// + /// `input` must be a sequence of bytes returned by a conforming Snappy + /// compressor. + /// + /// The size of `output` must be large enough to hold all decompressed + /// bytes from the `input`. The size required can be queried with the + /// `decompress_len` function. + /// + /// On success, this returns the number of bytes written to `output`. + /// + /// The data written to `output` is guaranteed to be initialized to the + /// number of bytes written returned by the method. Data outside that + /// bound is not initialized and reading from it is UB. It's the callee's + /// responsibility to finalize the state of `output`. + /// + /// # Errors + /// + /// This method returns an error in the following circumstances: + /// + /// * Invalid compressed Snappy data was seen. + /// * The total space required for decompression exceeds `2^32 - 1`. + /// * `output` has length less than `decompress_len(input)`. + pub fn decompress_uninit( + &mut self, + input: &[u8], + output: &mut [MaybeUninit], + ) -> Result { + if input.is_empty() { + return Err(Error::Empty); + } + let hdr = Header::read(input)?; + if hdr.decompress_len > output.len() { + return Err(Error::BufferTooSmall { + given: output.len() as u64, + min: hdr.decompress_len as u64, + }); + } + let dst = &mut output[..hdr.decompress_len]; + let mut dec = Decompress { src: &input[hdr.len..], s: 0, dst, d: 0 }; + dec.decompress()?; + Ok(dec.dst.len()) + } } /// Decompress is the state of the Snappy compressor. @@ -117,7 +160,7 @@ struct Decompress<'s, 'd> { /// The current position in the compressed bytes. s: usize, /// The output buffer to write the decompressed bytes. - dst: &'d mut [u8], + dst: &'d mut [MaybeUninit], /// The current position in the decompressed buffer. d: usize, } @@ -178,7 +221,7 @@ impl<'s, 'd> Decompress<'s, 'd> { let srcp = self.src.as_ptr().add(self.s); let dstp = self.dst.as_mut_ptr().add(self.d); // Hopefully uses SIMD registers for 128 bit load/store. - ptr::copy_nonoverlapping(srcp, dstp, 16); + ptr::copy_nonoverlapping(srcp, dstp as *mut u8, 16); } self.d += len as usize; self.s += len as usize; @@ -220,7 +263,7 @@ impl<'s, 'd> Decompress<'s, 'd> { // is correct. let srcp = self.src.as_ptr().add(self.s); let dstp = self.dst.as_mut_ptr().add(self.d); - ptr::copy_nonoverlapping(srcp, dstp, len as usize); + ptr::copy_nonoverlapping(srcp, dstp as *mut u8, len as usize); } self.s += len as usize; self.d += len as usize; From 77d1e8135a848d1fbdc7f11996bd215b8b6413a3 Mon Sep 17 00:00:00 2001 From: Antoine Rouaze Date: Wed, 1 Apr 2026 16:52:24 -0400 Subject: [PATCH 2/7] Reformat compress.rs --- src/compress.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compress.rs b/src/compress.rs index 1a2f9bb..0df752b 100644 --- a/src/compress.rs +++ b/src/compress.rs @@ -1,7 +1,7 @@ use std::mem::MaybeUninit; use std::ops::{Deref, DerefMut}; -use std::{mem, ptr}; use std::{fmt, slice}; +use std::{mem, ptr}; use crate::bytes; use crate::error::{Error, Result}; @@ -121,12 +121,14 @@ impl Encoder { /// This method returns an error under the same circumstances that /// `compress` does. pub fn compress_vec(&mut self, input: &[u8]) -> Result> { - let mut buf = Box::new_uninit_slice(max_compress_len(input.len())).into_vec(); + let mut buf = + Box::new_uninit_slice(max_compress_len(input.len())).into_vec(); let n = self.compress_uninit(input, &mut buf)?; buf.truncate(n); // SAFETY: The buffer is initialized up to the length returned by decompress_uninit. // decompress_uninit guarantees that the buffer is initialized up to the returned length. - let buf = unsafe { mem::transmute::>, Vec>(buf) }; + let buf = + unsafe { mem::transmute::>, Vec>(buf) }; Ok(buf) } @@ -205,7 +207,6 @@ impl Encoder { d = block.d; } Ok(d) - } } From 555c38166b48b791b7802ac919ccdb858b9eebdf Mon Sep 17 00:00:00 2001 From: Antoine Rouaze Date: Wed, 1 Apr 2026 17:14:47 -0400 Subject: [PATCH 3/7] Support Rust pinned version 1.70.0 --- src/compress.rs | 10 ++++++++-- src/decompress.rs | 10 ++++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/compress.rs b/src/compress.rs index 0df752b..1ad56ed 100644 --- a/src/compress.rs +++ b/src/compress.rs @@ -121,8 +121,14 @@ impl Encoder { /// This method returns an error under the same circumstances that /// `compress` does. pub fn compress_vec(&mut self, input: &[u8]) -> Result> { - let mut buf = - Box::new_uninit_slice(max_compress_len(input.len())).into_vec(); + // FIXME: When supporting Rust >= 1.82.0, replace with: + // let mut buf = + // Box::new_uninit_slice(max_compress_len(input.len())).into_vec(); + let mut buf = Vec::with_capacity(max_compress_len(input.len())); + // SAFETY: Vec::with_capacity above guarantees that the buffer is allocated up to the + // capacity. + unsafe { buf.set_len(max_compress_len(input.len())) } + let n = self.compress_uninit(input, &mut buf)?; buf.truncate(n); // SAFETY: The buffer is initialized up to the length returned by decompress_uninit. diff --git a/src/decompress.rs b/src/decompress.rs index 2f84565..732c75e 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -97,8 +97,14 @@ impl Decoder { /// This method returns an error under the same circumstances that /// `decompress` does. pub fn decompress_vec(&mut self, input: &[u8]) -> Result> { - let mut buf: Vec> = - Box::new_uninit_slice(decompress_len(input)?).into_vec(); + // FIXME: When supporting Rust >= 1.82.0, replace with: + // let mut buf = + // Box::new_uninit_slice(max_compress_len(input.len())).into_vec(); + let mut buf = Vec::with_capacity(decompress_len(input)?); + // SAFETY: Vec::with_capacity above guarantees that the buffer is allocated up to the + // capacity. + unsafe { buf.set_len(decompress_len(input)?) } + let n = self.decompress_uninit(input, &mut buf)?; buf.truncate(n); // SAFETY: The buffer is initialized up to the length returned by decompress_uninit. From 9afff9eb703cf2d7344c161d5f12296ece2455aa Mon Sep 17 00:00:00 2001 From: Antoine Rouaze Date: Thu, 2 Apr 2026 10:22:07 -0400 Subject: [PATCH 4/7] Improve {compress,decompres}_vec vec handling --- src/compress.rs | 20 ++++++-------------- src/decompress.rs | 19 ++++++------------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/src/compress.rs b/src/compress.rs index 1ad56ed..088fa6b 100644 --- a/src/compress.rs +++ b/src/compress.rs @@ -1,7 +1,7 @@ use std::mem::MaybeUninit; use std::ops::{Deref, DerefMut}; use std::{fmt, slice}; -use std::{mem, ptr}; +use std::ptr; use crate::bytes; use crate::error::{Error, Result}; @@ -121,20 +121,12 @@ impl Encoder { /// This method returns an error under the same circumstances that /// `compress` does. pub fn compress_vec(&mut self, input: &[u8]) -> Result> { - // FIXME: When supporting Rust >= 1.82.0, replace with: - // let mut buf = - // Box::new_uninit_slice(max_compress_len(input.len())).into_vec(); let mut buf = Vec::with_capacity(max_compress_len(input.len())); - // SAFETY: Vec::with_capacity above guarantees that the buffer is allocated up to the - // capacity. - unsafe { buf.set_len(max_compress_len(input.len())) } - - let n = self.compress_uninit(input, &mut buf)?; - buf.truncate(n); - // SAFETY: The buffer is initialized up to the length returned by decompress_uninit. - // decompress_uninit guarantees that the buffer is initialized up to the returned length. - let buf = - unsafe { mem::transmute::>, Vec>(buf) }; + let n = self.compress_uninit(input, buf.spare_capacity_mut())?; + + // SAFETY: compress_uninit guarantees all the data up to `n` is + // initialized + unsafe { buf.set_len(n) }; Ok(buf) } diff --git a/src/decompress.rs b/src/decompress.rs index 732c75e..3ac409a 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -1,5 +1,5 @@ use std::mem::MaybeUninit; -use std::{mem, ptr, slice}; +use std::{ptr, slice}; use crate::bytes; use crate::error::{Error, Result}; @@ -97,20 +97,13 @@ impl Decoder { /// This method returns an error under the same circumstances that /// `decompress` does. pub fn decompress_vec(&mut self, input: &[u8]) -> Result> { - // FIXME: When supporting Rust >= 1.82.0, replace with: - // let mut buf = - // Box::new_uninit_slice(max_compress_len(input.len())).into_vec(); let mut buf = Vec::with_capacity(decompress_len(input)?); - // SAFETY: Vec::with_capacity above guarantees that the buffer is allocated up to the - // capacity. - unsafe { buf.set_len(decompress_len(input)?) } + let n = self.decompress_uninit(input, buf.spare_capacity_mut())?; + + // SAFETY: compress_uninit guarantees all the data up to `n` is + // initialized + unsafe { buf.set_len(n); } - let n = self.decompress_uninit(input, &mut buf)?; - buf.truncate(n); - // SAFETY: The buffer is initialized up to the length returned by decompress_uninit. - // decompress_uninit guarantees that the buffer is initialized up to the returned length. - let buf = - unsafe { mem::transmute::>, Vec>(buf) }; Ok(buf) } From 35cc4aead83e288851e9c55d86236c5dea98e537 Mon Sep 17 00:00:00 2001 From: Antoine Rouaze Date: Thu, 2 Apr 2026 10:25:32 -0400 Subject: [PATCH 5/7] Reformat compress.rs & decompress.rs --- src/compress.rs | 6 +++--- src/decompress.rs | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/compress.rs b/src/compress.rs index 088fa6b..52e9817 100644 --- a/src/compress.rs +++ b/src/compress.rs @@ -1,7 +1,7 @@ use std::mem::MaybeUninit; use std::ops::{Deref, DerefMut}; -use std::{fmt, slice}; use std::ptr; +use std::{fmt, slice}; use crate::bytes; use crate::error::{Error, Result}; @@ -123,8 +123,8 @@ impl Encoder { pub fn compress_vec(&mut self, input: &[u8]) -> Result> { let mut buf = Vec::with_capacity(max_compress_len(input.len())); let n = self.compress_uninit(input, buf.spare_capacity_mut())?; - - // SAFETY: compress_uninit guarantees all the data up to `n` is + + // SAFETY: compress_uninit guarantees all the data up to `n` is // initialized unsafe { buf.set_len(n) }; Ok(buf) diff --git a/src/decompress.rs b/src/decompress.rs index 3ac409a..a70fa26 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -100,9 +100,11 @@ impl Decoder { let mut buf = Vec::with_capacity(decompress_len(input)?); let n = self.decompress_uninit(input, buf.spare_capacity_mut())?; - // SAFETY: compress_uninit guarantees all the data up to `n` is + // SAFETY: compress_uninit guarantees all the data up to `n` is // initialized - unsafe { buf.set_len(n); } + unsafe { + buf.set_len(n); + } Ok(buf) } From d0d986e22b99c7538e43e05de9702ec50e42a12f Mon Sep 17 00:00:00 2001 From: Antoine Rouaze Date: Thu, 2 Apr 2026 13:35:25 -0400 Subject: [PATCH 6/7] Update safety comments --- src/compress.rs | 5 ++++- src/decompress.rs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/compress.rs b/src/compress.rs index 52e9817..ec5e2f3 100644 --- a/src/compress.rs +++ b/src/compress.rs @@ -102,7 +102,10 @@ impl Encoder { input: &[u8], output: &mut [u8], ) -> Result { - // SAFETY: `output` is guaranteed to be valid for reads and writes of `output.len()` bytes. + // SAFETY: `output` is guaranteed to be valid for reads and writes of + // `output.len()` bytes. Also compress_uninit does not write any + // uninitialised data into the buffer + // (i.e. via array[0] = MaybeUninit::new()) let output: &mut [MaybeUninit] = unsafe { slice::from_raw_parts_mut( output.as_mut_ptr() as *mut MaybeUninit, diff --git a/src/decompress.rs b/src/decompress.rs index a70fa26..297b18c 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -78,7 +78,10 @@ impl Decoder { input: &[u8], output: &mut [u8], ) -> Result { - // SAFETY: `output` is guaranteed to be valid for reads and writes of `output.len()` bytes. + // SAFETY: `output` is guaranteed to be valid for reads and writes of + // `output.len()` bytes. Also compress_uninit does not write any + // uninitialised data into the buffer + // (i.e. via array[0] = MaybeUninit::new()) let output: &mut [MaybeUninit] = unsafe { slice::from_raw_parts_mut( output.as_mut_ptr() as *mut MaybeUninit, From 27d71a1b5565b80b877562f5fec297365ca75dd1 Mon Sep 17 00:00:00 2001 From: Antoine Rouaze Date: Tue, 7 Apr 2026 12:48:14 -0400 Subject: [PATCH 7/7] Move {compress|decompress}_vec methods --- src/compress.rs | 36 ++++++++++++++++++------------------ src/decompress.rs | 42 +++++++++++++++++++++--------------------- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/compress.rs b/src/compress.rs index ec5e2f3..2205e8c 100644 --- a/src/compress.rs +++ b/src/compress.rs @@ -115,24 +115,6 @@ impl Encoder { self.compress_uninit(input, output) } - /// Compresses all bytes in `input` into a freshly allocated `Vec`. - /// - /// This is just like the `compress` method, except it allocates a `Vec` - /// with the right size for you. (This is intended to be a convenience - /// method.) - /// - /// This method returns an error under the same circumstances that - /// `compress` does. - pub fn compress_vec(&mut self, input: &[u8]) -> Result> { - let mut buf = Vec::with_capacity(max_compress_len(input.len())); - let n = self.compress_uninit(input, buf.spare_capacity_mut())?; - - // SAFETY: compress_uninit guarantees all the data up to `n` is - // initialized - unsafe { buf.set_len(n) }; - Ok(buf) - } - /// Compresses all bytes in `input` into `output`. /// /// `input` can be any arbitrary sequence of bytes. @@ -209,6 +191,24 @@ impl Encoder { } Ok(d) } + + /// Compresses all bytes in `input` into a freshly allocated `Vec`. + /// + /// This is just like the `compress` method, except it allocates a `Vec` + /// with the right size for you. (This is intended to be a convenience + /// method.) + /// + /// This method returns an error under the same circumstances that + /// `compress` does. + pub fn compress_vec(&mut self, input: &[u8]) -> Result> { + let mut buf = Vec::with_capacity(max_compress_len(input.len())); + let n = self.compress_uninit(input, buf.spare_capacity_mut())?; + + // SAFETY: compress_uninit guarantees all the data up to `n` is + // initialized + unsafe { buf.set_len(n) }; + Ok(buf) + } } struct Block<'s, 'd> { diff --git a/src/decompress.rs b/src/decompress.rs index 297b18c..3691bb5 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -91,27 +91,6 @@ impl Decoder { self.decompress_uninit(input, output) } - /// Decompresses all bytes in `input` into a freshly allocated `Vec`. - /// - /// This is just like the `decompress` method, except it allocates a `Vec` - /// with the right size for you. (This is intended to be a convenience - /// method.) - /// - /// This method returns an error under the same circumstances that - /// `decompress` does. - pub fn decompress_vec(&mut self, input: &[u8]) -> Result> { - let mut buf = Vec::with_capacity(decompress_len(input)?); - let n = self.decompress_uninit(input, buf.spare_capacity_mut())?; - - // SAFETY: compress_uninit guarantees all the data up to `n` is - // initialized - unsafe { - buf.set_len(n); - } - - Ok(buf) - } - /// Decompresses all bytes in `input` into `output`. /// /// `input` must be a sequence of bytes returned by a conforming Snappy @@ -155,6 +134,27 @@ impl Decoder { dec.decompress()?; Ok(dec.dst.len()) } + + /// Decompresses all bytes in `input` into a freshly allocated `Vec`. + /// + /// This is just like the `decompress` method, except it allocates a `Vec` + /// with the right size for you. (This is intended to be a convenience + /// method.) + /// + /// This method returns an error under the same circumstances that + /// `decompress` does. + pub fn decompress_vec(&mut self, input: &[u8]) -> Result> { + let mut buf = Vec::with_capacity(decompress_len(input)?); + let n = self.decompress_uninit(input, buf.spare_capacity_mut())?; + + // SAFETY: compress_uninit guarantees all the data up to `n` is + // initialized + unsafe { + buf.set_len(n); + } + + Ok(buf) + } } /// Decompress is the state of the Snappy compressor.