use crate::hash::make_hash;
#[repr(C)]
#[derive(Debug)]
pub struct HashStr{
hash:u64,
str:str,
}
pub const SIZE_HASH:usize=core::mem::size_of::<u64>();
#[derive(Debug)]
pub enum RefFromBytesError{
TooShort,
UTF8(core::str::Utf8Error),
}
impl std::fmt::Display for RefFromBytesError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl HashStr{
#[inline]
pub const fn precomputed_hash(&self)->u64{
self.hash
}
#[inline]
pub const fn as_str(&self)->&str{
&self.str
}
#[inline]
pub const fn as_hash_str_bytes<'a>(&'a self)->&'a [u8]{
unsafe{core::slice::from_raw_parts(
self as *const Self as *const u8,
SIZE_HASH+self.as_str().len()
)}
}
#[inline]
pub fn ref_from_bytes<'a>(bytes:&'a [u8])->Result<&'a Self,RefFromBytesError>{
let Some(str_slice)=bytes.get(SIZE_HASH..)else{
return Err(RefFromBytesError::TooShort);
};
match core::str::from_utf8(str_slice){
Ok(_)=>Ok(unsafe{HashStr::ref_from_bytes_unchecked(bytes)}),
Err(e)=>Err(RefFromBytesError::UTF8(e))
}
}
#[inline]
pub const unsafe fn ref_from_bytes_unchecked<'a>(bytes:&'a [u8])->&'a Self{
let ptr=bytes as *const [u8] as *const u8;
let str_len_hacked=bytes.len()-SIZE_HASH;
let bytes_hacked=unsafe{core::slice::from_raw_parts(
ptr,
str_len_hacked
)};
let h=bytes_hacked as *const [u8] as *const Self;
unsafe{&*h}
}
#[inline]
pub fn anonymous(value:String)->Box<HashStr>{
let hash=make_hash(&value);
let mut bytes=value.into_bytes();
bytes.reserve_exact(SIZE_HASH);
insert_bytes(&mut bytes,&hash.to_ne_bytes());
let boxed=bytes.into_boxed_slice();
let href=unsafe{Self::ref_from_bytes_unchecked(Box::leak(boxed))};
unsafe{Box::from_raw(href as *const Self as *mut Self)}
}
}
fn insert_bytes(vec:&mut Vec<u8>, bytes: &[u8]) {
let len = vec.len();
let amt = bytes.len();
vec.reserve_exact(amt);
unsafe {
core::ptr::copy(vec.as_ptr(), vec.as_mut_ptr().add(amt), len);
core::ptr::copy_nonoverlapping(bytes.as_ptr(), vec.as_mut_ptr(), amt);
vec.set_len(len + amt);
}
}