use std::borrow::Borrow;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::num::NonZeroUsize;
use std::ops::Deref;
use constptr::ConstPtr;
use crate::*;
#[derive(Debug)]
pub enum Error {
Capacity,
Range,
ThreadOwnership,
}
#[repr(transparent)]
#[derive(Debug)]
pub struct CowStr(CowStrInner);
#[derive(Debug)]
enum CowStrInner {
Static(&'static str),
Shared(RcString),
}
impl CowStrInner {
#[inline]
fn new_shared(ptr: RcString) -> CowStrInner {
CowStrInner::Shared(ptr)
}
}
impl CowStr {
#[must_use]
#[inline]
pub fn new() -> Self {
CowStr(CowStrInner::new_shared(RcString::allocate(0)))
}
#[must_use]
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
CowStr(CowStrInner::new_shared(RcString::allocate(capacity)))
}
#[must_use]
#[inline]
pub const fn from_static(s: &'static str) -> Self {
CowStr(CowStrInner::Static(s))
}
#[must_use]
#[inline]
pub fn replicate(&self) -> Self {
match &self.0 {
CowStrInner::Static(s) => CowStr(CowStrInner::Static(s)),
CowStrInner::Shared(_) => CowStr::from(self.as_str()),
}
}
#[deprecated(since = "0.9.0", note = "use CowStr::replicate() instead")]
#[allow(missing_docs)]
#[must_use]
#[inline]
#[mutants::skip]
pub fn copy(&self) -> Self {
self.replicate()
}
fn to_mut(&mut self, reserve: usize) -> &mut RcString {
match self.0 {
CowStrInner::Shared(r) if r.strong_count() > 1 => {
self.0 = CowStrInner::new_shared(RcString::from_str(r.as_str(), reserve));
if r.decrement_strong_count() {
unsafe { RcString::dealloc(r) };
}
}
CowStrInner::Shared(r) if r.spare_capacity() < reserve => {
self.0 = CowStrInner::new_shared(RcString::grow(r, reserve));
}
CowStrInner::Static(s) => {
self.0 = CowStrInner::new_shared(RcString::from_str(s, reserve));
}
CowStrInner::Shared(_) => { }
}
if let CowStrInner::Shared(ref mut rc) = self.0 {
debug_assert_eq!(rc.strong_count(), 1);
rc
} else {
unsafe { std::hint::unreachable_unchecked() }
}
}
#[inline]
pub fn push_char(&mut self, c: char) {
self.to_mut(c.len_utf8()).push(c);
}
#[doc(hidden)]
#[deprecated(since = "0.11.0", note = "use push_char()")]
#[mutants::skip]
#[inline]
pub fn push(&mut self, c: char) {
self.push_char(c);
}
#[inline]
pub fn push_str(&mut self, s: &str) {
self.to_mut(s.len()).push_str(s);
}
#[inline]
pub unsafe fn try_push_char(&self, c: char) -> Result<(), Error> {
match self.0 {
CowStrInner::Static(_) => Err(Error::Capacity),
CowStrInner::Shared(r) => r.try_push(c),
}
}
#[doc(hidden)]
#[deprecated(since = "0.11.0", note = "use try_push_char()")]
#[mutants::skip]
#[inline]
pub unsafe fn try_push(&mut self, c: char) -> Result<(), Error> {
self.try_push_char(c)
}
#[inline]
pub unsafe fn try_push_str(&self, s: &str) -> Result<(), Error> {
match self.0 {
CowStrInner::Static(_) => Err(Error::Capacity),
CowStrInner::Shared(r) => r.try_push_str(s),
}
}
#[cfg(feature = "multithreaded")]
#[inline]
pub fn guard(&self) -> Result<Guard, Error> {
match self.0 {
CowStrInner::Static(_) => Err(Error::Capacity),
CowStrInner::Shared(ref rc) => {
if rc.acquire() {
Ok(Guard(self.clone(), Default::default()))
} else {
Err(Error::ThreadOwnership)
}
}
}
}
#[must_use]
#[inline]
#[doc(hidden)]
#[deprecated(since = "0.11.2", note = "use SubStr::new()")]
pub fn into_substr(self) -> SubStr {
let substr = ConstPtr::from(&self[..]);
SubStr {
string: self,
substr,
}
}
#[doc(hidden)]
#[deprecated(since = "0.11.2", note = "use SubStr::new_range()")]
pub fn into_range(self, start: usize, end: usize) -> Result<SubStr, RangeError> {
if let Some(substr) = self.get(start..end) {
let substr = ConstPtr::from(substr);
Ok(SubStr {
string: self,
substr,
})
} else {
Err(RangeError)
}
}
#[inline]
pub fn deref_static(&self) -> Result<&'static str, &str> {
match &self.0 {
CowStrInner::Static(s) => Ok(s),
CowStrInner::Shared(s) => Err(s.as_str()),
}
}
#[must_use]
#[inline]
#[allow(clippy::missing_panics_doc)]
pub fn strong_count(&self) -> Option<NonZeroUsize> {
match self.0 {
CowStrInner::Static(_) => None,
CowStrInner::Shared(r) => Some(NonZeroUsize::new(r.strong_count()).unwrap()),
}
}
#[must_use]
#[inline]
pub fn as_str(&self) -> &str {
match self.deref_static() {
Ok(s) | Err(s) => s,
}
}
#[inline]
pub fn as_mut_str(&mut self) -> &mut str {
unsafe {
self.to_mut(0).as_mut_str()
}
}
#[must_use]
#[inline]
pub fn capacity(&self) -> usize {
match self.0 {
CowStrInner::Static(s) => s.len(),
CowStrInner::Shared(r) => r.capacity(),
}
}
#[must_use]
#[inline]
pub fn spare_capacity(&self) -> usize {
match self.0 {
CowStrInner::Static(_) => 0,
CowStrInner::Shared(r) => r.spare_capacity(),
}
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.to_mut(additional);
}
pub fn shrink_to(&mut self, new_capacity: usize) {
match self.0 {
CowStrInner::Shared(r) if r.strong_count() == 1 => {
self.0 = CowStrInner::new_shared(RcString::shrink(r, new_capacity));
}
_ => { }
}
}
#[inline]
#[mutants::skip]
pub fn shrink_to_fit(&mut self) {
self.shrink_to(0);
}
#[inline]
pub fn shrink_clone(&mut self) -> Self {
self.shrink_to(0);
self.clone()
}
#[inline]
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
self.as_str().as_bytes()
}
#[inline]
#[must_use]
pub fn is_same_as(&self, other: &Self) -> bool {
std::ptr::eq(self.as_str(), other.as_str())
}
#[must_use]
pub fn range_of(&self, substr: &str) -> std::ops::Range<usize> {
assert!(
self.as_bytes().as_ptr_range().contains(&substr.as_ptr()),
"`substr` is not part of `self`"
);
let start = (substr.as_ptr() as usize) - (self.as_ptr() as usize);
start..start + substr.len()
}
}
impl Clone for CowStr {
fn clone(&self) -> Self {
match self.0 {
CowStrInner::Static(s) => CowStr(CowStrInner::Static(s)),
CowStrInner::Shared(rc) => {
rc.increment_strong_count();
CowStr(CowStrInner::Shared(rc))
}
}
}
}
impl Drop for CowStr {
fn drop(&mut self) {
match self.0 {
CowStrInner::Shared(rc) => unsafe {
if rc.decrement_strong_count() {
RcString::dealloc(rc);
}
},
CowStrInner::Static(_) => { }
}
}
}
impl Default for CowStr {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl From<&str> for CowStr {
#[inline]
fn from(source: &str) -> Self {
CowStr(CowStrInner::new_shared(RcString::from_str(source, 0)))
}
}
impl Deref for CowStr {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl AsRef<str> for CowStr {
#[inline]
fn as_ref(&self) -> &str {
self
}
}
impl Borrow<str> for CowStr {
#[inline]
fn borrow(&self) -> &str {
self
}
}
impl Eq for CowStr {}
impl PartialEq for CowStr {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}
impl PartialOrd for CowStr {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.as_ref().partial_cmp(other.as_ref())
}
}
impl Ord for CowStr {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_ref().cmp(other.as_ref())
}
}
impl Hash for CowStr {
#[inline]
#[mutants::skip]
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl fmt::Display for CowStr {
#[inline]
#[mutants::skip]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
#[derive(Default)]
struct NotSend(std::marker::PhantomData<*const ()>);
#[cfg(feature = "multithreaded")]
pub struct Guard(CowStr, NotSend);
#[cfg(feature = "multithreaded")]
impl Drop for Guard {
fn drop(&mut self) {
if let CowStrInner::Shared(r) = self.0 .0 {
r.release();
}
}
}
impl Deref for Guard {
type Target = CowStr;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[test]
fn smoke() {
let _empty_string = CowStr::new();
let _static_string = CowStr::from_static("smoke 1");
let _dynamic_string = CowStr::from("smoke 2");
}
#[test]
fn size() {
assert_eq!(std::mem::size_of::<CowStr>(), std::mem::size_of::<&str>());
}
#[test]
fn as_str_static() {
let my_string = CowStr::from_static("as_str_static");
assert_eq!(my_string.as_str(), "as_str_static");
}
#[test]
fn as_str_dynamic() {
let my_string = CowStr::from("as_str_dynamic");
let mut cloned = my_string.clone();
cloned.push_str(" foobar");
assert_eq!(my_string.as_str(), "as_str_dynamic");
assert_eq!(cloned.as_str(), "as_str_dynamic foobar");
}
#[test]
fn as_mut_str() {
let mut my_string = CowStr::from_static("as_mut_str");
assert_eq!(my_string.as_mut_str(), "as_mut_str");
}
#[test]
#[should_panic(expected = "Capacity overflow")]
fn over_capacity() {
let _boom = CowStr::with_capacity(usize::MAX);
}
#[test]
fn push_char() {
let mut my_string = CowStr::from_static("push_char");
my_string.push_char(' ');
assert_eq!(&*my_string, "push_char ");
my_string.push_char('1');
my_string.push_char('2');
my_string.push_char('3');
my_string.push_char('4');
my_string.push_char('5');
my_string.push_char('6');
my_string.push_char('7');
my_string.push_char('8');
assert_eq!(&*my_string, "push_char 12345678");
}
#[test]
fn push_str() {
let mut my_string = CowStr::from_static("push_str");
my_string.push_str(" foo");
assert_eq!(&*my_string, "push_str foo");
my_string.push_str(" bar");
assert_eq!(&*my_string, "push_str foo bar");
}
#[cfg(feature = "multithreaded")]
#[test]
fn try_push() {
let my_string = CowStr::with_capacity(100);
let cloned = my_string.clone();
let guard = my_string.guard().unwrap();
unsafe {
guard.try_push_str("try_push ").unwrap();
guard.try_push_char('1').unwrap();
guard.try_push_char('2').unwrap();
guard.try_push_char('3').unwrap();
guard.try_push_char('4').unwrap();
guard.try_push_char('5').unwrap();
guard.try_push_char('6').unwrap();
guard.try_push_char('7').unwrap();
guard.try_push_char('8').unwrap();
}
assert_eq!(&*cloned, "try_push 12345678");
}
#[test]
#[should_panic]
#[cfg(feature = "multithreaded")]
fn two_guard() {
let my_string = CowStr::with_capacity(100);
let _guard1 = my_string.guard().unwrap();
let _guard2 = my_string.guard().unwrap();
}
#[test]
fn clone_static() {
let my_string = CowStr::from_static("clone_static");
assert_eq!(my_string.strong_count(), None);
assert!(my_string.deref_static().is_ok());
let my_string2 = my_string.clone();
assert!(my_string.deref_static().is_ok());
assert_eq!(&*my_string2, "clone_static");
drop(my_string);
assert_eq!(&*my_string2, "clone_static");
}
#[test]
fn clone_dynamic() {
let my_string = CowStr::from("clone_dynamic");
assert_eq!(
my_string.strong_count(),
Some(NonZeroUsize::new(1).unwrap())
);
let my_string2 = my_string.clone();
assert_eq!(
my_string.strong_count(),
Some(NonZeroUsize::new(2).unwrap())
);
assert_eq!(
my_string2.strong_count(),
Some(NonZeroUsize::new(2).unwrap())
);
assert_eq!(&*my_string2, "clone_dynamic");
let my_string3 = my_string.clone();
assert_eq!(
my_string3.strong_count(),
Some(NonZeroUsize::new(3).unwrap())
);
drop(my_string);
assert_eq!(
my_string2.strong_count(),
Some(NonZeroUsize::new(2).unwrap())
);
}
#[test]
fn replicate() {
let my_string = CowStr::from("replicate");
assert_eq!(
my_string.strong_count(),
Some(NonZeroUsize::new(1).unwrap())
);
let my_string2 = my_string.replicate();
assert_eq!(
my_string.strong_count(),
Some(NonZeroUsize::new(1).unwrap())
);
assert_eq!(
my_string2.strong_count(),
Some(NonZeroUsize::new(1).unwrap())
);
assert_eq!(my_string, my_string2);
}
#[test]
fn cmp() {
let my_static_string = CowStr::from_static("cmp");
let my_dynamic_string = CowStr::from("cmp");
let my_dynamic_string2 = CowStr::from("other cmp");
assert_eq!(my_static_string, my_dynamic_string);
assert_ne!(my_dynamic_string, my_dynamic_string2);
}
#[test]
fn ord() {
let my_static_string = CowStr::from_static("a ord");
let my_dynamic_string = CowStr::from("b ord");
assert!(my_static_string < my_dynamic_string);
}
#[test]
fn as_ref() {
let my_string = CowStr::from("as_ref");
assert_eq!(my_string.deref(), my_string.as_ref());
}
#[test]
fn borrow() {
let my_string = CowStr::from("borrow");
assert_eq!(
my_string.deref(),
<cowstr::CowStr as Borrow<str>>::borrow(&my_string)
);
}
#[test]
#[ignore]
fn print() {
let my_string = CowStr::from_static("static string");
println!("static_string = {my_string}");
let my_string = CowStr::from("dynamic string");
println!("dynamic_string = {my_string}");
}