use std::borrow::Borrow;
use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::num::NonZeroUsize;
use std::ops::Deref;
use std::ptr::NonNull;
use crate::*;
#[repr(transparent)]
#[derive(Debug, Eq)]
pub struct CowStr(CowStrInner);
#[derive(Debug, Eq, PartialEq)]
enum CowStrInner {
Static(&'static str),
Shared(SharedInner),
}
impl CowStrInner {
#[inline]
fn new_shared(ptr: Option<NonNull<RcString>>) -> CowStrInner {
CowStrInner::Shared(SharedInner(ptr.expect("Out of Memory")))
}
}
impl CowStr {
#[must_use]
pub fn new() -> Self {
CowStr(CowStrInner::new_shared(RcString::allocate(0)))
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
CowStr(CowStrInner::new_shared(RcString::allocate(capacity)))
}
#[must_use]
pub fn from_static(s: &'static str) -> Self {
CowStr(CowStrInner::Static(s))
}
fn to_mut(&mut self, reserve: usize) -> &mut RcString {
match self.rcstring() {
Ok(r) if r.as_ref().strong_count() > 1 => {
self.0 = CowStrInner::new_shared(RcString::from_str(r.as_str(), reserve));
}
Ok(r) if r.as_ref().spare_capacity() < reserve => {
self.0 = CowStrInner::new_shared(RcString::grow(r.as_ptr(), reserve));
}
Err(s) => {
self.0 = CowStrInner::new_shared(RcString::from_str(s, reserve));
}
Ok(_) => { }
}
if let CowStrInner::Shared(ref mut rc) = self.0 {
debug_assert_eq!(rc.as_ref().strong_count(), 1);
rc.as_mut()
} else {
unsafe { std::hint::unreachable_unchecked() }
}
}
#[inline(always)]
fn rcstring(&self) -> Result<&SharedInner, &str> {
match &self.0 {
CowStrInner::Static(s) => Err(s),
CowStrInner::Shared(rc) => Ok(rc),
}
}
pub fn push(&mut self, c: char) {
self.to_mut(c.len_utf8()).push(c);
}
pub fn push_str(&mut self, s: &str) {
self.to_mut(s.len()).push_str(s);
}
#[must_use]
pub fn into_substr(self) -> SubStr {
let substr = &self[..] as *const str;
SubStr {
string: self,
substr,
}
}
pub fn into_range(self, start: usize, end: usize) -> Result<SubStr, RangeError> {
if let Some(substr) = self.get(start..end) {
let substr = substr as *const str;
Ok(SubStr {
string: self,
substr,
})
} else {
Err(RangeError)
}
}
pub fn deref_static(&self) -> Result<&'static str, &str> {
match &self.0 {
CowStrInner::Static(s) => Ok(s),
CowStrInner::Shared(s) => Err(s.as_ref().as_str()),
}
}
#[must_use]
#[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.as_ref().strong_count()).unwrap()),
}
}
#[must_use]
pub fn as_str(&self) -> &str {
match self.deref_static() {
Ok(s) | Err(s) => s,
}
}
pub fn as_mut_str(&mut self) -> &mut str {
self.to_mut(0).as_mut_str()
}
#[must_use]
pub fn capacity(&self) -> usize {
match self.0 {
CowStrInner::Static(s) => s.len(),
CowStrInner::Shared(r) => r.as_ref().capacity(),
}
}
#[must_use]
pub fn spare_capacity(&self) -> usize {
match self.0 {
CowStrInner::Static(_) => 0,
CowStrInner::Shared(r) => r.as_ref().spare_capacity(),
}
}
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.as_ref().strong_count() == 1 => {
self.0 = CowStrInner::new_shared(RcString::shrink(r.as_ptr(), new_capacity));
}
_ => { }
}
}
pub fn shrink_to_fit(&mut self) {
self.shrink_to(0);
}
pub fn as_bytes(&self) -> &[u8] {
self.as_str().as_bytes()
}
}
impl Clone for CowStr {
fn clone(&self) -> Self {
match self.0 {
CowStrInner::Static(s) => CowStr(CowStrInner::Static(s)),
CowStrInner::Shared(rc) => {
rc.as_ref().increment_strong_count();
CowStr(CowStrInner::Shared(rc))
}
}
}
}
impl Drop for CowStr {
fn drop(&mut self) {
match self.0 {
CowStrInner::Shared(rc) => unsafe {
if rc.as_ref().decrement_strong_count() == 0 {
RcString::dealloc(rc.0);
}
},
CowStrInner::Static(_) => { }
}
}
}
impl Default for CowStr {
fn default() -> Self {
Self::new()
}
}
impl From<&str> for CowStr {
fn from(source: &str) -> Self {
CowStr(CowStrInner::new_shared(RcString::from_str(source, 0)))
}
}
impl Deref for CowStr {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl AsRef<str> for CowStr {
fn as_ref(&self) -> &str {
self
}
}
impl Borrow<str> for CowStr {
fn borrow(&self) -> &str {
self
}
}
impl PartialEq for CowStr {
fn eq(&self, other: &Self) -> bool {
self.as_ref() == other.as_ref()
}
}
impl PartialOrd for CowStr {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.as_ref().partial_cmp(other.as_ref())
}
}
impl Ord for CowStr {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.as_ref().cmp(other.as_ref())
}
}
impl Hash for CowStr {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
impl fmt::Display for CowStr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
struct SharedInner(NonNull<RcString>);
impl SharedInner {
#[inline]
fn as_ptr(&self) -> NonNull<RcString> {
self.0
}
#[inline]
fn as_str(&self) -> &str {
unsafe { self.0.as_ref().as_str() }
}
#[inline]
fn as_ref(&self) -> &RcString {
unsafe { self.0.as_ref() }
}
#[inline]
fn as_mut(&mut self) -> &mut RcString {
unsafe { self.0.as_mut() }
}
}
#[test]
fn smoke() {
let _empty_string = CowStr::new();
let _static_string = CowStr::from_static("test string");
let _dynamic_string = CowStr::from("test string");
}
#[test]
#[should_panic(expected = "Capacity overflow")]
fn over_capacity() {
let _boom = CowStr::with_capacity(usize::MAX);
}
#[test]
#[cfg(not(miri))] #[should_panic(expected = "Out of Memory")]
fn allocation_failure() {
let _boom = CowStr::with_capacity(usize::MAX - 1000);
}
#[test]
fn push_char() {
let mut my_string = CowStr::from_static("test string");
my_string.push(' ');
assert_eq!(&*my_string, "test string ");
my_string.push('1');
my_string.push('2');
my_string.push('3');
my_string.push('4');
my_string.push('5');
my_string.push('6');
my_string.push('7');
my_string.push('8');
assert_eq!(&*my_string, "test string 12345678");
}
#[test]
fn push_str() {
let mut my_string = CowStr::from_static("test string");
my_string.push_str(" foo");
assert_eq!(&*my_string, "test string foo");
my_string.push_str(" bar");
assert_eq!(&*my_string, "test string foo bar");
}
#[test]
fn clone_static() {
let my_string = CowStr::from_static("test string");
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, "test string");
drop(my_string);
assert_eq!(&*my_string2, "test string");
}
#[test]
fn clone_dynamic() {
let my_string = CowStr::from("test string");
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, "test string");
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 cmp() {
let my_static_string = CowStr::from_static("test string");
let my_dynamic_string = CowStr::from("test string");
let my_dynamic_string2 = CowStr::from("test string");
assert_eq!(my_static_string, my_dynamic_string);
assert_eq!(my_dynamic_string, my_dynamic_string2);
}
#[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}");
}