#[cfg(debug_assertions)]
use core::ptr::NonNull;
use bun_alloc::AllocError;
pub type CowSlice<T> = CowSliceZ<T, false>;
pub struct CowSliceZ<T: 'static, const Z: bool> {
ptr: *mut T,
flags: Flags,
#[cfg(debug_assertions)]
debug: Option<NonNull<DebugData>>,
}
#[repr(transparent)]
#[derive(Clone, Copy)]
struct Flags(usize);
impl Flags {
const IS_OWNED_BIT: usize = 1 << (usize::BITS - 1);
const LEN_MASK: usize = !Self::IS_OWNED_BIT;
#[inline]
const fn new(len: usize, is_owned: bool) -> Self {
debug_assert!(len <= Self::LEN_MASK);
Self((len & Self::LEN_MASK) | if is_owned { Self::IS_OWNED_BIT } else { 0 })
}
#[inline]
const fn len(self) -> usize {
self.0 & Self::LEN_MASK
}
#[inline]
const fn is_owned(self) -> bool {
self.0 & Self::IS_OWNED_BIT != 0
}
#[inline]
fn set_len(&mut self, len: usize) {
debug_assert!(len <= Self::LEN_MASK);
self.0 = (self.0 & Self::IS_OWNED_BIT) | (len & Self::LEN_MASK);
}
#[inline]
fn set_is_owned(&mut self, v: bool) {
if v {
self.0 |= Self::IS_OWNED_BIT;
} else {
self.0 &= Self::LEN_MASK;
}
}
}
impl<T: 'static, const Z: bool> CowSliceZ<T, Z> {
pub const EMPTY: Self = Self::init_static(&[]);
#[cfg(debug_assertions)]
#[inline]
fn debug_data(&self) -> Option<&DebugData> {
self.debug.map(|d| unsafe { d.as_ref() })
}
pub fn init_owned(data: Box<[T]>) -> Self {
let len = data.len();
let ptr = bun_core::heap::into_raw(data).cast::<T>();
Self {
ptr,
flags: Flags::new(len, true),
#[cfg(debug_assertions)]
debug: Some(DebugData::new_boxed()),
}
}
pub fn init_dupe(data: &[T]) -> Result<Self, AllocError>
where
T: Clone + Default,
{
let bytes: Box<[T]> = Box::<[T]>::from(data);
Ok(Self::init_owned(bytes))
}
pub const fn init_static(data: &'static [T]) -> Self {
Self {
ptr: data.as_ptr().cast_mut(),
flags: Flags::new(data.len(), false),
#[cfg(debug_assertions)]
debug: None,
}
}
#[inline]
pub fn is_owned(&self) -> bool {
self.flags.is_owned()
}
pub fn slice(&self) -> &[T] {
unsafe { core::slice::from_raw_parts(self.ptr, self.flags.len()) }
}
#[inline]
pub fn length(&self) -> usize {
self.flags.len()
}
pub fn slice_mut(&mut self) -> Result<&mut [T], AllocError>
where
T: Clone + Default,
{
if !self.is_owned() {
self.into_owned()?;
}
Ok(unsafe { core::slice::from_raw_parts_mut(self.ptr, self.flags.len()) })
}
pub fn slice_mut_unsafe(&mut self) -> &mut [T] {
debug_assert!(
self.is_owned(),
"CowSlice.slice_mut_unsafe cannot be called on Cows that borrow their data."
);
unsafe { core::slice::from_raw_parts_mut(self.ptr, self.flags.len()) }
}
pub fn take_slice(&mut self) -> Result<Box<[T]>, AllocError>
where
T: Clone + Default,
{
if !self.is_owned() {
self.into_owned()?;
}
let ptr = self.ptr;
let len = self.flags.len();
#[cfg(debug_assertions)]
if self.is_owned() {
if let Some(d) = self.debug.take() {
drop(unsafe { bun_core::heap::take(d.as_ptr()) });
}
}
let _ = core::mem::ManuallyDrop::new(core::mem::replace(self, Self::EMPTY));
Ok(unsafe { bun_core::heap::take(core::ptr::slice_from_raw_parts_mut(ptr, len)) })
}
pub fn borrow(&self) -> Self {
#[cfg(debug_assertions)]
if let Some(debug) = self.debug_data() {
let mut borrows = debug.mutex.lock();
*borrows += 1;
}
Self {
ptr: self.ptr,
flags: Flags::new(self.flags.len(), false),
#[cfg(debug_assertions)]
debug: self.debug,
}
}
pub fn borrow_subslice(&self, start: usize, end: Option<usize>) -> Self {
let end_ = end.unwrap_or(self.flags.len());
let mut result = self.borrow();
result.ptr = unsafe { self.ptr.add(start) };
result.flags.set_len(end_ - start);
result
}
pub fn to_owned(&mut self) -> Result<(), AllocError>
where
T: Clone + Default,
{
if !self.is_owned() {
self.into_owned()?;
}
Ok(())
}
#[inline(always)]
fn into_owned(&mut self) -> Result<(), AllocError>
where
T: Clone + Default,
{
debug_assert!(!self.is_owned());
let bytes: Box<[T]> = Box::<[T]>::from(self.slice());
self.ptr = bun_core::heap::into_raw(bytes).cast::<T>();
self.flags.set_is_owned(true);
#[cfg(debug_assertions)]
{
if let Some(dbg) = self.debug_data() {
let mut borrows = dbg.mutex.lock();
debug_assert!(*borrows > 0);
*borrows -= 1;
drop(borrows);
self.debug = None;
}
self.debug = Some(DebugData::new_boxed());
}
Ok(())
}
}
impl<T: 'static, const Z: bool> Drop for CowSliceZ<T, Z> {
fn drop(&mut self) {
#[cfg(debug_assertions)]
if let Some(dbg) = self.debug_data() {
if self.is_owned() {
let borrows = dbg.mutex.lock();
debug_assert!(
*borrows == 0,
"Cannot drop a CowSlice with active borrows. Current borrow count: {}",
*borrows
);
drop(borrows);
drop(unsafe { bun_core::heap::take(self.debug.unwrap().as_ptr()) });
} else {
let mut borrows = dbg.mutex.lock();
*borrows -= 1; }
}
if self.flags.is_owned() {
drop(unsafe {
bun_core::heap::take(core::ptr::slice_from_raw_parts_mut(
self.ptr,
self.flags.len(),
))
});
}
}
}
impl<const Z: bool> core::fmt::Display for CowSliceZ<u8, Z> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(bstr::BStr::new(self.slice()), f)
}
}
#[cfg(debug_assertions)]
struct DebugData {
mutex: bun_core::Mutex<usize>,
}
#[cfg(debug_assertions)]
impl DebugData {
fn new_boxed() -> NonNull<Self> {
bun_core::heap::into_raw_nn(Box::new(Self {
mutex: bun_core::Mutex::new(0),
}))
}
}
#[cfg(not(debug_assertions))]
const _: () = assert!(
core::mem::size_of::<CowSlice<u8>>() == core::mem::size_of::<&[u8]>(),
"CowSlice should be the same size as a native slice"
);
#[cfg(debug_assertions)]
const _: () = assert!(
core::mem::size_of::<CowSlice<u8>>() - core::mem::size_of::<Option<NonNull<DebugData>>>()
== core::mem::size_of::<&[u8]>(),
"CowSlice should be the same size as a native slice"
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cow_slice() {
let mut str = CowSlice::<u8>::init_static(b"hello");
assert!(!str.is_owned());
assert_eq!(str.slice(), b"hello");
let borrow = str.borrow();
assert!(!borrow.is_owned());
assert_eq!(borrow.slice(), b"hello");
str.to_owned().unwrap();
assert!(str.is_owned());
assert_eq!(str.slice(), b"hello");
drop(str);
assert_eq!(borrow.slice(), b"hello");
}
}