use alloc::borrow::{Borrow, Cow};
use alloc::boxed::Box;
use alloc::string::String;
use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::marker::PhantomData;
use core::ops::Deref;
use core::ptr::NonNull;
const OWNED: usize = 1 << (usize::BITS - 1);
const TAG_SHIFT: u32 = usize::BITS - 4;
const TAG: usize = 0b111 << TAG_SHIFT;
const LEN: usize = !(OWNED | TAG);
pub(crate) struct Slice<'a> {
ptr: NonNull<u8>,
len: usize,
_marker: PhantomData<&'a [u8]>,
}
unsafe impl Send for Slice<'_> {}
unsafe impl Sync for Slice<'_> {}
impl<'a> Slice<'a> {
#[inline]
pub(crate) const fn borrowed(data: &'a [u8]) -> Slice<'a> {
assert!(data.len() <= LEN, "slice too large");
Slice {
ptr: unsafe { NonNull::new_unchecked(data.as_ptr().cast_mut()) },
len: data.len(),
_marker: PhantomData,
}
}
#[inline]
pub(crate) fn owned(data: Box<[u8]>) -> Slice<'a> {
let len = data.len();
assert!(len <= LEN, "slice too large");
let ptr = Box::into_raw(data).cast::<u8>();
Slice {
ptr: unsafe { NonNull::new_unchecked(ptr) },
len: len | OWNED,
_marker: PhantomData,
}
}
#[inline]
pub(crate) fn len(&self) -> usize {
self.len & LEN
}
#[inline]
pub(crate) fn tag(&self) -> u8 {
((self.len & TAG) >> TAG_SHIFT) as u8
}
#[inline]
pub(crate) fn with_tag(mut self, tag: u8) -> Slice<'a> {
debug_assert!(tag <= 7);
self.len = (self.len & !TAG) | ((tag as usize) << TAG_SHIFT) & TAG;
self
}
#[inline]
pub(crate) fn is_owned(&self) -> bool {
self.len & OWNED != 0
}
#[inline]
pub(crate) fn as_slice(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len()) }
}
#[inline]
pub(crate) fn borrowed_slice(&self) -> Option<&'a [u8]> {
if self.is_owned() {
None
} else {
Some(unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len()) })
}
}
#[inline]
pub(crate) fn reborrow(&self) -> Slice<'_> {
Slice::borrowed(self.as_slice())
}
#[inline]
pub(crate) fn into_box(self) -> Box<[u8]> {
if self.is_owned() {
let len = self.len();
let ptr = self.ptr.as_ptr();
core::mem::forget(self);
unsafe { Box::from_raw(core::ptr::slice_from_raw_parts_mut(ptr, len)) }
} else {
Box::from(self.as_slice())
}
}
#[inline]
pub(crate) fn into_cow(self) -> Cow<'a, [u8]> {
match self.borrowed_slice() {
Some(data) => Cow::Borrowed(data),
None => Cow::Owned(self.into_box().into_vec()),
}
}
#[inline]
pub(crate) fn from_cow(data: Cow<'a, [u8]>) -> Slice<'a> {
match data {
Cow::Borrowed(data) => Slice::borrowed(data),
Cow::Owned(data) => Slice::owned(data.into_boxed_slice()),
}
}
#[inline]
pub(crate) fn to_static(&self) -> Slice<'static> {
Slice::owned(Box::from(self.as_slice()))
}
}
impl Drop for Slice<'_> {
#[inline]
fn drop(&mut self) {
if self.is_owned() {
drop(unsafe {
Box::from_raw(core::ptr::slice_from_raw_parts_mut(
self.ptr.as_ptr(),
self.len(),
))
});
}
}
}
impl Clone for Slice<'_> {
#[inline]
fn clone(&self) -> Self {
if self.is_owned() {
Slice::owned(Box::from(self.as_slice())).with_tag(self.tag())
} else {
Slice {
ptr: self.ptr,
len: self.len,
_marker: PhantomData,
}
}
}
}
#[derive(Clone)]
pub struct Text<'a>(Slice<'a>);
impl<'a> Text<'a> {
#[inline]
pub const fn borrowed(text: &'a str) -> Text<'a> {
Text(Slice::borrowed(text.as_bytes()))
}
#[inline]
pub fn owned<S: Into<Box<str>>>(text: S) -> Text<'a> {
Text(Slice::owned(text.into().into_boxed_bytes()))
}
#[inline]
pub(crate) fn tag(&self) -> u8 {
self.0.tag()
}
#[inline]
pub(crate) fn with_tag(self, tag: u8) -> Text<'a> {
Text(self.0.with_tag(tag))
}
#[inline]
pub fn as_str(&self) -> &str {
unsafe { core::str::from_utf8_unchecked(self.0.as_slice()) }
}
#[inline]
pub fn borrowed_str(&self) -> Option<&'a str> {
self.0
.borrowed_slice()
.map(|data| unsafe { core::str::from_utf8_unchecked(data) })
}
#[inline]
pub fn is_borrowed(&self) -> bool {
!self.0.is_owned()
}
#[inline]
pub fn as_borrowed(&self) -> Text<'_> {
Text(self.0.reborrow())
}
#[inline]
pub fn to_static(&self) -> Text<'static> {
Text(self.0.to_static())
}
#[inline]
pub fn into_cow(self) -> Cow<'a, str> {
match self.0.into_cow() {
Cow::Borrowed(data) => Cow::Borrowed(unsafe { core::str::from_utf8_unchecked(data) }),
Cow::Owned(data) => Cow::Owned(unsafe { String::from_utf8_unchecked(data) }),
}
}
#[inline]
pub fn into_owned(self) -> String {
self.into_cow().into_owned()
}
}
impl Default for Text<'_> {
#[inline]
fn default() -> Self {
Text::borrowed("")
}
}
impl Deref for Text<'_> {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.as_str()
}
}
impl AsRef<str> for Text<'_> {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for Text<'_> {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl<'a> From<&'a str> for Text<'a> {
#[inline]
fn from(text: &'a str) -> Text<'a> {
Text::borrowed(text)
}
}
impl<'a> From<&'a String> for Text<'a> {
#[inline]
fn from(text: &'a String) -> Text<'a> {
Text::borrowed(text)
}
}
impl From<String> for Text<'_> {
#[inline]
fn from(text: String) -> Self {
Text::owned(text)
}
}
impl From<Box<str>> for Text<'_> {
#[inline]
fn from(text: Box<str>) -> Self {
Text::owned(text)
}
}
impl<'a> From<Cow<'a, str>> for Text<'a> {
#[inline]
fn from(text: Cow<'a, str>) -> Text<'a> {
match text {
Cow::Borrowed(text) => Text::borrowed(text),
Cow::Owned(text) => Text::owned(text),
}
}
}
impl<'a> From<Text<'a>> for Cow<'a, str> {
#[inline]
fn from(text: Text<'a>) -> Cow<'a, str> {
text.into_cow()
}
}
impl From<Text<'_>> for String {
#[inline]
fn from(text: Text<'_>) -> String {
text.into_owned()
}
}
impl fmt::Debug for Text<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_str(), f)
}
}
impl fmt::Display for Text<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
impl PartialEq for Text<'_> {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl Eq for Text<'_> {}
impl PartialOrd for Text<'_> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Text<'_> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl Hash for Text<'_> {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state)
}
}
impl PartialEq<str> for Text<'_> {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<&str> for Text<'_> {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl PartialEq<String> for Text<'_> {
#[inline]
fn eq(&self, other: &String) -> bool {
self.as_str() == other
}
}
impl PartialEq<Text<'_>> for str {
#[inline]
fn eq(&self, other: &Text<'_>) -> bool {
self == other.as_str()
}
}
impl PartialEq<Text<'_>> for &str {
#[inline]
fn eq(&self, other: &Text<'_>) -> bool {
*self == other.as_str()
}
}
#[test]
fn test_text() {
assert_eq!(core::mem::size_of::<Text>(), 16);
assert_eq!(core::mem::size_of::<Option<Text>>(), 16);
let borrowed = Text::from("borrowed");
assert!(borrowed.is_borrowed());
assert_eq!(borrowed.borrowed_str(), Some("borrowed"));
assert_eq!(borrowed.clone(), "borrowed");
assert_eq!(borrowed.to_static(), "borrowed");
assert!(!borrowed.to_static().is_borrowed());
let mut owned = String::with_capacity(64);
owned.push_str("owned");
let owned = Text::from(owned);
assert!(!owned.is_borrowed());
assert_eq!(owned.borrowed_str(), None);
assert_eq!(owned.as_borrowed().borrowed_str(), Some("owned"));
assert_eq!(owned.clone(), owned);
assert_eq!(owned.clone().into_owned(), "owned");
assert_eq!(owned.into_cow(), Cow::<str>::Owned("owned".into()));
let empty = Text::from(String::new());
assert_eq!(empty, "");
assert_eq!(empty.into_owned(), "");
assert_eq!(Text::default(), "");
}
#[test]
fn test_text_tag() {
for text in [Text::from("tagged"), Text::from(String::from("tagged"))] {
let owned = !text.is_borrowed();
let tagged = text.with_tag(5);
assert_eq!(tagged.tag(), 5);
assert_eq!(tagged, "tagged");
assert_eq!(tagged.len(), 6);
assert_eq!(tagged.is_borrowed(), !owned);
assert_eq!(tagged.clone().tag(), 5);
assert_eq!(tagged.as_borrowed().tag(), 0);
assert_eq!(tagged.to_static().tag(), 0);
let retagged = tagged.with_tag(2);
assert_eq!(retagged.tag(), 2);
assert_eq!(retagged.clone().into_owned(), "tagged");
assert_eq!(retagged.with_tag(0).tag(), 0);
}
}