Skip to content

Commit

Permalink
feat: add custom CowStr type
Browse files Browse the repository at this point in the history
Related issue: #20
  • Loading branch information
kmaasrud committed Mar 15, 2023
1 parent 16491a4 commit 3f89525
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ mod block;
mod inline;
mod lex;
mod span;
mod string;
mod tree;

use span::DiscontinuousString;
use span::Span;

pub use attr::{AttributeValue, AttributeValueParts, Attributes};
pub use string::CowStr as JotdownCowStr;

type CowStr<'s> = std::borrow::Cow<'s, str>;

Expand Down
129 changes: 129 additions & 0 deletions src/string.rs
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 })
}
}
}

0 comments on commit 3f89525

Please sign in to comment.