maciejhirsz / beef

Faster, more compact implementation of std::borrow::Cow
https://crates.io/crates/beef
Apache License 2.0
335 stars 17 forks source link

implement ops::Add for cow<str>? #53

Open lithbitren opened 1 year ago

lithbitren commented 1 year ago

just like:

std::borrow::Cow

check that Cow<'a, str> implements addition

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> Add<&'a str> for Cow<'a, str> {
    type Output = Cow<'a, str>;

    #[inline]
    fn add(mut self, rhs: &'a str) -> Self::Output {
        self += rhs;
        self
    }
}

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> Add<Cow<'a, str>> for Cow<'a, str> {
    type Output = Cow<'a, str>;

    #[inline]
    fn add(mut self, rhs: Cow<'a, str>) -> Self::Output {
        self += rhs;
        self
    }
}

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> AddAssign<&'a str> for Cow<'a, str> {
    fn add_assign(&mut self, rhs: &'a str) {
        if self.is_empty() {
            *self = Cow::Borrowed(rhs)
        } else if !rhs.is_empty() {
            if let Cow::Borrowed(lhs) = *self {
                let mut s = String::with_capacity(lhs.len() + rhs.len());
                s.push_str(lhs);
                *self = Cow::Owned(s);
            }
            self.to_mut().push_str(rhs);
        }
    }
}

#[cfg(not(no_global_oom_handling))]
#[stable(feature = "cow_add", since = "1.14.0")]
impl<'a> AddAssign<Cow<'a, str>> for Cow<'a, str> {
    fn add_assign(&mut self, rhs: Cow<'a, str>) {
        if self.is_empty() {
            *self = rhs
        } else if !rhs.is_empty() {
            if let Cow::Borrowed(lhs) = *self {
                let mut s = String::with_capacity(lhs.len() + rhs.len());
                s.push_str(lhs);
                *self = Cow::Owned(s);
            }
            self.to_mut().push_str(&rhs);
        }
    }
}
use std::borrow::Cow;

// check that Cow<'a, str> implements addition
#[test]
fn check_cow_add_cow() {
    let borrowed1 = Cow::Borrowed("Hello, ");
    let borrowed2 = Cow::Borrowed("World!");
    let borrow_empty = Cow::Borrowed("");

    let owned1: Cow<'_, str> = Cow::Owned(String::from("Hi, "));
    let owned2: Cow<'_, str> = Cow::Owned(String::from("Rustaceans!"));
    let owned_empty: Cow<'_, str> = Cow::Owned(String::new());

    assert_eq!("Hello, World!", borrowed1.clone() + borrowed2.clone());
    assert_eq!("Hello, Rustaceans!", borrowed1.clone() + owned2.clone());

    assert_eq!("Hi, World!", owned1.clone() + borrowed2.clone());
    assert_eq!("Hi, Rustaceans!", owned1.clone() + owned2.clone());

    if let Cow::Owned(_) = borrowed1.clone() + borrow_empty.clone() {
        panic!("Adding empty strings to a borrow should note allocate");
    }
    if let Cow::Owned(_) = borrow_empty.clone() + borrowed1.clone() {
        panic!("Adding empty strings to a borrow should note allocate");
    }
    if let Cow::Owned(_) = borrowed1.clone() + owned_empty.clone() {
        panic!("Adding empty strings to a borrow should note allocate");
    }
    if let Cow::Owned(_) = owned_empty.clone() + borrowed1.clone() {
        panic!("Adding empty strings to a borrow should note allocate");
    }
}

#[test]
fn check_cow_add_str() {
    let borrowed = Cow::Borrowed("Hello, ");
    let borrow_empty = Cow::Borrowed("");

    let owned: Cow<'_, str> = Cow::Owned(String::from("Hi, "));
    let owned_empty: Cow<'_, str> = Cow::Owned(String::new());

    assert_eq!("Hello, World!", borrowed.clone() + "World!");

    assert_eq!("Hi, World!", owned.clone() + "World!");

    if let Cow::Owned(_) = borrowed.clone() + "" {
        panic!("Adding empty strings to a borrow should note allocate");
    }
    if let Cow::Owned(_) = borrow_empty.clone() + "Hello, " {
        panic!("Adding empty strings to a borrow should note allocate");
    }
    if let Cow::Owned(_) = owned_empty.clone() + "Hello, " {
        panic!("Adding empty strings to a borrow should note allocate");
    }
}

#[test]
fn check_cow_add_assign_cow() {
    let mut borrowed1 = Cow::Borrowed("Hello, ");
    let borrowed2 = Cow::Borrowed("World!");
    let borrow_empty = Cow::Borrowed("");

    let mut owned1: Cow<'_, str> = Cow::Owned(String::from("Hi, "));
    let owned2: Cow<'_, str> = Cow::Owned(String::from("Rustaceans!"));
    let owned_empty: Cow<'_, str> = Cow::Owned(String::new());

    let mut s = borrowed1.clone();
    s += borrow_empty.clone();
    assert_eq!("Hello, ", s);
    if let Cow::Owned(_) = s {
        panic!("Adding empty strings to a borrow should note allocate");
    }
    let mut s = borrow_empty.clone();
    s += borrowed1.clone();
    assert_eq!("Hello, ", s);
    if let Cow::Owned(_) = s {
        panic!("Adding empty strings to a borrow should note allocate");
    }
    let mut s = borrowed1.clone();
    s += owned_empty.clone();
    assert_eq!("Hello, ", s);
    if let Cow::Owned(_) = s {
        panic!("Adding empty strings to a borrow should note allocate");
    }
    let mut s = owned_empty.clone();
    s += borrowed1.clone();
    assert_eq!("Hello, ", s);
    if let Cow::Owned(_) = s {
        panic!("Adding empty strings to a borrow should note allocate");
    }

    owned1 += borrowed2;
    borrowed1 += owned2;

    assert_eq!("Hi, World!", owned1);
    assert_eq!("Hello, Rustaceans!", borrowed1);
}

#[test]
fn check_cow_add_assign_str() {
    let mut borrowed = Cow::Borrowed("Hello, ");
    let borrow_empty = Cow::Borrowed("");

    let mut owned: Cow<'_, str> = Cow::Owned(String::from("Hi, "));
    let owned_empty: Cow<'_, str> = Cow::Owned(String::new());

    let mut s = borrowed.clone();
    s += "";
    assert_eq!("Hello, ", s);
    if let Cow::Owned(_) = s {
        panic!("Adding empty strings to a borrow should note allocate");
    }
    let mut s = borrow_empty.clone();
    s += "World!";
    assert_eq!("World!", s);
    if let Cow::Owned(_) = s {
        panic!("Adding empty strings to a borrow should note allocate");
    }
    let mut s = owned_empty.clone();
    s += "World!";
    assert_eq!("World!", s);
    if let Cow::Owned(_) = s {
        panic!("Adding empty strings to a borrow should note allocate");
    }

    owned += "World!";
    borrowed += "World!";

    assert_eq!("Hi, World!", owned);
    assert_eq!("Hello, World!", borrowed);
}

#[test]
fn check_cow_clone_from() {
    let mut c1: Cow<'_, str> = Cow::Owned(String::with_capacity(25));
    let s: String = "hi".to_string();
    assert!(s.capacity() < 25);
    let c2: Cow<'_, str> = Cow::Owned(s);
    c1.clone_from(&c2);
    assert!(c1.into_owned().capacity() >= 25);
    let mut c3: Cow<'_, str> = Cow::Borrowed("bye");
    c3.clone_from(&c2);
    assert_eq!(c2, c3);
}
lithbitren commented 1 year ago

or give cow<str> a new name and let it be like a CowString, so that we can use it like:

let abc = CowString::from("abc");
let empty = "".to_cow_string();
let abc_empty = abc + empty;
assert!(abc_empty.is_borrowed());
let upper = abc_empty.to_uppercase();
assert_eq!("ABC", upper);
let upper_upper = upper.to_uppercase();
assert!(upper_upper.is_borrowed());