use std::alloc::{Layout, LayoutError};
use std::cmp::Ordering;
use std::collections::hash_map::DefaultHasher;
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::mem;
use std::ptr::NonNull;
use crate::alloc::{alloc_infallible, dealloc_infallible};
use crate::string::IString;
use crate::thin::{ThinMut, ThinMutExt, ThinRef, ThinRefExt};
use super::{
Destructured, DestructuredMut, DestructuredRef, IValue, ReprTag, ValueRepr, ValueType,
};
use crate::object::IObject;
#[repr(C)]
#[repr(align(8))]
pub(crate) struct Header {
pub(crate) len: usize,
pub(crate) cap: usize,
}
#[repr(C)]
#[derive(Debug)]
pub(crate) struct KeyValuePair {
pub(crate) key: IString,
pub(crate) value: IValue,
}
pub(crate) struct SplitHeader<'a> {
pub(crate) cap: usize,
pub(crate) items: &'a [KeyValuePair],
pub(crate) table: &'a [usize],
}
impl SplitHeader<'_> {
pub(crate) fn find_bucket(&self, key: &IString) -> Result<usize, usize> {
let hash_cap = ObjectRepr::hash_capacity(self.cap);
debug_assert!(hash_cap > 0, "find_bucket on a zero-capacity table");
let initial_bucket = ObjectRepr::hash_bucket(key, hash_cap);
unsafe {
for i in 0..hash_cap {
let bucket = (initial_bucket + i) % hash_cap;
let index = *self.table.get_unchecked(bucket);
if index == usize::MAX {
return Err(bucket);
}
let k = &self.items.get_unchecked(index).key;
if k == key {
return Ok(bucket);
}
let key_dist =
(bucket + hash_cap - ObjectRepr::hash_bucket(k, hash_cap)) % hash_cap;
if key_dist < i {
return Err(bucket);
}
}
}
Err(usize::MAX)
}
pub(crate) unsafe fn find_bucket_from_index(&self, index: usize) -> usize {
let hash_cap = ObjectRepr::hash_capacity(self.cap);
debug_assert!(
hash_cap > 0,
"find_bucket_from_index on a zero-capacity table"
);
let key = &self.items.get_unchecked(index).key;
let mut bucket = ObjectRepr::hash_bucket(key, hash_cap);
while *self.table.get_unchecked(bucket) != index {
bucket = (bucket + 1) % hash_cap;
}
bucket
}
}
pub(crate) struct SplitHeaderMut<'a> {
pub(crate) cap: usize,
pub(crate) items: &'a mut [KeyValuePair],
pub(crate) table: &'a mut [usize],
}
impl SplitHeaderMut<'_> {
pub(crate) fn as_ref<'a>(&'a self) -> SplitHeader<'a> {
SplitHeader {
cap: self.cap,
items: self.items,
table: self.table,
}
}
pub(crate) unsafe fn unshift(&mut self, initial_bucket: usize) {
let hash_cap = ObjectRepr::hash_capacity(self.cap);
let mut prev_bucket = initial_bucket;
for i in 1..hash_cap {
let bucket = (initial_bucket + i) % hash_cap;
let index = *self.table.get_unchecked(bucket);
if index == usize::MAX {
return;
}
let k = &self.items.get_unchecked(index).key;
if ObjectRepr::hash_bucket(k, hash_cap) == bucket {
return;
}
self.table.swap(prev_bucket, bucket);
prev_bucket = bucket;
}
}
pub(crate) unsafe fn shift(&mut self, initial_bucket: usize, mut index: usize) {
let hash_cap = ObjectRepr::hash_capacity(self.cap);
for i in 0..hash_cap {
if index == usize::MAX {
return;
}
let bucket = (initial_bucket + i) % hash_cap;
mem::swap(self.table.get_unchecked_mut(bucket), &mut index);
}
}
pub(crate) unsafe fn remove_bucket(&mut self, bucket: usize) {
let index = mem::replace(self.table.get_unchecked_mut(bucket), usize::MAX);
self.unshift(bucket);
let last_index = self.items.len() - 1;
if last_index != index {
let bucket_to_update = self.as_ref().find_bucket_from_index(last_index);
*self.table.get_unchecked_mut(bucket_to_update) = index;
self.items.swap(index, last_index);
}
}
}
pub(crate) trait HeaderRef<'a>: ThinRefExt<'a, Header> {
fn items_ptr(&self) -> *const KeyValuePair {
unsafe { self.ptr().add(1).cast() }
}
fn hashes_ptr(&self) -> *const usize {
unsafe { self.items_ptr().add(self.cap).cast() }
}
fn split(&self) -> SplitHeader<'a> {
unsafe {
SplitHeader {
cap: self.cap,
items: std::slice::from_raw_parts(self.items_ptr(), self.len),
table: std::slice::from_raw_parts(
self.hashes_ptr(),
ObjectRepr::hash_capacity(self.cap),
),
}
}
}
}
pub(crate) trait HeaderMut<'a>: ThinMutExt<'a, Header> {
fn items_ptr_mut(&mut self) -> *mut KeyValuePair {
unsafe { self.ptr_mut().add(1).cast() }
}
fn hashes_ptr_mut(&mut self) -> *mut usize {
unsafe { self.items_ptr_mut().add(self.cap).cast() }
}
fn split_mut(mut self) -> SplitHeaderMut<'a> {
let len = self.len;
let hash_cap = ObjectRepr::hash_capacity(self.cap);
let item_ptr = self.items_ptr_mut();
let hash_ptr = self.hashes_ptr_mut();
unsafe {
SplitHeaderMut {
cap: self.cap,
items: std::slice::from_raw_parts_mut(item_ptr as *mut _, len),
table: std::slice::from_raw_parts_mut(hash_ptr as *mut _, hash_cap),
}
}
}
unsafe fn pop(&mut self) -> (IString, IValue) {
self.len -= 1;
let item = self.items_ptr_mut().add(self.len).read();
(item.key, item.value)
}
unsafe fn push(&mut self, key: IString, value: IValue) -> usize {
self.items_ptr_mut()
.add(self.len)
.write(KeyValuePair { key, value });
let res = self.len;
self.len += 1;
res
}
fn clear(&mut self) {
for item in self.reborrow().split_mut().table {
*item = usize::MAX;
}
while self.len > 0 {
unsafe {
self.pop();
}
}
}
}
impl<'a, T: ThinRefExt<'a, Header>> HeaderRef<'a> for T {}
impl<'a, T: ThinMutExt<'a, Header>> HeaderMut<'a> for T {}
pub(crate) struct ObjectRepr;
impl ObjectRepr {
fn hash_capacity(cap: usize) -> usize {
cap + cap / 4
}
fn hash_fn(s: &IString) -> usize {
let v: &IValue = s.as_ref();
let mut p = v.usize_() >> 3;
p = p.wrapping_mul(202_529);
p = p ^ (p >> 13);
p.wrapping_mul(202_529)
}
fn hash_bucket(s: &IString, hash_cap: usize) -> usize {
Self::hash_fn(s) % hash_cap
}
fn layout(cap: usize) -> Result<Layout, LayoutError> {
Ok(Layout::new::<Header>()
.extend(Layout::array::<KeyValuePair>(cap)?)?
.0
.extend(Layout::array::<usize>(Self::hash_capacity(cap))?)?
.0
.pad_to_align())
}
fn alloc(cap: usize) -> NonNull<Header> {
unsafe {
let hd = alloc_infallible(Self::layout(cap).unwrap()).cast::<Header>();
hd.write(Header { len: 0, cap });
let mut hd_mut = ThinMut::new(hd);
let hash_ptr = hd_mut.hashes_ptr_mut();
for i in 0..Self::hash_capacity(cap) {
hash_ptr.add(i).write(usize::MAX);
}
hd
}
}
fn dealloc(ptr: NonNull<Header>) {
unsafe {
let layout = Self::layout(ptr.as_ref().cap).unwrap();
dealloc_infallible(ptr.cast(), layout);
}
}
pub(crate) unsafe fn header(v: &IValue) -> ThinRef<'_, Header> {
ThinRef::new(v.ptr().cast())
}
pub(crate) unsafe fn header_mut(v: &mut IValue) -> ThinMut<'_, Header> {
ThinMut::new(v.ptr().cast())
}
pub(crate) unsafe fn len(v: &IValue) -> usize {
if v.usize_() == 0 {
0
} else {
Self::header(v).len
}
}
pub(crate) unsafe fn capacity(v: &IValue) -> usize {
if v.usize_() == 0 {
0
} else {
Self::header(v).cap
}
}
pub(crate) unsafe fn items(v: &IValue) -> &[KeyValuePair] {
if v.usize_() == 0 {
&[]
} else {
Self::header(v).split().items
}
}
pub(crate) fn empty() -> IValue {
unsafe { IValue::new_usize(ReprTag::Object, 0) }
}
pub(crate) fn with_capacity(cap: usize) -> IValue {
if cap == 0 {
Self::empty()
} else {
unsafe { IValue::new_ptr(ReprTag::Object, Self::alloc(cap).cast()) }
}
}
unsafe fn resize_internal(v: &mut IValue, cap: usize) {
let mut old = mem::replace(v, Self::with_capacity(cap));
if Self::capacity(v) != 0 {
let mut hd = Self::header_mut(v);
while Self::len(&old) != 0 {
let (key, value) = Self::header_mut(&mut old).pop();
if let Err(bucket) = hd.split().find_bucket(&key) {
let index = hd.push(key, value);
hd.reborrow().split_mut().shift(bucket, index);
}
}
}
}
pub(crate) unsafe fn reserve(v: &mut IValue, additional: usize) {
let current_capacity = Self::capacity(v);
let desired_capacity = Self::len(v).checked_add(additional).unwrap();
if current_capacity >= desired_capacity {
return;
}
Self::resize_internal(v, (current_capacity * 2).max(desired_capacity.max(4)));
}
pub(crate) unsafe fn shrink_to_fit(v: &mut IValue) {
Self::resize_internal(v, Self::len(v));
}
}
impl ValueRepr for ObjectRepr {
fn value_type(&self, _v: &IValue) -> ValueType {
ValueType::Object
}
unsafe fn clone(&self, v: &IValue) -> IValue {
if v.usize_() == 0 {
return Self::empty();
}
let split = Self::header(v).split();
let mut res = Self::with_capacity(split.items.len());
if !split.items.is_empty() {
let mut hd = Self::header_mut(&mut res);
for kvp in split.items {
if let Err(bucket) = hd.split().find_bucket(&kvp.key) {
let index = hd.push(kvp.key.clone(), kvp.value.clone());
hd.reborrow().split_mut().shift(bucket, index);
}
}
}
res
}
unsafe fn drop(&self, v: &mut IValue) {
if v.usize_() == 0 {
return;
}
Self::header_mut(v).clear();
Self::dealloc(v.ptr().cast());
v.set_usize(0);
}
unsafe fn hash(&self, v: &IValue, state: &mut dyn Hasher) {
let entries = Self::items(v);
state.write_usize(entries.len());
let mut total_hash = 0_u64;
for kvp in entries {
let mut h = DefaultHasher::new();
(&kvp.key, &kvp.value).hash(&mut h);
total_hash = total_hash.wrapping_add(h.finish());
}
state.write_u64(total_hash);
}
unsafe fn eq(&self, a: &IValue, b: &IValue) -> bool {
if a.raw_eq(b) {
return true;
}
let len_a = Self::len(a);
if len_a != Self::len(b) {
return false;
}
if len_a == 0 {
return true;
}
let sa = Self::header(a).split();
let sb = Self::header(b).split();
for kvp in sa.items {
match sb.find_bucket(&kvp.key) {
Ok(bucket) => {
let index = *sb.table.get_unchecked(bucket);
if sb.items.get_unchecked(index).value != kvp.value {
return false;
}
}
Err(_) => return false,
}
}
true
}
unsafe fn partial_cmp(&self, a: &IValue, b: &IValue) -> Option<Ordering> {
if self.eq(a, b) {
Some(Ordering::Equal)
} else {
None
}
}
unsafe fn debug(&self, v: &IValue, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_map()
.entries(Self::items(v).iter().map(|kvp| (&kvp.key, &kvp.value)))
.finish()
}
fn destructure(&self, v: IValue) -> Destructured {
Destructured::Object(IObject(v))
}
unsafe fn destructure_ref<'a>(&self, v: &'a IValue) -> DestructuredRef<'a> {
DestructuredRef::Object(v.as_object_unchecked())
}
unsafe fn destructure_mut<'a>(&self, v: &'a mut IValue) -> DestructuredMut<'a> {
DestructuredMut::Object(v.as_object_unchecked_mut())
}
unsafe fn len(&self, v: &IValue) -> Option<usize> {
Some(Self::len(v))
}
}