#[allow(unused_imports)] use alloc::vec::Vec; use crate::fastcpy::slice_copy; /// Returns a Sink implementation appropriate for outputing up to `required_capacity` /// bytes at `vec[offset..offset+required_capacity]`. /// It can be either a `SliceSink` (pre-filling the vec with zeroes if necessary) /// when the `safe-decode` feature is enabled, or `VecSink` otherwise. /// The argument `pos` defines the initial output position in the Sink. #[inline] #[cfg(feature = "frame")] pub fn vec_sink_for_compression( vec: &mut Vec, offset: usize, pos: usize, required_capacity: usize, ) -> SliceSink { return { vec.resize(offset + required_capacity, 0); SliceSink::new(&mut vec[offset..], pos) }; } /// Returns a Sink implementation appropriate for outputing up to `required_capacity` /// bytes at `vec[offset..offset+required_capacity]`. /// It can be either a `SliceSink` (pre-filling the vec with zeroes if necessary) /// when the `safe-decode` feature is enabled, or `VecSink` otherwise. /// The argument `pos` defines the initial output position in the Sink. #[cfg(feature = "frame")] #[inline] pub fn vec_sink_for_decompression( vec: &mut Vec, offset: usize, pos: usize, required_capacity: usize, ) -> SliceSink { return { vec.resize(offset + required_capacity, 0); SliceSink::new(&mut vec[offset..], pos) }; } pub trait Sink { /// Returns a raw ptr to the first unfilled byte of the Sink. Analogous to `[pos..].as_ptr()`. #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] unsafe fn pos_mut_ptr(&mut self) -> *mut u8; /// read byte at position fn byte_at(&mut self, pos: usize) -> u8; /// Pushes a byte to the end of the Sink. #[cfg(feature = "safe-encode")] fn push(&mut self, byte: u8); #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] unsafe fn base_mut_ptr(&mut self) -> *mut u8; fn pos(&self) -> usize; fn capacity(&self) -> usize; #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] unsafe fn set_pos(&mut self, new_pos: usize); #[cfg(feature = "safe-decode")] fn extend_with_fill(&mut self, byte: u8, len: usize); /// Extends the Sink with `data`. fn extend_from_slice(&mut self, data: &[u8]); fn extend_from_slice_wild(&mut self, data: &[u8], copy_len: usize); /// Copies `len` bytes starting from `start` to the end of the Sink. /// # Panics /// Panics if `start` >= `pos`. #[cfg(feature = "safe-decode")] fn extend_from_within(&mut self, start: usize, wild_len: usize, copy_len: usize); #[cfg(feature = "safe-decode")] fn extend_from_within_overlapping(&mut self, start: usize, num_bytes: usize); } /// SliceSink is used as target to de/compress data into a preallocated and possibly uninitialized /// `&[u8]` /// space. /// /// # Handling of Capacity /// Extend methods will panic if there's insufficient capacity left in the Sink. /// /// # Invariants /// - Bytes `[..pos()]` are always initialized. pub struct SliceSink<'a> { /// The working slice, which may contain uninitialized bytes output: &'a mut [u8], /// Number of bytes in start of `output` guaranteed to be initialized pos: usize, } impl<'a> SliceSink<'a> { /// Creates a `Sink` backed by the given byte slice. /// `pos` defines the initial output position in the Sink. /// # Panics /// Panics if `pos` is out of bounds. #[inline] pub fn new(output: &'a mut [u8], pos: usize) -> Self { // SAFETY: Caller guarantees that all elements of `output[..pos]` are initialized. let _ = &mut output[..pos]; // bounds check pos SliceSink { output, pos } } } impl<'a> Sink for SliceSink<'a> { /// Returns a raw ptr to the first unfilled byte of the Sink. Analogous to `[pos..].as_ptr()`. #[inline] #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] unsafe fn pos_mut_ptr(&mut self) -> *mut u8 { self.base_mut_ptr().add(self.pos()) as *mut u8 } /// Pushes a byte to the end of the Sink. #[inline] fn byte_at(&mut self, pos: usize) -> u8 { self.output[pos] } /// Pushes a byte to the end of the Sink. #[inline] #[cfg(feature = "safe-encode")] fn push(&mut self, byte: u8) { self.output[self.pos] = byte; self.pos += 1; } #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] unsafe fn base_mut_ptr(&mut self) -> *mut u8 { self.output.as_mut_ptr() } #[inline] fn pos(&self) -> usize { self.pos } #[inline] fn capacity(&self) -> usize { self.output.len() } #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] #[inline] unsafe fn set_pos(&mut self, new_pos: usize) { debug_assert!(new_pos <= self.capacity()); self.pos = new_pos; } #[inline] #[cfg(feature = "safe-decode")] fn extend_with_fill(&mut self, byte: u8, len: usize) { self.output[self.pos..self.pos + len].fill(byte); self.pos += len; } /// Extends the Sink with `data`. #[inline] fn extend_from_slice(&mut self, data: &[u8]) { self.extend_from_slice_wild(data, data.len()) } #[inline] fn extend_from_slice_wild(&mut self, data: &[u8], copy_len: usize) { assert!(copy_len <= data.len()); slice_copy(data, &mut self.output[self.pos..(self.pos) + data.len()]); self.pos += copy_len; } /// Copies `len` bytes starting from `start` to the end of the Sink. /// # Panics /// Panics if `start` >= `pos`. #[inline] #[cfg(feature = "safe-decode")] fn extend_from_within(&mut self, start: usize, wild_len: usize, copy_len: usize) { self.output.copy_within(start..start + wild_len, self.pos); self.pos += copy_len; } #[inline] #[cfg(feature = "safe-decode")] #[cfg_attr(nightly, optimize(size))] // to avoid loop unrolling fn extend_from_within_overlapping(&mut self, start: usize, num_bytes: usize) { let offset = self.pos - start; for i in start + offset..start + offset + num_bytes { self.output[i] = self.output[i - offset]; } self.pos += num_bytes; } } /// PtrSink is used as target to de/compress data into a preallocated and possibly uninitialized /// `&[u8]` /// space. /// /// #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] pub struct PtrSink { /// The working slice, which may contain uninitialized bytes output: *mut u8, /// Number of bytes in start of `output` guaranteed to be initialized pos: usize, /// Number of bytes in output available cap: usize, } #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] impl PtrSink { /// Creates a `Sink` backed by the given byte slice. /// `pos` defines the initial output position in the Sink. /// # Panics /// Panics if `pos` is out of bounds. #[inline] pub fn from_vec(output: &mut Vec, pos: usize) -> Self { // SAFETY: Bytes behind pointer may be uninitialized. Self { output: output.as_mut_ptr(), pos, cap: output.capacity(), } } } #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] impl Sink for PtrSink { /// Returns a raw ptr to the first unfilled byte of the Sink. Analogous to `[pos..].as_ptr()`. #[inline] #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] unsafe fn pos_mut_ptr(&mut self) -> *mut u8 { self.base_mut_ptr().add(self.pos()) as *mut u8 } /// Pushes a byte to the end of the Sink. #[inline] fn byte_at(&mut self, pos: usize) -> u8 { unsafe { self.output.add(pos).read() } } /// Pushes a byte to the end of the Sink. #[inline] #[cfg(feature = "safe-encode")] fn push(&mut self, byte: u8) { unsafe { self.pos_mut_ptr().write(byte); } self.pos += 1; } #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] unsafe fn base_mut_ptr(&mut self) -> *mut u8 { self.output } #[inline] fn pos(&self) -> usize { self.pos } #[inline] fn capacity(&self) -> usize { self.cap } #[cfg(not(all(feature = "safe-encode", feature = "safe-decode")))] #[inline] unsafe fn set_pos(&mut self, new_pos: usize) { debug_assert!(new_pos <= self.capacity()); self.pos = new_pos; } #[inline] #[cfg(feature = "safe-decode")] fn extend_with_fill(&mut self, _byte: u8, _len: usize) { unreachable!(); } /// Extends the Sink with `data`. #[inline] fn extend_from_slice(&mut self, data: &[u8]) { self.extend_from_slice_wild(data, data.len()) } #[inline] fn extend_from_slice_wild(&mut self, data: &[u8], copy_len: usize) { assert!(copy_len <= data.len()); unsafe { core::ptr::copy_nonoverlapping(data.as_ptr(), self.pos_mut_ptr(), copy_len); } self.pos += copy_len; } /// Copies `len` bytes starting from `start` to the end of the Sink. /// # Panics /// Panics if `start` >= `pos`. #[inline] #[cfg(feature = "safe-decode")] fn extend_from_within(&mut self, _start: usize, _wild_len: usize, _copy_len: usize) { unreachable!(); } #[inline] #[cfg(feature = "safe-decode")] fn extend_from_within_overlapping(&mut self, _start: usize, _num_bytes: usize) { unreachable!(); } } #[cfg(test)] mod tests { #[test] #[cfg(any(feature = "safe-encode", feature = "safe-decode"))] fn test_sink_slice() { use crate::sink::Sink; use crate::sink::SliceSink; use alloc::vec::Vec; let mut data = Vec::new(); data.resize(5, 0); let sink = SliceSink::new(&mut data, 1); assert_eq!(sink.pos(), 1); assert_eq!(sink.capacity(), 5); } }