#![allow(clippy::assert_is_empty)]
use std::{cmp, mem, num::NonZeroUsize, ptr, ptr::NonNull, slice, sync::atomic::Ordering::Relaxed};
use crate::{info::Info, info::Kind, stext::StorageVTable, stvec};
#[cfg(target_endian = "little")]
#[repr(C)]
pub(crate) struct Storage {
pub(crate) offset: NonZeroUsize,
pub(crate) ptr: *mut u8,
pub(crate) len: usize,
}
#[cfg(target_endian = "big")]
#[repr(C)]
pub(crate) struct Storage {
pub(crate) len: usize,
pub(crate) ptr: *mut u8,
pub(crate) offset: NonZeroUsize,
}
const KIND_STEXT: usize = 0b00;
const KIND_VEC: usize = 0b01;
const KIND_INLINE: usize = 0b10;
const KIND_STATIC: usize = 0b11;
const KIND_MASK: usize = 0b11;
const KIND_OFFSET_BITS: usize = 2;
pub(crate) const MIN_CAPACITY: usize = 128 - crate::METADATA_SIZE;
const INLINE_LEN_MASK: usize = 0b1111_1100;
#[cfg(target_endian = "little")]
const INLINE_DATA_OFFSET: isize = 1;
#[cfg(target_endian = "big")]
const INLINE_DATA_OFFSET: isize = 0;
#[cfg(target_pointer_width = "64")]
pub(crate) const INLINE_CAP: usize = 3 * 8 - 1;
#[cfg(target_pointer_width = "32")]
pub(crate) const INLINE_CAP: usize = 3 * 4 - 1;
const PTR_INLINE: NonZeroUsize = NonZeroUsize::new(KIND_INLINE).unwrap();
const PTR_STATIC: NonZeroUsize = NonZeroUsize::new(KIND_STATIC).unwrap();
const _: () = assert!(mem::align_of::<stvec::Header>() > KIND_MASK);
#[cfg(all(target_endian = "little", target_pointer_width = "64"))]
mod inline_le64 {
use std::{num::NonZeroUsize, ptr};
use super::{INLINE_CAP, KIND_INLINE, KIND_OFFSET_BITS, Storage};
const _: () = assert!(INLINE_CAP == 23 && super::INLINE_DATA_OFFSET == 1);
#[inline]
unsafe fn rd64(src: *const u8) -> u64 {
src.cast::<u64>().read_unaligned()
}
#[inline]
unsafe fn rd32(src: *const u8) -> u64 {
u64::from(src.cast::<u32>().read_unaligned())
}
#[inline]
pub(super) unsafe fn load_exact(src: *const u8, len: usize) -> [u64; 3] {
match len {
0 => [0, 0, 0],
1..=3 => {
let mid = len / 2;
let w = u64::from(*src)
| u64::from(*src.add(mid)) << (mid * 8)
| u64::from(*src.add(len - 1)) << ((len - 1) * 8);
[w, 0, 0]
}
4..=8 => [rd32(src) | rd32(src.add(len - 4)) << ((len - 4) * 8), 0, 0],
9..=16 => [rd64(src), rd64(src.add(len - 8)) >> ((16 - len) * 8), 0],
_ => [
rd64(src),
rd64(src.add(8)),
rd64(src.add(len - 8)) >> ((24 - len) * 8),
],
}
}
#[inline]
pub(super) unsafe fn load_window(src: *const u8, len: usize) -> [u64; 3] {
[
rd64(src) & mask(len),
rd64(src.add(8)) & mask(len.saturating_sub(8)),
(rd64(src.add(15)) >> 8) & mask(len.saturating_sub(16)),
]
}
#[inline]
fn mask(n: usize) -> u64 {
((1u128 << (n.min(8) * 8)) - 1) as u64
}
#[inline]
pub(super) fn build(d0: u64, d1: u64, d2: u64, len: usize) -> Storage {
let w0 = (KIND_INLINE | (len << KIND_OFFSET_BITS)) as u64 | (d0 << 8);
let w1 = (d0 >> 56) | (d1 << 8);
let w2 = (d1 >> 56) | (d2 << 8);
Storage {
offset: unsafe { NonZeroUsize::new_unchecked(w0 as usize) },
ptr: ptr::without_provenance_mut(w1 as usize),
len: w2 as usize,
}
}
}
impl Storage {
#[inline]
pub(crate) const fn empty() -> Storage {
Storage {
ptr: ptr::null_mut(),
len: 0,
offset: PTR_INLINE,
}
}
#[inline]
pub(crate) const fn from_static(bytes: &'static [u8]) -> Storage {
let ptr = bytes.as_ptr().cast_mut();
Storage {
ptr,
len: bytes.len(),
offset: PTR_STATIC,
}
}
#[inline]
pub(crate) fn from_stext(addr: *const u8, len: usize, vtable: *const StorageVTable) -> Storage {
Storage {
len,
ptr: addr.cast_mut(),
offset: unsafe { NonZeroUsize::new_unchecked(vtable.expose_provenance()) },
}
}
#[inline]
pub(crate) fn from_slice(src: &[u8]) -> Storage {
if src.len() <= INLINE_CAP {
unsafe { Storage::from_ptr_inline(src.as_ptr(), src.len()) }
} else {
Storage::from_slice_with_capacity(src.len(), src)
}
}
#[inline]
fn from_slice_with_capacity(cap: usize, src: &[u8]) -> Storage {
let shared = stvec::Header::create(cap, src);
unsafe { Storage::from_shared_vec(shared, stvec::METADATA_SIZE, src.len()) }
}
#[inline]
pub(crate) unsafe fn from_shared_vec(
shared: NonNull<stvec::Header>,
offset: usize,
len: usize,
) -> Storage {
Storage {
len,
ptr: shared.as_ptr().cast::<u8>().add(offset),
offset: NonZeroUsize::new_unchecked(shared.as_ptr().addr() | KIND_VEC),
}
}
#[inline]
pub(crate) unsafe fn from_ptr_inline(src: *const u8, len: usize) -> Storage {
#[cfg(all(target_endian = "little", target_pointer_width = "64"))]
{
let [d0, d1, d2] = inline_le64::load_exact(src, len);
inline_le64::build(d0, d1, d2, len)
}
#[cfg(not(all(target_endian = "little", target_pointer_width = "64")))]
{
let mut st = Storage {
ptr: ptr::null_mut(),
len: 0,
offset: PTR_INLINE,
};
let dst = st.inline_ptr();
ptr::copy(src, dst, len);
st.set_inline_len(len);
st
}
}
#[inline]
pub(crate) unsafe fn from_ptr_inline_prefix(
src: *const u8,
avail: usize,
len: usize,
) -> Storage {
debug_assert!(len <= avail && len <= INLINE_CAP);
#[cfg(all(target_endian = "little", target_pointer_width = "64"))]
if avail >= INLINE_CAP {
let [d0, d1, d2] = inline_le64::load_window(src, len);
return inline_le64::build(d0, d1, d2, len);
}
let _ = avail;
Storage::from_ptr_inline(src, len)
}
#[inline]
pub(crate) fn as_ref(&self) -> &[u8] {
match self.kind() {
KIND_INLINE => unsafe {
slice::from_raw_parts(self.inline_ptr_ro(), self.inline_len())
},
KIND_STEXT => unsafe { slice::from_raw_parts(self.as_ptr(), self.len()) },
_ => unsafe { slice::from_raw_parts(self.ptr, self.len) },
}
}
#[inline]
pub(crate) unsafe fn as_ptr(&self) -> *const u8 {
match self.kind() {
KIND_INLINE => self.inline_ptr_ro(),
KIND_STEXT => ((*self.st_vtable()).as_ptr)(self.st_addr(), self.st_len()),
_ => self.ptr,
}
}
#[inline]
pub(crate) fn len(&self) -> usize {
match self.kind() {
KIND_STEXT => unsafe { ((*self.st_vtable()).len)(self.st_addr(), self.st_len()) },
KIND_INLINE => self.inline_len(),
_ => self.len,
}
}
pub(crate) fn get_u8(&mut self) -> u8 {
let data = self.as_ref();
assert!(!data.is_empty());
let ret = data[0];
unsafe { self.set_start(1) };
ret
}
#[inline]
unsafe fn inline_ptr(&mut self) -> *mut u8 {
(ptr::from_mut::<Storage>(self).cast::<u8>()).offset(INLINE_DATA_OFFSET)
}
#[inline]
unsafe fn inline_ptr_ro(&self) -> *const u8 {
ptr::from_ref::<Storage>(self)
.cast::<u8>()
.offset(INLINE_DATA_OFFSET)
}
#[inline]
fn inline_len(&self) -> usize {
(self.offset.get() & INLINE_LEN_MASK) >> KIND_OFFSET_BITS
}
#[inline]
pub(crate) fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub(crate) fn capacity(&self) -> usize {
let kind = self.kind();
match kind {
KIND_VEC => unsafe {
stvec::Header::capacity(self.shared_vec()) + stvec::METADATA_SIZE
- self.vec_offset()
},
KIND_STEXT => unsafe { ((*self.st_vtable()).len)(self.st_addr(), self.st_len()) },
KIND_INLINE => INLINE_CAP,
_ => self.len,
}
}
#[inline]
pub(crate) fn split_off(&mut self, at: usize, create_inline: bool) -> Storage {
let other = unsafe {
if create_inline && self.len() - at <= INLINE_CAP {
Storage::from_ptr_inline(self.as_ptr().add(at), self.len() - at)
} else {
let mut other = self.shallow_clone();
other.set_start(at);
other
}
};
unsafe {
if create_inline && at <= INLINE_CAP {
*self = Storage::from_ptr_inline_prefix(self.as_ptr(), self.len(), at);
} else {
self.set_end(at);
}
}
other
}
pub(crate) fn slice(&self, begin: usize, end: usize) -> Storage {
debug_assert!(begin <= end && end <= self.len());
if end - begin <= INLINE_CAP || (self.kind() == KIND_STEXT && end - begin < self.len()) {
Storage::from_slice(&self.as_ref()[begin..end])
} else {
unsafe {
let mut other = self.shallow_clone();
other.set_end(end);
other.set_start(begin);
other
}
}
}
#[inline]
pub(crate) fn split_to(&mut self, at: usize) -> Storage {
let other = unsafe {
if at <= INLINE_CAP {
Storage::from_ptr_inline_prefix(self.as_ptr(), self.len(), at)
} else {
let mut other = self.shallow_clone();
other.set_end(at);
other
}
};
unsafe {
self.set_start(at);
}
other
}
pub(crate) fn truncate(&mut self, len: usize) {
unsafe {
if len <= self.len() {
if len < INLINE_CAP {
*self = Storage::from_ptr_inline_prefix(self.as_ptr(), self.len(), len);
} else {
self.set_len(len);
}
}
}
}
pub(crate) fn trimdown(&mut self) {
let kind = self.kind();
if !(kind == KIND_INLINE || kind == KIND_STATIC) {
let capacity = if kind == KIND_VEC {
unsafe { stvec::Header::capacity(self.shared_vec()) }
} else {
self.capacity()
};
if self.len() <= INLINE_CAP {
*self = unsafe { Storage::from_ptr_inline(self.as_ptr(), self.len()) };
} else if capacity - self.len() >= 64 {
*self = Storage::from_slice_with_capacity(self.len(), self.as_ref());
}
}
}
#[inline]
pub(crate) unsafe fn set_len(&mut self, len: usize) {
let kind = self.kind();
match kind {
KIND_VEC => {
assert!(len <= self.len);
self.len = len;
}
KIND_STEXT => {
if len != self.len() {
assert!(len <= self.capacity());
*self = Storage::from_slice(&self.as_ref()[..len]);
}
}
KIND_INLINE => self.set_inline_len(len),
_ => {
assert!(len <= self.len);
self.len = len;
}
}
}
#[inline]
fn set_inline_len(&mut self, len: usize) {
debug_assert!(len <= INLINE_CAP);
self.offset = unsafe {
NonZeroUsize::new_unchecked(
self.offset.get() & !INLINE_LEN_MASK | (len << KIND_OFFSET_BITS),
)
};
}
pub(crate) unsafe fn set_start(&mut self, start: usize) {
if start == 0 {
return;
}
assert!(
start <= self.len(),
"cannot advance past the end of the buffer, cnt:{start} len:{}",
self.len()
);
match self.kind() {
KIND_VEC => {
self.ptr = self.ptr.add(start);
self.len -= start;
}
KIND_INLINE => {
let len = self.inline_len();
if len <= start {
self.set_inline_len(0);
} else {
let new_len = len - start;
let dst = self.inline_ptr();
let src = (dst.cast_const()).add(start);
ptr::copy(src, dst, new_len);
self.set_inline_len(new_len);
}
}
KIND_STEXT => {
*self = Storage::from_slice(&self.as_ref()[start..]);
}
_ => {
self.len -= start;
self.ptr = self.ptr.add(start);
}
}
}
pub(crate) unsafe fn set_end(&mut self, end: usize) {
match self.kind() {
KIND_VEC => {
self.len = cmp::min(self.len, end);
}
KIND_INLINE => {
assert!(end <= INLINE_CAP);
let new_len = cmp::min(self.inline_len(), end);
self.set_inline_len(new_len);
}
KIND_STEXT => {
if end < self.len() {
*self = Storage::from_slice(&self.as_ref()[..end]);
}
}
_ => {
assert!(end <= self.len);
self.len = end;
}
}
}
#[inline]
unsafe fn shallow_clone(&self) -> Storage {
let kind = self.kind();
if kind == KIND_INLINE || kind == KIND_STATIC {
Storage { ..*self }
} else if kind == KIND_VEC {
let shared = self.shared_vec();
let ref_cnt = (*shared).ref_count.fetch_add(1, Relaxed);
if ref_cnt == u32::MAX {
stvec::abort();
}
Storage { ..*self }
} else {
if let Some((addr, len)) = ((*self.st_vtable()).clone)(self.st_addr(), self.st_len()) {
Storage {
len,
ptr: addr.cast_mut(),
offset: self.offset,
}
} else {
Storage::from_slice(self.as_ref())
}
}
}
#[inline]
pub(crate) fn is_inline(&self) -> bool {
self.kind() == KIND_INLINE
}
pub(crate) fn try_into_vec(self) -> Result<stvec::StorageVec, Storage> {
if self.kind() == KIND_VEC {
let offset = self.vec_offset();
let st =
unsafe { stvec::StorageVec::from_unique_view(self.shared_vec(), offset, self.len) };
if let Some(st) = st {
mem::forget(self);
return Ok(st);
}
}
Err(self)
}
#[inline]
fn shared_vec(&self) -> *mut stvec::Header {
#[allow(clippy::cast_ptr_alignment)]
self.ptr
.with_addr(self.offset.get() & !KIND_MASK)
.cast::<stvec::Header>()
}
#[inline]
fn vec_offset(&self) -> usize {
self.ptr.addr() - (self.offset.get() & !KIND_MASK)
}
#[inline]
fn st_vtable(&self) -> *const StorageVTable {
ptr::with_exposed_provenance::<StorageVTable>(self.offset.get())
}
#[inline]
fn st_addr(&self) -> *const u8 {
self.ptr.cast_const()
}
#[inline]
fn st_len(&self) -> usize {
self.len
}
#[inline]
fn kind(&self) -> usize {
self.offset.get() & KIND_MASK
}
pub(crate) fn info(&self) -> Info {
let kind = self.kind();
let (id, refs, capacity) = unsafe {
if kind == KIND_VEC {
let ptr = self.shared_vec();
(
ptr as usize,
(*ptr).ref_count.load(Relaxed),
stvec::Header::capacity(ptr) + stvec::METADATA_SIZE,
)
} else {
(0, 0, 0)
}
};
Info {
id,
refs,
capacity,
kind: Kind::from_raw(kind),
}
}
}
unsafe impl Send for Storage {}
unsafe impl Sync for Storage {}
impl Clone for Storage {
#[inline]
fn clone(&self) -> Storage {
unsafe { self.shallow_clone() }
}
}
impl Drop for Storage {
#[inline]
fn drop(&mut self) {
match self.kind() {
KIND_VEC => {
stvec::release_shared_vec(self.shared_vec());
}
KIND_STEXT => unsafe {
((*self.st_vtable()).drop)(self.st_addr(), self.st_len());
},
_ => {}
}
}
}
impl Kind {
fn from_raw(n: usize) -> Kind {
match n {
KIND_STEXT => Kind::StExt,
KIND_INLINE => Kind::Inline,
KIND_STATIC => Kind::Static,
_ => Kind::Vec,
}
}
}
#[cfg(test)]
mod tests {
use crate::*;
use std::ptr;
use super::{KIND_INLINE, Storage};
const LONG: &[u8] =
b"mary had a little lamb, little lamb, little lamb, little lamb, little lamb, little lamb \
mary had a little lamb, little lamb, little lamb, little lamb, little lamb, little lamb \
mary had a little lamb, little lamb, little lamb, little lamb, little lamb, little lamb";
#[test]
#[cfg(target_pointer_width = "32")]
fn vec_view_offset_over_1gib() {
const GIB: usize = 1 << 30;
let mut buf = BytesMut::with_capacity(GIB + 256);
unsafe { buf.advance_mut(GIB + 128) };
let mut b = buf.freeze();
let head = b.split_to(GIB);
assert_eq!(head.len(), GIB);
assert_eq!(b.len(), 128);
assert_eq!(b.storage.info().id, head.storage.info().id);
assert_eq!(
b.storage.capacity(),
b.storage.info().capacity - stvec::METADATA_SIZE - GIB
);
drop(head);
let b2 = b.clone();
drop(b);
drop(b2);
}
#[test]
fn trimdown() {
let mut b = Bytes::from(LONG.to_vec());
assert_eq!(b.storage.capacity(), 263);
unsafe { b.storage.set_len(68) };
assert_eq!(b.len(), 68);
assert_eq!(&b[..], &LONG[..68]);
assert_eq!(b.storage.capacity(), 263);
b.trimdown();
assert_eq!(&b[..], &LONG[..68]);
assert_eq!(b.storage.capacity(), 68);
unsafe { b.storage.set_len(super::INLINE_CAP) };
assert_eq!(&b[..], &LONG[..super::INLINE_CAP]);
b.trimdown();
assert!(b.is_inline());
let src = Bytes::from(LONG.to_vec());
let mut b = src.slice(LONG.len() - 40..);
assert!(b.storage.capacity() - b.len() < 64);
b.trimdown();
assert_eq!(&b[..], &LONG[LONG.len() - 40..]);
assert_eq!(b.storage.capacity(), 40);
assert!(!src.as_ref().as_ptr_range().contains(&b.as_ptr()));
}
#[test]
#[allow(clippy::unnecessary_fallible_conversions)]
fn bytes_mut() {
let bv = BytesMut::copy_from_slice(LONG);
assert_eq!(bv.capacity(), 263);
assert_eq!(bv.len(), 263);
assert_eq!(bv.as_ref().len(), 263);
assert_eq!(bv.as_ref(), LONG);
assert_eq!(&bv[..], LONG);
let sl: &[u8] = &[];
let bv = BytesMut::copy_from_slice(sl);
assert_eq!(bv.capacity(), 0);
assert_eq!(bv.len(), 0);
assert_eq!(bv.as_ref().len(), 0);
assert_eq!(bv.as_ref(), sl);
assert_eq!(&bv[..], sl);
let mut bv = BytesMut::copy_from_slice(&b"hello"[..]);
assert_eq!(bv.capacity(), 5);
bv.reserve_exact(123);
assert_eq!(bv.capacity(), 128);
assert_eq!(bv.len(), 5);
assert_eq!(bv.as_ref(), &b"hello"[..]);
let mut bv = BytesMut::copy_from_slice(&b"hello"[..]);
assert_eq!(bv.capacity(), 5);
assert_eq!(bv.len(), 5);
assert_eq!(bv.as_ref().len(), 5);
assert_eq!(bv.as_ref()[0], b"h"[0]);
assert_eq!(bv.remaining_mut(), 0);
bv.reserve(1);
assert_eq!(bv.capacity(), 10);
assert_eq!(bv.remaining_mut(), 5);
bv.put_u8(b" "[0]);
assert_eq!(bv.as_ref(), &b"hello "[..]);
assert_eq!(bv.remaining_mut(), 4);
bv.reserve(5);
assert_eq!(bv.capacity(), 12);
assert_eq!(bv.remaining_mut(), 6);
bv.put_slice(b"world");
assert_eq!(bv, "hello world");
bv.advance_to(6);
assert_eq!(bv, "world");
assert_eq!(bv.remaining_mut(), 1);
let bv = BytesMut::copy_from_slice(&b"hello world"[..]);
let b = Bytes::from(bv);
assert_eq!(b, "hello world");
let mut bv = BytesMut::with_capacity(0);
bv.extend_from_slice(b"hello world.");
bv.extend_from_slice(b"hello world.");
bv.extend_from_slice(b"hello world.");
bv.extend_from_slice(b"hello world.");
let p1 = unsafe { bv.storage.as_ptr() as usize };
bv.advance(48);
assert!(bv.is_empty());
assert_eq!(bv.capacity(), 0);
bv.reserve(48);
assert!(bv.is_empty());
assert_eq!(bv.capacity(), 48);
let p2 = unsafe { bv.storage.as_ptr() as usize };
assert_eq!(p1, p2);
}
#[test]
fn ext_storage() {
let long: std::sync::Arc<str> = std::str::from_utf8(LONG).unwrap().into();
let mut b = Bytes::from_ext(long);
assert_eq!(b.info().kind, info::Kind::StExt);
assert_eq!(b.storage.capacity(), LONG.len());
b.trimdown();
b.truncate(LONG.len() + 10);
b.advance(0);
assert_eq!(b.info().kind, info::Kind::StExt);
assert_eq!(&b[..], LONG);
let full = b.slice(..);
assert_eq!(full.info().kind, info::Kind::StExt);
assert_eq!(&full[..], LONG);
let len = LONG.len();
let tail = b.slice(len - 40..);
assert_eq!(tail.info().kind, info::Kind::Vec);
assert_eq!(&tail[..], &LONG[len - 40..]);
assert!(tail.info().capacity < 100, "{}", tail.info().capacity);
let mid = b.slice(10..len - 10);
assert_eq!(&mid[..], &LONG[10..len - 10]);
assert!(
mid.info().capacity < len + stvec::METADATA_SIZE,
"{}",
mid.info().capacity
);
assert_eq!(b.info().kind, info::Kind::StExt);
b.truncate(100);
assert_eq!(&b[..], &LONG[..100]);
assert_eq!(b.info().kind, info::Kind::Vec);
}
#[test]
fn static_storage() {
let mut b = Bytes::from_static(LONG);
assert_eq!(b.info().kind, info::Kind::Static);
assert_eq!(b.storage.capacity(), LONG.len());
b.truncate(100);
assert_eq!(&b[..], &LONG[..100]);
assert_eq!(b.info().kind, info::Kind::Static);
}
#[test]
fn inline_storage() {
let cap = super::INLINE_CAP;
let mut b = Bytes::copy_from_slice(&LONG[..cap]);
assert!(b.is_inline());
b.truncate(cap);
assert_eq!(&b[..], &LONG[..cap]);
unsafe { b.storage.set_end(5) };
assert_eq!(&b[..], &LONG[..5]);
unsafe { b.storage.set_end(cap) };
assert_eq!(&b[..], &LONG[..5]);
}
#[test]
fn inline_from_ptr_matches_reference() {
fn reference(src: &[u8]) -> Storage {
let mut st = Storage::empty();
unsafe { ptr::copy_nonoverlapping(src.as_ptr(), st.inline_ptr(), src.len()) };
st.set_inline_len(src.len());
st
}
fn fields(st: &Storage) -> (usize, usize, usize) {
(st.offset.get(), st.ptr.addr(), st.len)
}
let buf: Vec<u8> = (1..=64).collect();
for len in 0..=super::INLINE_CAP {
for start in 0..=8 {
let src = &buf[start..start + len];
let exact = src.to_vec();
let expected = fields(&reference(src));
let built = unsafe {
[
Storage::from_ptr_inline(exact.as_ptr(), len),
Storage::from_ptr_inline_prefix(exact.as_ptr(), len, len),
Storage::from_ptr_inline_prefix(
buf[start..].as_ptr(),
buf.len() - start,
len,
),
]
};
for st in &built {
assert_eq!(st.kind(), KIND_INLINE);
assert_eq!(st.as_ref(), src, "len {len}, start {start}");
assert_eq!(fields(st), expected, "len {len}, start {start}");
}
}
}
}
}