use std::cmp::Ordering;
use std::fmt::{self, Formatter};
use std::ptr::NonNull;
use super::{InlineValue, IS_STRING, TAG_MASK};
use crate::string::IString;
use crate::value::{
string_cmp, string_debug, Destructured, DestructuredMut, DestructuredRef, IValue, ValueType,
};
pub(crate) const CAPACITY: usize = std::mem::size_of::<usize>() - 1;
const LEN_SHIFT: u32 = super::PAYLOAD_SHIFT;
const LEN_MASK: usize = 0b111;
#[cfg(target_endian = "little")]
const CONTROL_OFFSET: usize = 0;
#[cfg(target_endian = "little")]
const CHAR_OFFSET: usize = 1;
#[cfg(target_endian = "big")]
const CONTROL_OFFSET: usize = std::mem::size_of::<usize>() - 1;
#[cfg(target_endian = "big")]
const CHAR_OFFSET: usize = 0;
pub(crate) struct InlineStringRepr;
impl InlineStringRepr {
pub(crate) fn try_encode(s: &str) -> Option<usize> {
(s.len() <= CAPACITY).then(|| Self::encode(s))
}
fn encode(s: &str) -> usize {
debug_assert!(s.len() <= CAPACITY);
let mut bytes = [0u8; std::mem::size_of::<usize>()];
bytes[CONTROL_OFFSET] = IS_STRING as u8 | ((s.len() as u8) << LEN_SHIFT);
bytes[CHAR_OFFSET..CHAR_OFFSET + s.len()].copy_from_slice(s.as_bytes());
let bits = usize::from_ne_bytes(bytes);
debug_assert_eq!(
bits & TAG_MASK,
0,
"inline string must leave the tag bits clear"
);
bits
}
fn len(bits: usize) -> usize {
(bits >> LEN_SHIFT) & LEN_MASK
}
unsafe fn bytes<'a>(storage: NonNull<u8>, bits: usize) -> &'a [u8] {
std::slice::from_raw_parts(storage.as_ptr().add(CHAR_OFFSET), Self::len(bits))
}
}
impl InlineValue for InlineStringRepr {
fn value_type(&self, _v: &IValue) -> ValueType {
ValueType::String
}
unsafe fn partial_cmp(&self, a: &IValue, b: &IValue) -> Option<Ordering> {
Some(string_cmp(a, b))
}
unsafe fn debug(&self, v: &IValue, f: &mut Formatter<'_>) -> fmt::Result {
string_debug(v, f)
}
fn destructure(&self, v: IValue) -> Destructured {
Destructured::String(IString(v))
}
unsafe fn destructure_ref<'a>(&self, v: &'a IValue) -> DestructuredRef<'a> {
DestructuredRef::String(v.as_string_unchecked())
}
unsafe fn destructure_mut<'a>(&self, v: &'a mut IValue) -> DestructuredMut<'a> {
DestructuredMut::String(v.as_string_unchecked_mut())
}
unsafe fn as_bytes<'a>(&self, v: &'a IValue) -> Option<&'a [u8]> {
Some(Self::bytes(NonNull::from(v).cast(), v.usize_()))
}
}