use alloc::vec::Vec;
use core::hint::unreachable_unchecked;
use core::marker::PhantomData;
use core::mem::{align_of, forget, replace, size_of, transmute, ManuallyDrop, MaybeUninit};
use core::num::NonZeroU8;
use core::ops::Range;
use allocated::Allocated;
use borrowed::Borrowed;
use crate::Backend;
pub mod allocated;
pub mod borrowed;
pub mod inline;
#[cfg(test)]
mod tests;
const TAG_BITS: u8 = 2;
const MASK: u8 = (1 << TAG_BITS) - 1;
const TAG_INLINE: u8 = 1;
const TAG_BORROWED: u8 = 2;
const TAG_ALLOCATED: u8 = 3;
const INLINE_CAPACITY: usize = size_of::<Borrowed>() - 1;
const WORD_SIZE_M1: usize = size_of::<usize>() - 1;
pub type Inline = inline::Inline<INLINE_CAPACITY>;
#[repr(C)]
pub struct HipByt<'borrow, B: Backend> {
pivot: Pivot,
_marker: PhantomData<&'borrow B>,
}
#[derive(Clone, Copy)]
#[repr(C)]
pub(super) struct Pivot {
#[cfg(target_endian = "little")]
tag_byte: NonZeroU8,
#[cfg(target_endian = "little")]
_word_remainder: MaybeUninit<[u8; WORD_SIZE_M1]>,
#[cfg(target_endian = "little")]
_word1: MaybeUninit<*mut ()>,
_word2: MaybeUninit<*mut ()>,
#[cfg(target_endian = "big")]
_word1: MaybeUninit<*mut ()>,
#[cfg(target_endian = "big")]
_word_remainder: MaybeUninit<[u8; WORD_SIZE_M1]>,
#[cfg(target_endian = "big")]
tag_byte: NonZeroU8,
}
unsafe impl<B: Backend + Sync> Sync for HipByt<'_, B> {}
unsafe impl<B: Backend + Send> Send for HipByt<'_, B> {}
#[repr(C)]
pub union Union<'borrow, B: Backend> {
pub inline: Inline,
pub allocated: Allocated<B>,
pub borrowed: Borrowed<'borrow>,
pivot: Pivot,
}
impl<'borrow, B: Backend> Union<'borrow, B> {
const ASSERTS: () = {
assert!(size_of::<Self>() == size_of::<HipByt<'borrow, B>>());
assert!(align_of::<Self>() == align_of::<HipByt<'borrow, B>>());
};
#[inline]
pub const fn into_raw(self) -> HipByt<'borrow, B> {
let () = Self::ASSERTS;
let pivot = unsafe { self.pivot };
HipByt {
pivot,
_marker: PhantomData,
}
}
}
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Tag {
Inline = TAG_INLINE,
Borrowed = TAG_BORROWED,
Allocated = TAG_ALLOCATED,
}
pub enum Split<'a, 'borrow, B: Backend> {
Inline(&'a Inline),
Allocated(&'a Allocated<B>),
Borrowed(&'a Borrowed<'borrow>),
}
pub enum SplitMut<'a, 'borrow, B: Backend> {
Inline(&'a mut Inline),
Allocated(&'a mut Allocated<B>),
Borrowed(&'a mut Borrowed<'borrow>),
}
impl<'borrow, B: Backend> HipByt<'borrow, B> {
#[inline]
pub(super) const fn union(&self) -> &Union<'borrow, B> {
let raw_ptr: *const _ = &self.pivot;
let union_ptr: *const Union<'borrow, B> = raw_ptr.cast();
unsafe { &*union_ptr }
}
#[inline]
pub(super) const fn union_mut(&mut self) -> &mut Union<'borrow, B> {
let raw_ptr: *mut _ = &mut self.pivot;
let union_ptr: *mut Union<'borrow, B> = raw_ptr.cast();
unsafe { &mut *union_ptr }
}
pub(super) fn union_move(self) -> Union<'borrow, B> {
let this = ManuallyDrop::new(self);
Union { pivot: this.pivot }
}
#[inline]
pub(super) const fn from_allocated(allocated: Allocated<B>) -> Self {
Union { allocated }.into_raw()
}
#[inline]
pub(super) const fn from_inline(inline: Inline) -> Self {
Union { inline }.into_raw()
}
#[inline]
pub(super) const fn from_borrowed(borrowed: Borrowed<'borrow>) -> Self {
Union { borrowed }.into_raw()
}
pub(super) const fn tag(&self) -> Tag {
match self.pivot.tag_byte.get() & MASK {
TAG_INLINE => Tag::Inline,
TAG_BORROWED => Tag::Borrowed,
TAG_ALLOCATED => Tag::Allocated,
_ => unsafe { unreachable_unchecked() },
}
}
#[inline]
pub(super) const fn split(&self) -> Split<'_, 'borrow, B> {
let tag = self.tag();
let union = self.union();
match tag {
Tag::Inline => {
Split::Inline(unsafe { &union.inline })
}
Tag::Borrowed => {
Split::Borrowed(unsafe { &union.borrowed })
}
Tag::Allocated => {
Split::Allocated(unsafe { &union.allocated })
}
}
}
#[inline]
pub(super) const fn split_mut(&mut self) -> SplitMut<'_, 'borrow, B> {
let tag = self.tag();
let union = self.union_mut();
match tag {
Tag::Inline => {
SplitMut::Inline(unsafe { &mut union.inline })
}
Tag::Borrowed => {
SplitMut::Borrowed(unsafe { &mut union.borrowed })
}
Tag::Allocated => {
SplitMut::Allocated(unsafe { &mut union.allocated })
}
}
}
pub(super) fn from_vec(vec: Vec<u8>) -> Self {
let allocated = Allocated::new(vec);
Self::from_allocated(allocated)
}
#[inline]
pub(super) const fn inline_empty() -> Self {
const { Self::from_inline(Inline::empty()) }
}
pub(super) const unsafe fn inline_unchecked(bytes: &[u8]) -> Self {
let inline = unsafe { Inline::new_unchecked(bytes) };
Self::from_inline(inline)
}
#[inline]
pub(crate) fn normalized_from_vec(vec: Vec<u8>) -> Self {
let len = vec.len();
if len <= INLINE_CAPACITY {
unsafe { Self::inline_unchecked(&vec) }
} else {
Self::from_vec(vec)
}
}
pub(crate) fn from_slice(bytes: &[u8]) -> Self {
let len = bytes.len();
if len == 0 {
Self::inline_empty()
} else if len <= INLINE_CAPACITY {
unsafe { Self::inline_unchecked(bytes) }
} else {
Self::from_allocated(Allocated::from_slice(bytes))
}
}
#[inline]
pub(super) unsafe fn range_unchecked(&self, range: Range<usize>) -> Self {
debug_assert!(range.start <= range.end);
debug_assert!(range.end <= self.len());
let result = match self.split() {
Split::Inline(inline) => {
unsafe { Self::inline_unchecked(&inline.as_slice()[range]) }
}
Split::Borrowed(borrowed) => Self::borrowed(&borrowed.as_slice()[range]),
Split::Allocated(allocated) => {
if range.len() <= INLINE_CAPACITY {
unsafe { Self::inline_unchecked(&allocated.as_slice()[range]) }
} else {
unsafe {
let allocated = allocated.slice_unchecked(range);
Self::from_allocated(allocated)
}
}
}
};
debug_assert!(self.is_normalized());
result
}
#[must_use]
pub unsafe fn slice_ref_unchecked(&self, slice: &[u8]) -> Self {
#[cfg(debug_assertions)]
{
let range = self.as_slice().as_ptr_range();
let slice_range = slice.as_ptr_range();
assert!(range.contains(&slice_range.start) || range.end == slice_range.start);
assert!(range.contains(&slice_range.end) || range.end == slice_range.end);
}
let result = match self.split() {
Split::Inline(_) => {
unsafe { Self::inline_unchecked(slice) }
}
Split::Borrowed(_) => {
let sl: &'borrow [u8] = unsafe { transmute(slice) };
Self::borrowed(sl)
}
Split::Allocated(allocated) => {
if slice.len() <= INLINE_CAPACITY {
unsafe { Self::inline_unchecked(slice) }
} else {
let range = unsafe { range_of_unchecked(self.as_slice(), slice) };
unsafe {
let allocated = allocated.slice_unchecked(range);
Self::from_allocated(allocated)
}
}
}
};
debug_assert!(self.is_normalized());
result
}
#[inline]
pub(crate) fn take_vec(&mut self) -> Vec<u8> {
if self.is_allocated() {
let allocated = unsafe { self.union_mut().allocated };
if let Ok(owned) = allocated.try_into_vec() {
forget(replace(self, Self::new()));
return owned;
}
}
let owned = Vec::from(self.as_slice());
*self = Self::new();
owned
}
#[inline]
pub(super) const fn take_allocated(&mut self) -> Option<Allocated<B>> {
match self.split() {
Split::Allocated(&allocated) => {
forget(replace(self, Self::new()));
Some(allocated)
}
_ => None,
}
}
#[inline]
pub(super) fn make_unique(&mut self) {
let tag = self.tag();
match tag {
Tag::Inline => {}
Tag::Borrowed => {
let old = replace(self, Self::new()).union_move();
let borrowed = unsafe { old.borrowed };
*self = Self::from_slice(borrowed.as_slice());
}
Tag::Allocated => {
if unsafe { self.union().allocated }.is_unique() {
return;
}
let old = replace(self, Self::new());
let allocated = unsafe { old.union_move().allocated };
let new = Self::from_vec(allocated.as_slice().to_vec());
allocated.explicit_drop();
*self = new;
}
}
}
#[inline(never)]
pub(crate) fn inherent_eq<B2: Backend>(&self, other: &HipByt<B2>) -> bool {
extern "C" {
fn memcmp(a: *const u8, b: *const u8, size: usize) -> core::ffi::c_int;
}
let len = self.len();
if len != other.len() {
return false;
}
let self_ptr = self.as_ptr();
let other_ptr = other.as_ptr();
if core::ptr::eq(self_ptr, other_ptr) {
return true;
}
let size = len * size_of::<u8>();
unsafe { memcmp(self_ptr, other_ptr, size) == 0 }
}
}
impl<B: Backend> Drop for HipByt<'_, B> {
#[inline]
fn drop(&mut self) {
if let Some(allocated) = self.take_allocated() {
allocated.explicit_drop();
}
}
}
impl<B: Backend> Clone for HipByt<'_, B> {
fn clone(&self) -> Self {
match self.split() {
Split::Inline(&inline) => Self::from_inline(inline),
Split::Borrowed(&borrowed) => Self::from_borrowed(borrowed),
Split::Allocated(allocated) => {
let clone = allocated.explicit_clone();
Self::from_allocated(clone)
}
}
}
}
unsafe fn range_of_unchecked(whole: &[u8], slice: &[u8]) -> Range<usize> {
unsafe {
let offset = slice.as_ptr().offset_from(whole.as_ptr());
let offset: usize = offset.try_into().unwrap_unchecked();
offset..offset + slice.len()
}
}
pub fn try_range_of(whole: &[u8], slice: &[u8]) -> Option<Range<usize>> {
let len = whole.len();
let Range { start, end } = whole.as_ptr_range();
let slice_len = slice.len();
let slice_start = slice.as_ptr();
if slice_start < start || slice_start > end {
return None;
}
let offset = unsafe { slice_start.offset_from(start) };
let offset: usize = unsafe { offset.try_into().unwrap_unchecked() };
if offset + slice_len > len {
None
} else {
Some(offset..offset + slice_len)
}
}