#![warn(missing_docs)]
#[cfg(target_endian = "big")]
compile_error!("kevy-bytes requires little-endian: heap-tag byte overlaps inline length byte");
mod traits;
use std::alloc::{Layout, alloc, dealloc, handle_alloc_error};
use std::mem::{self, ManuallyDrop};
use std::ptr::NonNull;
use std::slice;
pub(crate) const INLINE_CAP: usize = 23;
pub(crate) const INLINE_LEN_MAX: u8 = (INLINE_CAP - 1) as u8;
#[cfg(target_pointer_width = "64")]
const TAG_HEAP_BIT: usize = 0xFFusize << 56;
#[cfg(target_pointer_width = "64")]
const CAP_MASK: usize = (1usize << 56) - 1;
#[cfg(target_pointer_width = "32")]
const HEAP_TAG_BYTE: u8 = 0xFF;
#[repr(C)]
#[derive(Copy, Clone)]
struct Inline {
data: [u8; INLINE_CAP],
tag: u8,
}
#[cfg(target_pointer_width = "64")]
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct Heap {
pub(crate) ptr: NonNull<u8>,
pub(crate) len: usize,
pub(crate) cap_and_tag: usize,
}
#[cfg(target_pointer_width = "32")]
#[repr(C)]
#[derive(Copy, Clone)]
pub(crate) struct Heap {
pub(crate) ptr: NonNull<u8>,
pub(crate) len: u32,
pub(crate) cap: u32,
pub(crate) _pad: [u8; 11],
pub(crate) tag: u8,
}
impl Heap {
#[cfg(target_pointer_width = "64")]
#[inline]
pub(crate) fn new(ptr: NonNull<u8>, len: usize, cap: usize) -> Self {
debug_assert!(cap <= CAP_MASK, "kevy-bytes: capacity exceeds 56-bit field");
Self {
ptr,
len,
cap_and_tag: TAG_HEAP_BIT | (cap & CAP_MASK),
}
}
#[cfg(target_pointer_width = "32")]
#[inline]
pub(crate) fn new(ptr: NonNull<u8>, len: usize, cap: usize) -> Self {
debug_assert!(
len <= u32::MAX as usize && cap <= u32::MAX as usize,
"kevy-bytes: len/cap exceeds u32 on 32-bit platform"
);
Self {
ptr,
len: len as u32,
cap: cap as u32,
_pad: [0; 11],
tag: HEAP_TAG_BYTE,
}
}
#[cfg(target_pointer_width = "64")]
#[inline]
fn capacity(&self) -> usize {
self.cap_and_tag & CAP_MASK
}
#[cfg(target_pointer_width = "32")]
#[inline]
fn capacity(&self) -> usize {
self.cap as usize
}
#[cfg(target_pointer_width = "64")]
#[inline]
fn length(&self) -> usize {
self.len
}
#[cfg(target_pointer_width = "32")]
#[inline]
fn length(&self) -> usize {
self.len as usize
}
}
#[repr(C)]
pub union SmallBytes {
inline: Inline,
heap: Heap,
}
const _: () = {
assert!(mem::size_of::<SmallBytes>() == 24);
assert!(mem::align_of::<SmallBytes>() == mem::align_of::<usize>());
};
unsafe impl Send for SmallBytes {}
unsafe impl Sync for SmallBytes {}
impl SmallBytes {
pub const fn new() -> Self {
Self {
inline: Inline {
data: [0; INLINE_CAP],
tag: 0,
},
}
}
pub fn from_slice(bytes: &[u8]) -> Self {
if bytes.len() <= INLINE_LEN_MAX as usize {
let mut data = [0u8; INLINE_CAP];
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), data.as_mut_ptr(), bytes.len());
}
Self {
inline: Inline {
data,
tag: bytes.len() as u8,
},
}
} else {
Self::alloc_heap(bytes)
}
}
pub fn from_vec(vec: Vec<u8>) -> Self {
if vec.len() <= INLINE_LEN_MAX as usize {
Self::from_slice(&vec)
} else {
let mut v = ManuallyDrop::new(vec);
let ptr = unsafe { NonNull::new_unchecked(v.as_mut_ptr()) };
let len = v.len();
let cap = v.capacity();
Self {
heap: Heap::new(ptr, len, cap),
}
}
}
#[inline]
fn alloc_heap(bytes: &[u8]) -> Self {
let len = bytes.len();
let layout = unsafe { Layout::from_size_align_unchecked(len, 1) };
let raw = unsafe { alloc(layout) };
let ptr = match NonNull::new(raw) {
Some(p) => p,
None => handle_alloc_error(layout),
};
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr.as_ptr(), len);
}
Self {
heap: Heap::new(ptr, len, len),
}
}
#[inline]
fn is_inline(&self) -> bool {
unsafe { self.inline.tag <= INLINE_LEN_MAX }
}
#[inline]
pub fn len(&self) -> usize {
if self.is_inline() {
unsafe { self.inline.tag as usize }
} else {
unsafe { self.heap.length() }
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn heap_bytes(&self) -> usize {
if self.is_inline() { 0 } else { self.len() }
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
if self.is_inline() {
unsafe {
slice::from_raw_parts(self.inline.data.as_ptr(), self.inline.tag as usize)
}
} else {
unsafe { slice::from_raw_parts(self.heap.ptr.as_ptr(), self.heap.length()) }
}
}
pub fn to_vec(&self) -> Vec<u8> {
self.as_slice().to_vec()
}
pub fn into_vec(self) -> Vec<u8> {
if self.is_inline() {
self.as_slice().to_vec()
} else {
let (ptr, len, cap) = unsafe {
(
self.heap.ptr.as_ptr(),
self.heap.length(),
self.heap.capacity(),
)
};
let _do_not_drop = ManuallyDrop::new(self);
unsafe { Vec::from_raw_parts(ptr, len, cap) }
}
}
}
impl Default for SmallBytes {
fn default() -> Self {
Self::new()
}
}
impl Drop for SmallBytes {
fn drop(&mut self) {
if self.is_inline() {
return;
}
unsafe {
let cap = self.heap.capacity();
let layout = Layout::array::<u8>(cap).expect("kevy-bytes: drop layout");
dealloc(self.heap.ptr.as_ptr(), layout);
}
}
}
impl Clone for SmallBytes {
#[inline]
fn clone(&self) -> Self {
if self.is_inline() {
unsafe { Self { inline: self.inline } }
} else {
unsafe { self.clone_heap() }
}
}
}
impl SmallBytes {
#[inline]
unsafe fn clone_heap(&self) -> Self {
let (src_ptr, len) = unsafe { (self.heap.ptr.as_ptr(), self.heap.length()) };
let layout = unsafe { Layout::from_size_align_unchecked(len, 1) };
let raw = unsafe { alloc(layout) };
let ptr = match NonNull::new(raw) {
Some(p) => p,
None => handle_alloc_error(layout),
};
unsafe { std::ptr::copy_nonoverlapping(src_ptr, ptr.as_ptr(), len) };
Self {
heap: Heap::new(ptr, len, len),
}
}
}
impl PartialEq for SmallBytes {
#[inline]
fn eq(&self, other: &Self) -> bool {
let self_tag = unsafe { self.inline.tag };
let other_tag = unsafe { other.inline.tag };
let self_inline = self_tag <= INLINE_LEN_MAX;
let other_inline = other_tag <= INLINE_LEN_MAX;
match (self_inline, other_inline) {
(true, true) => {
let len = self_tag as usize;
if len != other_tag as usize {
return false;
}
let a = unsafe {
slice::from_raw_parts(self.inline.data.as_ptr(), len)
};
let b = unsafe {
slice::from_raw_parts(other.inline.data.as_ptr(), len)
};
a == b
}
(false, false) => {
let (a_len, b_len) =
unsafe { (self.heap.length(), other.heap.length()) };
if a_len != b_len {
return false;
}
let a = unsafe {
slice::from_raw_parts(self.heap.ptr.as_ptr(), a_len)
};
let b = unsafe {
slice::from_raw_parts(other.heap.ptr.as_ptr(), b_len)
};
a == b
}
_ => self.as_slice() == other.as_slice(),
}
}
}
impl Eq for SmallBytes {}
#[cfg(test)]
mod tests;