use ::core::fmt;
use ::core::num::NonZeroUsize;
use ::core::ptr;
use crate::arch::encode;
use crate::arch::layout;
use crate::arch::layout::StateTag;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct RawParts {
raw_ptr: *const u8,
meta: NonZeroUsize,
}
#[repr(C)]
union RawView {
parts: RawParts,
bytes: [u8; layout::INLINE_TOTAL_BYTES],
}
impl RawParts {
pub const unsafe fn new(raw_ptr: *const u8, meta: usize) -> Self {
let tag = match encode::from_meta_checked(meta) {
Some(tag) => tag,
None => panic!("invalid tag for compressed string"),
};
match tag {
StateTag::Borrowed | StateTag::Shared => {
let len = encode::decode_borrowed_or_shared_len(meta);
assert!(
len <= layout::MAX_BORROWED_OR_SHARED_LEN,
"string too large to compress"
);
}
StateTag::Inline => {
let len = encode::inline_len_from_meta(meta);
assert!(
len <= layout::INLINE_CAPACITY,
"inline string too large to compress"
);
}
}
Self {
raw_ptr,
meta: unsafe { NonZeroUsize::new_unchecked(meta) },
}
}
pub fn pack_inline(s: &str) -> Self {
let len = s.len();
debug_assert!(encode::supports_inline_len(len));
let mut view = RawView {
bytes: [0u8; layout::INLINE_TOTAL_BYTES],
};
unsafe {
view.bytes[0..len].copy_from_slice(s.as_bytes());
view.bytes[layout::BUFFER_TAG_BYTE_INDEX] = encode::inline_tag_byte(len, s.is_ascii());
view.parts
}
}
pub const fn raw_ptr(self) -> *const u8 {
self.raw_ptr
}
pub const fn meta(self) -> usize {
self.meta.get()
}
pub const fn is_ascii(self) -> bool {
let meta = self.meta();
(meta & layout::IS_ASCII_MASK) != 0
}
pub(crate) const fn cached_hash(self) -> usize {
encode::decode_cached_hash(self.meta())
}
pub const fn is_borrowed(self) -> bool {
let meta = self.meta();
(meta & (layout::INLINE_MASK | layout::NEEDS_DROP_MASK)) == 0
}
pub const fn is_shared(self) -> bool {
let meta = self.meta();
(meta & layout::NEEDS_DROP_MASK) != 0
}
pub const fn is_inline(self) -> bool {
let meta = self.meta();
(meta & layout::INLINE_MASK) != 0
}
pub const fn len(self) -> usize {
let meta = self.meta();
if (meta & layout::INLINE_MASK) != 0 {
encode::inline_len_from_meta(meta)
} else {
encode::decode_borrowed_or_shared_len(meta)
}
}
pub const fn is_empty(self) -> bool {
self.len() == 0
}
pub const fn into_fields(self) -> (*const u8, usize) {
(self.raw_ptr, self.meta())
}
#[inline]
pub const fn as_ptr(&self) -> *const u8 {
self.raw_ptr
}
pub(crate) const fn tag(self) -> StateTag {
let meta = self.meta();
encode::from_meta(meta)
}
pub(crate) const unsafe fn into_raw_non_inline(self) -> *const str {
let meta = self.meta();
let len = encode::decode_borrowed_or_shared_len(meta);
ptr::slice_from_raw_parts(self.raw_ptr, len) as *const str
}
}
impl fmt::Debug for RawParts {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawParts")
.field("raw_ptr", &self.raw_ptr)
.field("meta", &self.meta)
.field("len", &self.len())
.field(
"state",
&match self.tag() {
StateTag::Borrowed => "Borrowed",
StateTag::Shared => "Shared",
StateTag::Inline => "Inline",
},
)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
use ::core::mem::size_of;
#[test]
fn verify_niche_optimization() {
assert_eq!(size_of::<RawParts>(), 2 * size_of::<usize>());
assert_eq!(size_of::<Option<RawParts>>(), size_of::<RawParts>());
let parts = RawParts::pack_inline("hello");
assert!(parts.meta() != 0);
}
}