use std::alloc::{alloc, dealloc, handle_alloc_error};
use core::alloc::Layout;
use core::ptr::{slice_from_raw_parts, NonNull};
use core::sync::atomic::{AtomicUsize, Ordering};
const MAX_REFCOUNT: usize = isize::MAX as usize;
#[repr(C)]
struct Header {
ref_count: AtomicUsize,
len: usize,
}
#[repr(transparent)]
pub struct Payload {
_align: [Header; 0],
}
impl Payload {
#[inline]
fn layout(len: usize) -> Layout {
let (layout, byte_offset) = Layout::new::<Header>()
.extend(Layout::array::<u8>(len).unwrap())
.expect("attempted to allocate a FlyStr that was too large (~isize::MAX)");
debug_assert_eq!(byte_offset, size_of::<Header>());
debug_assert!(layout.align() > 1);
layout
}
pub fn alloc(bytes: &[u8]) -> NonNull<Self> {
let layout = Self::layout(bytes.len());
let ptr = unsafe { alloc(layout) };
if ptr.is_null() {
handle_alloc_error(layout);
}
let header = ptr.cast::<Header>();
unsafe {
header.write(Header {
ref_count: AtomicUsize::new(1),
len: bytes.len(),
});
}
let payload = unsafe { header.add(1).cast::<u8>() };
unsafe {
bytes.as_ptr().copy_to_nonoverlapping(payload, bytes.len());
}
unsafe { NonNull::new_unchecked(payload.cast::<Payload>()) }
}
#[inline]
pub unsafe fn dealloc(ptr: *mut Self) {
let header = unsafe { Self::header(ptr) };
let len = unsafe { (*header).len };
let layout = Self::layout(len);
unsafe {
dealloc(header.cast(), layout);
}
}
#[cfg(test)]
#[inline]
pub unsafe fn refcount(ptr: *mut Self) -> usize {
let header = unsafe { Self::header(ptr) };
unsafe { (*header).ref_count.load(Ordering::Relaxed) }
}
#[inline]
pub unsafe fn inc_ref(ptr: *mut Self) -> usize {
let header = unsafe { Self::header(ptr) };
let prev_count = unsafe { (*header).ref_count.fetch_add(1, Ordering::Relaxed) };
if prev_count > MAX_REFCOUNT {
std::process::abort();
}
prev_count
}
#[inline]
pub unsafe fn dec_ref(ptr: *mut Self) -> usize {
let header = unsafe { Self::header(ptr) };
unsafe { (*header).ref_count.fetch_sub(1, Ordering::Relaxed) }
}
#[inline]
unsafe fn header(ptr: *mut Self) -> *mut Header {
unsafe { ptr.cast::<Header>().sub(1) }
}
#[inline]
pub unsafe fn len(ptr: *mut Self) -> usize {
unsafe { (*Self::header(ptr)).len }
}
#[inline]
pub unsafe fn bytes(ptr: *mut Self) -> *const [u8] {
let len = unsafe { Self::len(ptr) };
slice_from_raw_parts(ptr.cast::<u8>(), len)
}
}