From b215116020bdf2d8f7f0f96125d963b07284e995 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Mon, 9 Oct 2023 14:28:02 +0200 Subject: [PATCH] Add a few methods to `EcoString` Now replicates all methods on `str` that return `String`. --- src/dynamic.rs | 15 ++++++++++++ src/string.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++--- src/vec.rs | 2 +- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/src/dynamic.rs b/src/dynamic.rs index af7ec8f..70077ac 100644 --- a/src/dynamic.rs +++ b/src/dynamic.rs @@ -119,6 +119,14 @@ impl DynamicVec { } } + #[inline] + pub fn make_mut(&mut self) -> &mut [u8] { + match self.variant_mut() { + VariantMut::Inline(inline) => inline.as_mut_slice(), + VariantMut::Spilled(spilled) => spilled.make_mut(), + } + } + #[inline] pub fn push(&mut self, byte: u8) { match self.variant_mut() { @@ -294,6 +302,13 @@ impl InlineVec { unsafe { self.buf.get_unchecked(..self.len()) } } + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + // Safety: We have the invariant `len <= LIMIT`. + let len = self.len(); + unsafe { self.buf.get_unchecked_mut(..len) } + } + #[inline] pub fn clear(&mut self) { unsafe { diff --git a/src/string.rs b/src/string.rs index 04fcd3f..08b811b 100644 --- a/src/string.rs +++ b/src/string.rs @@ -123,6 +123,19 @@ impl EcoString { unsafe { core::str::from_utf8_unchecked(self.0.as_slice()) } } + /// Produce a mutable slice containing the entire string. + /// + /// Clones the string if its reference count is larger than 1. + #[inline] + pub fn make_mut(&mut self) -> &mut str { + // Safety: + // The buffer contents stem from correct UTF-8 sources: + // - Valid ASCII characters + // - Other string slices + // - Chars that were encoded with char::encode_utf8 + unsafe { core::str::from_utf8_unchecked_mut(self.0.make_mut()) } + } + /// Append the given character at the end. #[inline] pub fn push(&mut self, c: char) { @@ -153,7 +166,36 @@ impl EcoString { self.0.clear(); } - /// Convert the string to lowercase. + /// Replaces all matches of a string with another string. + /// + /// This is a bit less general that [`str::replace`] because the `Pattern` + /// trait is unstable. In return, it can produce an `EcoString` without + /// any intermediate [`String`] allocation. + pub fn replace(&self, pat: &str, to: &str) -> Self { + self.replacen(pat, to, usize::MAX) + } + + /// Replaces the first N matches of a string with another string. + /// + /// This is a bit less general that [`str::replacen`] because the `Pattern` + /// trait is unstable. In return, it can produce an `EcoString` without + /// any intermediate [`String`] allocation. + pub fn replacen(&self, pat: &str, to: &str, count: usize) -> Self { + // Copied from the standard library: https://github.com/rust-lang/rust + let mut result = Self::new(); + let mut last_end = 0; + for (start, part) in self.match_indices(pat).take(count) { + // Safety: Copied from std. + result.push_str(unsafe { self.get_unchecked(last_end..start) }); + result.push_str(to); + last_end = start + part.len(); + } + // Safety: Copied from std. + result.push_str(unsafe { self.get_unchecked(last_end..self.len()) }); + result + } + + /// Returns the lowercase equivalent of this string. pub fn to_lowercase(&self) -> Self { let str = self.as_str(); let mut lower = Self::with_capacity(str.len()); @@ -169,7 +211,7 @@ impl EcoString { lower } - /// Convert the string to uppercase. + /// Returns the uppercase equivalent of this string. pub fn to_uppercase(&self) -> Self { let str = self.as_str(); let mut upper = Self::with_capacity(str.len()); @@ -181,6 +223,22 @@ impl EcoString { upper } + /// Returns a copy of this string where each character is mapped to its + /// ASCII uppercase equivalent. + pub fn to_ascii_lowercase(&self) -> Self { + let mut s = self.clone(); + s.make_mut().make_ascii_lowercase(); + s + } + + /// Returns a copy of this string where each character is mapped to its + /// ASCII uppercase equivalent. + pub fn to_ascii_uppercase(&self) -> Self { + let mut s = self.clone(); + s.make_mut().make_ascii_uppercase(); + s + } + /// Repeat this string `n` times. pub fn repeat(&self, n: usize) -> Self { let slice = self.as_bytes(); @@ -284,7 +342,7 @@ impl Ord for EcoString { impl PartialOrd for EcoString { #[inline] fn partial_cmp(&self, other: &Self) -> Option { - self.as_str().partial_cmp(other.as_str()) + Some(self.cmp(other)) } } diff --git a/src/vec.rs b/src/vec.rs index 9d12c6d..d16546b 100644 --- a/src/vec.rs +++ b/src/vec.rs @@ -1210,7 +1210,7 @@ mod serde { where S: serde::Serializer, { - (&self[..]).serialize(serializer) + self.as_slice().serialize(serializer) } }