-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Related issue: #20
- Loading branch information
Showing
2 changed files
with
131 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
use std::{ | ||
fmt::Display, | ||
ops::{Deref, DerefMut}, | ||
str::{from_utf8_unchecked, from_utf8_unchecked_mut}, | ||
}; | ||
|
||
#[derive(Debug)] | ||
pub enum CowStr<'s> { | ||
Owned(String), | ||
Borrowed(&'s str), | ||
Inlined(InlineStr), | ||
} | ||
|
||
impl<'s> Deref for CowStr<'s> { | ||
type Target = str; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
match self { | ||
Self::Owned(ref s) => s, | ||
Self::Borrowed(s) => s, | ||
Self::Inlined(s) => s.deref(), | ||
} | ||
} | ||
} | ||
|
||
impl<'s> AsRef<str> for CowStr<'s> { | ||
fn as_ref(&self) -> &str { | ||
self.deref() | ||
} | ||
} | ||
|
||
impl<'s> From<&'s str> for CowStr<'s> { | ||
fn from(value: &'s str) -> Self { | ||
CowStr::Borrowed(value) | ||
} | ||
} | ||
|
||
impl<'s> From<String> for CowStr<'s> { | ||
fn from(value: String) -> Self { | ||
CowStr::Owned(value) | ||
} | ||
} | ||
|
||
impl<'s> Clone for CowStr<'s> { | ||
fn clone(&self) -> Self { | ||
match self { | ||
CowStr::Owned(s) => match InlineStr::try_from(&**s) { | ||
Ok(inline) => CowStr::Inlined(inline), | ||
Err(_) => CowStr::Owned(s.clone()), | ||
}, | ||
CowStr::Borrowed(s) => CowStr::Borrowed(s), | ||
CowStr::Inlined(s) => CowStr::Inlined(*s), | ||
} | ||
} | ||
} | ||
|
||
impl<'s> PartialEq for CowStr<'s> { | ||
fn eq(&self, other: &Self) -> bool { | ||
self.deref() == other.deref() | ||
} | ||
} | ||
|
||
impl<'s> Eq for CowStr<'s> {} | ||
|
||
impl<'s> Display for CowStr<'s> { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
f.write_str(self.deref()) | ||
} | ||
} | ||
|
||
impl<'s, 'a> FromIterator<&'a str> for CowStr<'s> { | ||
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self { | ||
CowStr::Owned(FromIterator::from_iter(iter)) | ||
} | ||
} | ||
|
||
const MAX_INLINE_STR_LEN: usize = 3 * std::mem::size_of::<isize>() - 2; | ||
|
||
pub struct Error; | ||
|
||
#[derive(Clone, Copy, Debug)] | ||
pub struct InlineStr { | ||
inner: [u8; MAX_INLINE_STR_LEN], | ||
len: usize, | ||
} | ||
|
||
impl Deref for InlineStr { | ||
type Target = str; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
// SAFETY: `InlineStr` can only be constructed from strings or chars, which means they are | ||
// guaranteed to be valid UTF-8. | ||
unsafe { from_utf8_unchecked(&self.inner[..self.len]) } | ||
} | ||
} | ||
|
||
impl DerefMut for InlineStr { | ||
fn deref_mut(&mut self) -> &mut Self::Target { | ||
// SAFETY: `InlineStr` can only be constructed from strings or chars, which means they are | ||
// guaranteed to be valid UTF-8. | ||
unsafe { from_utf8_unchecked_mut(&mut self.inner[..self.len]) } | ||
} | ||
} | ||
|
||
impl From<char> for InlineStr { | ||
fn from(value: char) -> Self { | ||
let mut inner = [0u8; MAX_INLINE_STR_LEN]; | ||
value.encode_utf8(&mut inner); | ||
Self { | ||
inner, | ||
len: value.len_utf8(), | ||
} | ||
} | ||
} | ||
|
||
impl TryFrom<&str> for InlineStr { | ||
type Error = Error; | ||
|
||
fn try_from(value: &str) -> Result<Self, Self::Error> { | ||
let len = value.len(); | ||
if len > MAX_INLINE_STR_LEN { | ||
Err(Error) | ||
} else { | ||
let mut inner = [0u8; MAX_INLINE_STR_LEN]; | ||
inner.copy_from_slice(value.as_bytes()); | ||
Ok(Self { inner, len }) | ||
} | ||
} | ||
} |