use std::fmt;
use std::fmt::Debug;
use std::intrinsics::copy_nonoverlapping;
use std::mem;
use std::slice;
use std::str;
use allocative::Allocative;
use starlark_derive::Trace;
use starlark_map::Hashed;
use starlark_map::StarlarkHashValue;
use crate as starlark;
use crate::coerce::Coerce;
use crate::collections::aligned_padded_str::AlignedPaddedStr;
#[derive(Clone, Trace, Allocative)]
pub(crate) struct Symbol {
hash: u64,
len: u32,
payload: Box<[usize]>,
small_hash: StarlarkHashValue,
}
unsafe impl Coerce<Symbol> for Symbol {}
impl Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}
impl PartialEq for Symbol {
fn eq(&self, other: &Self) -> bool {
if self.len != other.len {
return false;
}
let p1 = &*self.payload;
let p2 = &*other.payload;
for i in 0..self.payload.len() {
if unsafe { p1.get_unchecked(i) != p2.get_unchecked(i) } {
return false;
}
}
true
}
}
impl Eq for Symbol {}
impl Symbol {
pub(crate) fn new(x: &str) -> Self {
Self::new_hashed(Hashed::new(x))
}
pub(crate) fn new_hashed(x: Hashed<&str>) -> Self {
let small_hash = x.hash();
let hash = small_hash.promote();
let len = x.key().len();
let len_words = (len + mem::size_of::<usize>() - 1) / mem::size_of::<usize>();
let mut payload = vec![0; len_words]; unsafe {
copy_nonoverlapping(x.key().as_ptr(), payload.as_mut_ptr() as *mut u8, len);
}
Self {
hash,
len: len.try_into().unwrap(),
payload: payload.into_boxed_slice(),
small_hash,
}
}
#[inline]
pub(crate) fn hash(&self) -> u64 {
self.hash
}
pub(crate) fn as_str(&self) -> &str {
unsafe {
let s = slice::from_raw_parts(self.payload.as_ptr() as *const u8, self.len as usize);
str::from_utf8_unchecked(s)
}
}
#[inline]
pub(crate) fn as_aligned_padded_str(&self) -> AlignedPaddedStr {
unsafe { AlignedPaddedStr::new(self.len as usize, self.payload.as_ptr()) }
}
pub(crate) fn as_str_hashed(&self) -> Hashed<&str> {
Hashed::new_unchecked(self.small_hash, self.as_str())
}
pub(crate) fn small_hash(&self) -> StarlarkHashValue {
self.small_hash
}
}