asciidoc_parser/
strings.rsuse std::{
borrow::{Borrow, Cow},
fmt,
hash::{Hash, Hasher},
ops::Deref,
str::from_utf8,
};
pub(crate) const MAX_INLINE_STR_LEN: usize = 3 * std::mem::size_of::<isize>() - 2;
#[derive(Debug)]
pub struct StringTooLongError;
#[derive(Debug, Clone, Copy, Eq)]
pub struct InlineStr {
inner: [u8; MAX_INLINE_STR_LEN],
len: u8,
}
impl AsRef<str> for InlineStr {
fn as_ref(&self) -> &str {
self.deref()
}
}
impl Hash for InlineStr {
fn hash<H: Hasher>(&self, state: &mut H) {
self.deref().hash(state);
}
}
impl From<char> for InlineStr {
fn from(c: char) -> Self {
let mut inner = [0u8; MAX_INLINE_STR_LEN];
c.encode_utf8(&mut inner);
let len = c.len_utf8() as u8;
Self { inner, len }
}
}
impl std::cmp::PartialEq<InlineStr> for InlineStr {
fn eq(&self, other: &InlineStr) -> bool {
self.deref() == other.deref()
}
}
impl TryFrom<&str> for InlineStr {
type Error = StringTooLongError;
fn try_from(s: &str) -> Result<InlineStr, StringTooLongError> {
let len = s.len();
if len <= MAX_INLINE_STR_LEN {
let mut inner = [0u8; MAX_INLINE_STR_LEN];
inner[..len].copy_from_slice(s.as_bytes());
let len = len as u8;
Ok(Self { inner, len })
} else {
Err(StringTooLongError)
}
}
}
impl Deref for InlineStr {
type Target = str;
fn deref(&self) -> &str {
let len = self.len as usize;
from_utf8(&self.inner[..len]).unwrap_or_default()
}
}
impl fmt::Display for InlineStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_ref())
}
}
#[derive(Debug, Eq)]
pub enum CowStr<'a> {
Boxed(Box<str>),
Borrowed(&'a str),
Inlined(InlineStr),
}
impl<'a> AsRef<str> for CowStr<'a> {
fn as_ref(&self) -> &str {
self.deref()
}
}
impl<'a> Hash for CowStr<'a> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.deref().hash(state);
}
}
impl<'a> std::clone::Clone for CowStr<'a> {
fn clone(&self) -> Self {
match self {
CowStr::Boxed(s) => match InlineStr::try_from(&**s) {
Ok(inline) => CowStr::Inlined(inline),
Err(..) => CowStr::Boxed(s.clone()),
},
CowStr::Borrowed(s) => CowStr::Borrowed(s),
CowStr::Inlined(s) => CowStr::Inlined(*s),
}
}
}
impl<'a> std::cmp::PartialEq<CowStr<'a>> for CowStr<'a> {
fn eq(&self, other: &CowStr<'_>) -> bool {
self.deref() == other.deref()
}
}
impl<'a> From<&'a str> for CowStr<'a> {
fn from(s: &'a str) -> Self {
CowStr::Borrowed(s)
}
}
impl<'a> From<String> for CowStr<'a> {
fn from(s: String) -> Self {
CowStr::Boxed(s.into_boxed_str())
}
}
impl<'a> From<char> for CowStr<'a> {
fn from(c: char) -> Self {
CowStr::Inlined(c.into())
}
}
impl<'a> From<Cow<'a, str>> for CowStr<'a> {
fn from(s: Cow<'a, str>) -> Self {
match s {
Cow::Borrowed(s) => CowStr::Borrowed(s),
Cow::Owned(s) => CowStr::Boxed(s.into_boxed_str()),
}
}
}
impl<'a> From<CowStr<'a>> for Cow<'a, str> {
fn from(s: CowStr<'a>) -> Self {
match s {
CowStr::Boxed(s) => Cow::Owned(s.to_string()),
CowStr::Inlined(s) => Cow::Owned(s.to_string()),
CowStr::Borrowed(s) => Cow::Borrowed(s),
}
}
}
impl<'a> From<Cow<'a, char>> for CowStr<'a> {
fn from(s: Cow<'a, char>) -> Self {
CowStr::Inlined(InlineStr::from(*s))
}
}
impl<'a> Deref for CowStr<'a> {
type Target = str;
fn deref(&self) -> &str {
match self {
CowStr::Boxed(ref b) => b,
CowStr::Borrowed(b) => b,
CowStr::Inlined(ref s) => s.deref(),
}
}
}
impl<'a> Borrow<str> for CowStr<'a> {
fn borrow(&self) -> &str {
self.deref()
}
}
impl<'a> CowStr<'a> {
pub fn into_string(self) -> String {
match self {
CowStr::Boxed(b) => b.into(),
CowStr::Borrowed(b) => b.to_owned(),
CowStr::Inlined(s) => s.deref().to_owned(),
}
}
}
impl<'a> fmt::Display for CowStr<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_ref())
}
}