use core::borrow::Borrow;
use core::ops::{Deref, Index};
use core::{fmt, str};
use std::cmp::Ordering;
use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::mem::ManuallyDrop;
use std::ops::DerefMut;
use std::ptr;
use std::slice::SliceIndex;
use std::str::{from_utf8_unchecked, from_utf8_unchecked_mut};
use crate::list::ArchivedList;
use crate::primitive::FixedUsize;
pub const INLINE_CAPACITY: usize = size_of::<ArchivedList<u8>>();
pub const OUT_OF_LINE_CAPACITY: usize = (1 << (FixedUsize::BITS - 2)) - 1;
pub union ArchivedString {
pub(crate) out_of_line: ManuallyDrop<ArchivedList<u8>>,
pub(crate) inline: [u8; INLINE_CAPACITY],
}
pub(crate) enum DecodedString {
Inline,
OutOfLine { len: usize, offset: usize },
}
impl ArchivedString {
#[inline]
pub(crate) fn decode(&self) -> DecodedString {
unsafe {
if self.inline[0] & 0b1100_0000 == 0b1000_0000 {
let len = self.out_of_line.len.to_native() as usize;
#[cfg(not(feature = "big_endian"))]
let len = (len & 0b0011_1111) | ((len & !0xff) >> 2);
#[cfg(feature = "big_endian")]
let len = len & (FixedUsize::MAX >> 2);
DecodedString::OutOfLine {
len,
offset: self.out_of_line.offset.to_native() as usize,
}
} else {
DecodedString::Inline
}
}
}
#[inline]
fn as_ptr(&self) -> *const [u8] {
unsafe {
match self.decode() {
DecodedString::Inline => {
if let Some(string_end) = self.inline.iter().position(|&b| b == 0xff) {
&raw const self.inline[..string_end]
} else {
&raw const self.inline
}
}
DecodedString::OutOfLine { len, offset } => {
ptr::from_raw_parts(<*const ArchivedString>::cast::<u8>(self).add(offset), len)
}
}
}
}
#[inline]
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
unsafe { &*self.as_ptr() }
}
#[inline]
#[must_use]
pub fn as_str(&self) -> &str {
unsafe { from_utf8_unchecked(self.as_bytes()) }
}
#[inline]
#[must_use]
pub fn as_str_mut(&mut self) -> &mut str {
unsafe { from_utf8_unchecked_mut(&mut *self.as_ptr().cast_mut()) }
}
}
impl AsRef<str> for ArchivedString {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for ArchivedString {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl Debug for ArchivedString {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(self.as_str(), f)
}
}
impl Deref for ArchivedString {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.as_str()
}
}
impl DerefMut for ArchivedString {
#[inline]
fn deref_mut(&mut self) -> &mut str {
self.as_str_mut()
}
}
impl Display for ArchivedString {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.pad(self.as_str())
}
}
impl Eq for ArchivedString {}
impl Hash for ArchivedString {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state);
}
}
impl<Idx: SliceIndex<str>> Index<Idx> for ArchivedString {
type Output = Idx::Output;
#[inline]
fn index(&self, index: Idx) -> &Idx::Output {
self.as_str().index(index)
}
}
impl Ord for ArchivedString {
#[inline]
fn cmp(&self, other: &ArchivedString) -> Ordering {
self.as_bytes().cmp(other.as_bytes())
}
}
impl PartialEq for ArchivedString {
#[inline]
fn eq(&self, other: &ArchivedString) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl PartialOrd for ArchivedString {
#[inline]
fn partial_cmp(&self, other: &ArchivedString) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq<&str> for ArchivedString {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl PartialEq<str> for ArchivedString {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl PartialEq<ArchivedString> for &str {
#[inline]
fn eq(&self, other: &ArchivedString) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl PartialEq<ArchivedString> for str {
#[inline]
fn eq(&self, other: &ArchivedString) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl PartialOrd<&str> for ArchivedString {
#[inline]
fn partial_cmp(&self, other: &&str) -> Option<Ordering> {
Some(self.as_bytes().cmp(other.as_bytes()))
}
}
impl PartialOrd<str> for ArchivedString {
#[inline]
fn partial_cmp(&self, other: &str) -> Option<Ordering> {
Some(self.as_bytes().cmp(other.as_bytes()))
}
}
impl PartialOrd<ArchivedString> for &str {
#[inline]
fn partial_cmp(&self, other: &ArchivedString) -> Option<Ordering> {
Some(self.as_bytes().cmp(other.as_bytes()))
}
}
impl PartialOrd<ArchivedString> for str {
#[inline]
fn partial_cmp(&self, other: &ArchivedString) -> Option<Ordering> {
Some(self.as_bytes().cmp(other.as_bytes()))
}
}