/// A wrapper around a byte buffer that is incrementally filled and initialized. /// /// This type is a sort of "double cursor". It tracks three regions in the /// buffer: a region at the beginning of the buffer that has been logically /// filled with data, a region that has been initialized at some point but not /// yet logically filled, and a region at the end that may be uninitialized. /// The filled region is guaranteed to be a subset of the initialized region. /// /// In summary, the contents of the buffer can be visualized as: /// /// ```not_rust /// [ capacity ] /// [ filled | unfilled ] /// [ initialized | uninitialized ] /// ``` /// /// It is undefined behavior to de-initialize any bytes from the uninitialized /// region, since it is merely unknown whether this region is uninitialized or /// not, and if part of it turns out to be initialized, it must stay initialized. pubstruct ReadBuf<'a> {
buf: &'a mut [MaybeUninit<u8>],
filled: usize,
initialized: usize,
}
impl<'a> ReadBuf<'a> { /// Creates a new `ReadBuf` from a fully initialized buffer. #[inline] pubfn new(buf: &'a mut [u8]) -> ReadBuf<'a> { let initialized = buf.len(); let buf = unsafe { slice_to_uninit_mut(buf) };
ReadBuf {
buf,
filled: 0,
initialized,
}
}
/// Pointer to where the next byte will be written #[inline] pubfn next_out(&mutself) -> *mut MaybeUninit<u8> { self.buf[self.filled..].as_mut_ptr()
}
/// Pointer to the start of the `ReadBuf` #[inline] pubfn as_mut_ptr(&mutself) -> *mut MaybeUninit<u8> { self.buf.as_mut_ptr()
}
/// Returns the total capacity of the buffer. #[inline] pubfn capacity(&self) -> usize { self.buf.len()
}
/// Returns the length of the filled part of the buffer #[inline] pubfn len(&self) -> usize { self.filled
}
/// Returns true if there are no bytes in this ReadBuf #[inline] pubfn is_empty(&self) -> bool { self.filled == 0
}
/// Returns a shared reference to the filled portion of the buffer. #[inline] pubfn filled(&self) -> &[u8] { let slice = &self.buf[..self.filled]; // safety: filled describes how far into the buffer that the // user has filled with bytes, so it's been initialized. unsafe { slice_assume_init(slice) }
}
/// Returns a mutable reference to the entire buffer, without ensuring that it has been fully /// initialized. /// /// The elements between 0 and `self.len()` are filled, and those between 0 and /// `self.initialized().len()` are initialized (and so can be converted to a `&mut [u8]`). /// /// The caller of this method must ensure that these invariants are upheld. For example, if the /// caller initializes some of the uninitialized section of the buffer, it must call /// [`assume_init`](Self::assume_init) with the number of bytes initialized. /// /// # Safety /// /// The caller must not de-initialize portions of the buffer that have already been initialized. /// This includes any bytes in the region marked as uninitialized by `ReadBuf`. #[inline] pubunsafefn inner_mut(&mutself) -> &mut [MaybeUninit<u8>] { self.buf
}
/// Returns the number of bytes at the end of the slice that have not yet been filled. #[inline] pubfn remaining(&self) -> usize { self.capacity() - self.filled
}
/// Clears the buffer, resetting the filled region to empty. /// /// The number of initialized bytes is not changed, and the contents of the buffer are not modified. #[inline] pubfn clear(&mutself) { self.filled = 0;
}
/// Advances the size of the filled region of the buffer. /// /// The number of initialized bytes is not changed. /// /// # Panics /// /// Panics if the filled region of the buffer would become larger than the initialized region. #[inline] #[track_caller] pubfn advance(&mutself, n: usize) { let new = self.filled.checked_add(n).expect("filled overflow"); self.set_filled(new);
}
/// Sets the size of the filled region of the buffer. /// /// The number of initialized bytes is not changed. /// /// Note that this can be used to *shrink* the filled region of the buffer in addition to growing it (for /// example, by a `AsyncRead` implementation that compresses data in-place). /// /// # Panics /// /// Panics if the filled region of the buffer would become larger than the initialized region. #[inline] #[track_caller] pubfn set_filled(&mutself, n: usize) {
assert!(
n <= self.initialized, "filled must not become larger than initialized"
); self.filled = n;
}
/// Asserts that the first `n` unfilled bytes of the buffer are initialized. /// /// `ReadBuf` assumes that bytes are never de-initialized, so this method does nothing when called with fewer /// bytes than are already known to be initialized. /// /// # Safety /// /// The caller must ensure that `n` unfilled bytes of the buffer have already been initialized. #[inline] pubunsafefn assume_init(&mutself, n: usize) { self.initialized = Ord::max(self.initialized, self.filled + n);
}
#[track_caller] pubfn push(&mutself, byte: u8) {
assert!( self.remaining() >= 1, "read_buf is full ({} bytes)", self.capacity()
);
/// Appends data to the buffer, advancing the written position and possibly also the initialized position. /// /// # Panics /// /// Panics if `self.remaining()` is less than `buf.len()`. #[inline(always)] #[track_caller] pubfn extend(&mutself, buf: &[u8]) {
assert!( self.remaining() >= buf.len(), "buf.len() must fit in remaining()"
);
// using simd here (on x86_64) was not fruitful self.buf[self.filled..][..buf.len()].copy_from_slice(slice_to_uninit(buf));
let end = self.filled + buf.len(); self.initialized = Ord::max(self.initialized, end); self.filled = end;
}
fn copy_match_help<C: Chunk>(&mutself, offset_from_end: usize, length: usize) { let current = self.filled;
let start = current.checked_sub(offset_from_end).expect("in bounds"); let end = start.checked_add(length).expect("in bounds");
// Note also that the referenced string may overlap the current // position; for example, if the last 2 bytes decoded have values // X and Y, a string reference with <length = 5, distance = 2> // adds X,Y,X,Y,X to the output stream.
if end > current { if offset_from_end == 1 { // this will just repeat this value many times let element = self.buf[current - 1]; self.buf[current..][..length].fill(element);
} else { for i in0..length { self.buf[current + i] = self.buf[start + i];
}
}
} else { Self::copy_chunked_within::<C>(self.buf, current, start, end)
}
// safety: we just copied length initialized bytes right beyond self.filled unsafe { self.assume_init(length) };
self.advance(length);
}
#[inline(always)] fn copy_chunked_within<C: Chunk>(
buf: &mut [MaybeUninit<u8>],
current: usize,
start: usize,
end: usize,
) { if (end - start).next_multiple_of(core::mem::size_of::<C>()) <= (buf.len() - current) { unsafe { Self::copy_chunk_unchecked::<C>(
buf.as_ptr().add(start),
buf.as_mut_ptr().add(current),
buf.as_ptr().add(end),
)
}
} else { // a full simd copy does not fit in the output buffer
buf.copy_within(start..end, current);
}
}
/// # Safety /// /// `src` must be safe to perform unaligned reads in `core::mem::size_of::<C>()` chunks until /// `end` is reached. `dst` must be safe to (unalingned) write that number of chunks. #[inline(always)] unsafefn copy_chunk_unchecked<C: Chunk>( mut src: *const MaybeUninit<u8>, mut dst: *mut MaybeUninit<u8>,
end: *const MaybeUninit<u8>,
) { while src < end { let chunk = C::load_chunk(src);
C::store_chunk(dst, chunk);
fn slice_to_uninit(slice: &[u8]) -> &[MaybeUninit<u8>] { unsafe { &*(slice as *const [u8] as *const [MaybeUninit<u8>]) }
}
unsafefn slice_to_uninit_mut(slice: &mut [u8]) -> &mut [MaybeUninit<u8>] {
&mut *(slice as *mut [u8] as *mut [MaybeUninit<u8>])
}
// TODO: This could use `MaybeUninit::slice_assume_init` when it is stable. unsafefn slice_assume_init(slice: &[MaybeUninit<u8>]) -> &[u8] {
&*(slice as *const [MaybeUninit<u8>] as *const [u8])
}
trait Chunk { /// Safety: must be valid to read a `Self::Chunk` value from `from` with an unaligned read. unsafefn load_chunk(from: *const MaybeUninit<u8>) -> Self;
/// Safety: must be valid to write a `Self::Chunk` value to `out` with an unaligned write. unsafefn store_chunk(out: *mut MaybeUninit<u8>, chunk: Self);
}
#[cfg(target_arch = "x86_64")] impl Chunk for core::arch::x86_64::__m512i { #[inline(always)] unsafefn load_chunk(from: *const MaybeUninit<u8>) -> Self { // TODO AVX-512 is effectively unstable. // We cross our fingers that LLVM optimizes this into a vmovdqu32 // // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm512_loadu_si512&expand=3420&ig_expand=4110
core::ptr::read_unaligned(from.cast())
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.