#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[doc(hidden)]
pub use core;
use core::borrow::{
Borrow,
BorrowMut,
};
use core::cmp::Ordering;
use core::hash::{
Hash,
Hasher,
};
use core::iter::FromIterator;
use core::ops::{
Add,
AddAssign,
Bound,
Deref,
DerefMut,
RangeBounds,
};
use core::str::{
FromStr,
Utf8Error,
};
use core::{
fmt,
slice,
};
use std::borrow::Cow;
use std::ffi::OsStr;
use std::iter::FusedIterator;
mod features;
mod macros;
mod repr;
use repr::Repr;
mod traits;
pub use traits::{
CompactStringExt,
ToCompactString,
};
#[cfg(test)]
mod tests;
/// A [`CompactString`] is a compact string type that can be used almost anywhere a
/// [`String`] or [`str`] can be used.
///
/// ## Using `CompactString`
/// ```
/// use compact_str::CompactString;
/// # use std::collections::HashMap;
///
/// // CompactString auto derefs into a str so you can use all methods from `str`
/// // that take a `&self`
/// if CompactString::new("hello world!").is_ascii() {
/// println!("we're all ASCII")
/// }
///
/// // You can use a CompactString in collections like you would a String or &str
/// let mut map: HashMap<CompactString, CompactString> = HashMap::new();
///
/// // directly construct a new `CompactString`
/// map.insert(CompactString::new("nyc"), CompactString::new("empire state building"));
/// // create a `CompactString` from a `&str`
/// map.insert("sf".into(), "transamerica pyramid".into());
/// // create a `CompactString` from a `String`
/// map.insert(String::from("sea").into(), String::from("space needle").into());
///
/// fn wrapped_print<T: AsRef<str>>(text: T) {
/// println!("{}", text.as_ref());
/// }
///
/// // CompactString impls AsRef<str> and Borrow<str>, so it can be used anywhere
/// // that expects a generic string
/// if let Some(building) = map.get("nyc") {
/// wrapped_print(building);
/// }
///
/// // CompactString can also be directly compared to a String or &str
/// assert_eq!(CompactString::new("chicago"), "chicago");
/// assert_eq!(CompactString::new("houston"), String::from("houston"));
/// ```
///
/// # Converting from a `String`
/// It's important that a `CompactString` interops well with `String`, so you can easily use both in
/// your code base.
///
/// `CompactString` implements `From<String>` and operates in the following manner:
/// - Eagerly inlines the string, possibly dropping excess capacity
/// - Otherwise re-uses the same underlying buffer from `String`
///
/// ```
/// use compact_str::CompactString;
///
/// // eagerly inlining
/// let short = String::from("hello world");
/// let short_c = CompactString::from(short);
/// assert!(!short_c.is_heap_allocated());
///
/// // dropping excess capacity
/// let mut excess = String::with_capacity(256);
/// excess.push_str("abc");
///
/// let excess_c = CompactString::from(excess);
/// assert!(!excess_c.is_heap_allocated());
/// assert!(excess_c.capacity() < 256);
///
/// // re-using the same buffer
/// let long = String::from("this is a longer string that will be heap allocated");
///
/// let long_ptr = long.as_ptr();
/// let long_len = long.len();
/// let long_cap = long.capacity();
///
/// let mut long_c = CompactString::from(long);
/// assert!(long_c.is_heap_allocated());
///
/// let cpt_ptr = long_c.as_ptr();
/// let cpt_len = long_c.len();
/// let cpt_cap = long_c.capacity();
///
/// // the original String and the CompactString point to the same place in memory, buffer re-use!
/// assert_eq!(cpt_ptr, long_ptr);
/// assert_eq!(cpt_len, long_len);
/// assert_eq!(cpt_cap, long_cap);
/// ```
///
/// ### Prevent Eagerly Inlining
/// A consequence of eagerly inlining is you then need to de-allocate the existing buffer, which
/// might not always be desirable if you're converting a very large amount of `String`s. If your
/// code is very sensitive to allocations, consider the [`CompactString::from_string_buffer`] API.
#[derive(Clone)]
#[repr(transparent)]
pub struct CompactString(Repr);
impl CompactString {
/// Creates a new [`CompactString`] from any type that implements `AsRef<str>`.
/// If the string is short enough, then it will be inlined on the stack!
///
/// # Examples
///
/// ### Inlined
/// ```
/// # use compact_str::CompactString;
/// // We can inline strings up to 12 characters long on 32-bit architectures...
/// #[cfg(target_pointer_width = "32")]
/// let s = "i'm 12 chars";
/// // ...and up to 24 characters on 64-bit architectures!
/// #[cfg(target_pointer_width = "64")]
/// let s = "i am 24 characters long!";
///
/// let compact = CompactString::new(&s);
///
/// assert_eq!(compact, s);
/// // we are not allocated on the heap!
/// assert!(!compact.is_heap_allocated());
/// ```
///
/// ### Heap
/// ```
/// # use compact_str::CompactString;
/// // For longer strings though, we get allocated on the heap
/// let long = "I am a longer string that will be allocated on the heap";
/// let compact = CompactString::new(long);
///
/// assert_eq!(compact, long);
/// // we are allocated on the heap!
/// assert!(compact.is_heap_allocated());
/// ```
///
/// ### Creation
/// ```
/// use compact_str::CompactString;
///
/// // Using a `&'static str`
/// let s = "hello world!";
/// let hello = CompactString::new(&s);
///
/// // Using a `String`
/// let u = String::from("🦄🌈");
/// let unicorn = CompactString::new(u);
///
/// // Using a `Box<str>`
/// let b: Box<str> = String::from("📦📦📦").into_boxed_str();
/// let boxed = CompactString::new(&b);
/// ```
#[inline]
pub fn new<T: AsRef<str>>(text: T) -> Self {
CompactString(Repr::new(text.as_ref()))
}
/// Creates a new inline [`CompactString`] at compile time.
///
/// # Examples
/// ```
/// use compact_str::CompactString;
///
/// const DEFAULT_NAME: CompactString = CompactString::new_inline("untitled");
/// ```
///
/// Note: Trying to create a long string that can't be inlined, will fail to build.
/// ```compile_fail
/// # use compact_str::CompactString;
/// const LONG: CompactString = CompactString::new_inline("this is a long string that can't be stored on the stack");
/// ```
#[inline]
pub const fn new_inline(text: &str) -> Self {
CompactString(Repr::new_inline(text))
}
/// Creates a new empty [`CompactString`] with the capacity to fit at least `capacity` bytes.
///
/// A `CompactString` will inline strings on the stack, if they're small enough. Specifically,
/// if the string has a length less than or equal to `std::mem::size_of::<String>` bytes
/// then it will be inlined. This also means that `CompactString`s have a minimum capacity
/// of `std::mem::size_of::<String>`.
///
/// # Examples
///
/// ### "zero" Capacity
/// ```
/// # use compact_str::CompactString;
/// // Creating a CompactString with a capacity of 0 will create
/// // one with capacity of std::mem::size_of::<String>();
/// let empty = CompactString::with_capacity(0);
/// let min_size = std::mem::size_of::<String>();
///
/// assert_eq!(empty.capacity(), min_size);
/// assert_ne!(0, min_size);
/// assert!(!empty.is_heap_allocated());
/// ```
///
/// ### Max Inline Size
/// ```
/// # use compact_str::CompactString;
/// // Creating a CompactString with a capacity of std::mem::size_of::<String>()
/// // will not heap allocate.
/// let str_size = std::mem::size_of::<String>();
/// let empty = CompactString::with_capacity(str_size);
///
/// assert_eq!(empty.capacity(), str_size);
/// assert!(!empty.is_heap_allocated());
/// ```
///
/// ### Heap Allocating
/// ```
/// # use compact_str::CompactString;
/// // If you create a `CompactString` with a capacity greater than
/// // `std::mem::size_of::<String>`, it will heap allocated. For heap
/// // allocated strings we have a minimum capacity
///
/// const MIN_HEAP_CAPACITY: usize = std::mem::size_of::<usize>() * 4;
///
/// let heap_size = std::mem::size_of::<String>() + 1;
/// let empty = CompactString::with_capacity(heap_size);
///
/// assert_eq!(empty.capacity(), MIN_HEAP_CAPACITY);
/// assert!(empty.is_heap_allocated());
/// ```
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
CompactString(Repr::with_capacity(capacity))
}
/// Convert a slice of bytes into a [`CompactString`].
///
/// A [`CompactString`] is a contiguous collection of bytes (`u8`s) that is valid [`UTF-8`](https://en.wikipedia.org/wiki/UTF-8).
/// This method converts from an arbitrary contiguous collection of bytes into a
/// [`CompactString`], failing if the provided bytes are not `UTF-8`.
///
/// Note: If you want to create a [`CompactString`] from a non-contiguous collection of bytes,
/// enable the `bytes` feature of this crate, and see `CompactString::from_utf8_buf`
///
/// # Examples
/// ### Valid UTF-8
/// ```
/// # use compact_str::CompactString;
/// let bytes = vec![240, 159, 166, 128, 240, 159, 146, 175];
/// let compact = CompactString::from_utf8(bytes).expect("valid UTF-8");
///
/// assert_eq!(compact, "🦀💯");
/// ```
///
/// ### Invalid UTF-8
/// ```
/// # use compact_str::CompactString;
/// let bytes = vec![255, 255, 255];
/// let result = CompactString::from_utf8(bytes);
///
/// assert!(result.is_err());
/// ```
#[inline]
pub fn from_utf8<B: AsRef<[u8]>>(buf: B) -> Result<Self, Utf8Error> {
Repr::from_utf8(buf).map(CompactString)
}
/// Converts a vector of bytes to a [`CompactString`] without checking that the string contains
/// valid UTF-8.
///
/// See the safe version, [`CompactString::from_utf8`], for more details.
///
/// # Safety
///
/// This function is unsafe because it does not check that the bytes passed to it are valid
/// UTF-8. If this constraint is violated, it may cause memory unsafety issues with future users
/// of the [`CompactString`], as the rest of the standard library assumes that
/// [`CompactString`]s are valid UTF-8.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// // some bytes, in a vector
/// let sparkle_heart = vec![240, 159, 146, 150];
///
/// let sparkle_heart = unsafe {
/// CompactString::from_utf8_unchecked(sparkle_heart)
/// };
///
/// assert_eq!("💖", sparkle_heart);
/// ```
#[inline]
#[must_use]
pub unsafe fn from_utf8_unchecked<B: AsRef<[u8]>>(buf: B) -> Self {
CompactString(Repr::from_utf8_unchecked(buf))
}
/// Decode a [`UTF-16`](https://en.wikipedia.org/wiki/UTF-16) slice of bytes into a
/// [`CompactString`], returning an [`Err`] if the slice contains any invalid data.
///
/// # Examples
/// ### Valid UTF-16
/// ```
/// # use compact_str::CompactString;
/// let buf: &[u16] = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
/// let compact = CompactString::from_utf16(buf).unwrap();
///
/// assert_eq!(compact, "𝄞music");
/// ```
///
/// ### Invalid UTF-16
/// ```
/// # use compact_str::CompactString;
/// let buf: &[u16] = &[0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
/// let res = CompactString::from_utf16(buf);
///
/// assert!(res.is_err());
/// ```
#[inline]
pub fn from_utf16<B: AsRef<[u16]>>(buf: B) -> Result<Self, Utf16Error> {
// Note: we don't use collect::<Result<_, _>>() because that fails to pre-allocate a buffer,
// even though the size of our iterator, `buf`, is known ahead of time.
//
// rustlang issue #48994 is tracking the fix
let buf = buf.as_ref();
let mut ret = CompactString::with_capacity(buf.len());
for c in core::char::decode_utf16(buf.iter().copied()) {
if let Ok(c) = c {
ret.push(c);
} else {
return Err(Utf16Error(()));
}
}
Ok(ret)
}
/// Decode a UTF-16–encoded slice `v` into a `CompactString`, replacing invalid data with
/// the replacement character (`U+FFFD`), �.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// // 𝄞mus<invalid>ic<invalid>
/// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075,
/// 0x0073, 0xDD1E, 0x0069, 0x0063,
/// 0xD834];
///
/// assert_eq!(CompactString::from("𝄞mus\u{FFFD}ic\u{FFFD}"),
/// CompactString::from_utf16_lossy(v));
/// ```
#[inline]
pub fn from_utf16_lossy<B: AsRef<[u16]>>(buf: B) -> Self {
let buf = buf.as_ref();
let mut ret = CompactString::with_capacity(buf.len());
for c in std::char::decode_utf16(buf.iter().copied()) {
match c {
Ok(c) => ret.push(c),
Err(_) => ret.push_str("�"),
}
}
ret
}
/// Returns the length of the [`CompactString`] in `bytes`, not [`char`]s or graphemes.
///
/// When using `UTF-8` encoding (which all strings in Rust do) a single character will be 1 to 4
/// bytes long, therefore the return value of this method might not be what a human considers
/// the length of the string.
///
/// # Examples
/// ```
/// # use compact_str::CompactString;
/// let ascii = CompactString::new("hello world");
/// assert_eq!(ascii.len(), 11);
///
/// let emoji = CompactString::new("👱");
/// assert_eq!(emoji.len(), 4);
/// ```
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
/// Returns `true` if the [`CompactString`] has a length of 0, `false` otherwise
///
/// # Examples
/// ```
/// # use compact_str::CompactString;
/// let mut msg = CompactString::new("");
/// assert!(msg.is_empty());
///
/// // add some characters
/// msg.push_str("hello reader!");
/// assert!(!msg.is_empty());
/// ```
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns the capacity of the [`CompactString`], in bytes.
///
/// # Note
/// * A `CompactString` will always have a capacity of at least `std::mem::size_of::<String>()`
///
/// # Examples
/// ### Minimum Size
/// ```
/// # use compact_str::CompactString;
/// let min_size = std::mem::size_of::<String>();
/// let compact = CompactString::new("");
///
/// assert!(compact.capacity() >= min_size);
/// ```
///
/// ### Heap Allocated
/// ```
/// # use compact_str::CompactString;
/// let compact = CompactString::with_capacity(128);
/// assert_eq!(compact.capacity(), 128);
/// ```
#[inline]
pub fn capacity(&self) -> usize {
self.0.capacity()
}
/// Ensures that this [`CompactString`]'s capacity is at least `additional` bytes longer than
/// its length. The capacity may be increased by more than `additional` bytes if it chooses,
/// to prevent frequent reallocations.
///
/// # Note
/// * A `CompactString` will always have at least a capacity of `std::mem::size_of::<String>()`
/// * Reserving additional bytes may cause the `CompactString` to become heap allocated
///
/// # Panics
/// Panics if the new capacity overflows `usize`
///
/// # Examples
/// ```
/// # use compact_str::CompactString;
///
/// const WORD: usize = std::mem::size_of::<usize>();
/// let mut compact = CompactString::default();
/// assert!(compact.capacity() >= (WORD * 3) - 1);
///
/// compact.reserve(200);
/// assert!(compact.is_heap_allocated());
/// assert!(compact.capacity() >= 200);
/// ```
#[inline]
pub fn reserve(&mut self, additional: usize) {
self.0.reserve(additional)
}
/// Returns a string slice containing the entire [`CompactString`].
///
/// # Examples
/// ```
/// # use compact_str::CompactString;
/// let s = CompactString::new("hello");
///
/// assert_eq!(s.as_str(), "hello");
/// ```
#[inline]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
/// Returns a mutable string slice containing the entire [`CompactString`].
///
/// # Examples
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("hello");
/// s.as_mut_str().make_ascii_uppercase();
///
/// assert_eq!(s.as_str(), "HELLO");
/// ```
#[inline]
pub fn as_mut_str(&mut self) -> &mut str {
let len = self.len();
unsafe { std::str::from_utf8_unchecked_mut(&mut self.0.as_mut_buf()[..len]) }
}
/// Returns a byte slice of the [`CompactString`]'s contents.
///
/// # Examples
/// ```
/// # use compact_str::CompactString;
/// let s = CompactString::new("hello");
///
/// assert_eq!(&[104, 101, 108, 108, 111], s.as_bytes());
/// ```
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.0.as_slice()[..self.len()]
}
// TODO: Implement a `try_as_mut_slice(...)` that will fail if it results in cloning?
//
/// Provides a mutable reference to the underlying buffer of bytes.
///
/// # Safety
/// * All Rust strings, including `CompactString`, must be valid UTF-8. The caller must
/// guarantee
/// that any modifications made to the underlying buffer are valid UTF-8.
///
/// # Examples
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("hello");
///
/// let slice = unsafe { s.as_mut_bytes() };
/// // copy bytes into our string
/// slice[5..11].copy_from_slice(" world".as_bytes());
/// // set the len of the string
/// unsafe { s.set_len(11) };
///
/// assert_eq!(s, "hello world");
/// ```
#[inline]
pub unsafe fn as_mut_bytes(&mut self) -> &mut [u8] {
self.0.as_mut_buf()
}
/// Appends the given [`char`] to the end of this [`CompactString`].
///
/// # Examples
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("foo");
///
/// s.push('b');
/// s.push('a');
/// s.push('r');
///
/// assert_eq!("foobar", s);
/// ```
pub fn push(&mut self, ch: char) {
self.push_str(ch.encode_utf8(&mut [0; 4]));
}
/// Removes the last character from the [`CompactString`] and returns it.
/// Returns `None` if this [`CompactString`] is empty.
///
/// # Examples
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("abc");
///
/// assert_eq!(s.pop(), Some('c'));
/// assert_eq!(s.pop(), Some('b'));
/// assert_eq!(s.pop(), Some('a'));
///
/// assert_eq!(s.pop(), None);
/// ```
#[inline]
pub fn pop(&mut self) -> Option<char> {
self.0.pop()
}
/// Appends a given string slice onto the end of this [`CompactString`]
///
/// # Examples
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("abc");
///
/// s.push_str("123");
///
/// assert_eq!("abc123", s);
/// ```
#[inline]
pub fn push_str(&mut self, s: &str) {
self.0.push_str(s)
}
/// Removes a [`char`] from this [`CompactString`] at a byte position and returns it.
///
/// This is an *O*(*n*) operation, as it requires copying every element in the
/// buffer.
///
/// # Panics
///
/// Panics if `idx` is larger than or equal to the [`CompactString`]'s length,
/// or if it does not lie on a [`char`] boundary.
///
/// # Examples
///
/// ### Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// let mut c = CompactString::from("hello world");
///
/// assert_eq!(c.remove(0), 'h');
/// assert_eq!(c, "ello world");
///
/// assert_eq!(c.remove(5), 'w');
/// assert_eq!(c, "ello orld");
/// ```
///
/// ### Past total length:
///
/// ```should_panic
/// # use compact_str::CompactString;
/// let mut c = CompactString::from("hello there!");
/// c.remove(100);
/// ```
///
/// ### Not on char boundary:
///
/// ```should_panic
/// # use compact_str::CompactString;
/// let mut c = CompactString::from("🦄");
/// c.remove(1);
/// ```
#[inline]
pub fn remove(&mut self, idx: usize) -> char {
let len = self.len();
let substr = &mut self.as_mut_str()[idx..];
// get the char we want to remove
let ch = substr
.chars()
.next()
.expect("cannot remove a char from the end of a string");
let ch_len = ch.len_utf8();
// shift everything back one character
let num_bytes = substr.len() - ch_len;
let ptr = substr.as_mut_ptr();
// SAFETY: Both src and dest are valid for reads of `num_bytes` amount of bytes,
// and are properly aligned
unsafe {
core::ptr::copy(ptr.add(ch_len) as *const u8, ptr, num_bytes);
self.set_len(len - ch_len);
}
ch
}
/// Forces the length of the [`CompactString`] to `new_len`.
///
/// This is a low-level operation that maintains none of the normal invariants for
/// `CompactString`. If you want to modify the `CompactString` you should use methods like
/// `push`, `push_str` or `pop`.
///
/// # Safety
/// * `new_len` must be less than or equal to `capacity()`
/// * The elements at `old_len..new_len` must be initialized
#[inline]
pub unsafe fn set_len(&mut self, new_len: usize) {
self.0.set_len(new_len)
}
/// Returns whether or not the [`CompactString`] is heap allocated.
///
/// # Examples
/// ### Inlined
/// ```
/// # use compact_str::CompactString;
/// let hello = CompactString::new("hello world");
///
/// assert!(!hello.is_heap_allocated());
/// ```
///
/// ### Heap Allocated
/// ```
/// # use compact_str::CompactString;
/// let msg = CompactString::new("this message will self destruct in 5, 4, 3, 2, 1 💥");
///
/// assert!(msg.is_heap_allocated());
/// ```
#[inline]
pub fn is_heap_allocated(&self) -> bool {
self.0.is_heap_allocated()
}
/// Ensure that the given range is inside the set data, and that no codepoints are split.
///
/// Returns the range `start..end` as a tuple.
#[inline]
fn ensure_range(&self, range: impl RangeBounds<usize>) -> (usize, usize) {
#[cold]
#[inline(never)]
fn illegal_range() -> ! {
panic!("illegal range");
}
let start = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => match n.checked_add(1) {
Some(n) => n,
None => illegal_range(),
},
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => match n.checked_add(1) {
Some(n) => n,
None => illegal_range(),
},
Bound::Excluded(&n) => n,
Bound::Unbounded => self.len(),
};
if end < start {
illegal_range();
}
let s = self.as_str();
if !s.is_char_boundary(start) || !s.is_char_boundary(end) {
illegal_range();
}
(start, end)
}
/// Removes the specified range in the [`CompactString`],
/// and replaces it with the given string.
/// The given string doesn't need to be the same length as the range.
///
/// # Panics
///
/// Panics if the starting point or end point do not lie on a [`char`]
/// boundary, or if they're out of bounds.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("Hello, world!");
///
/// s.replace_range(7..12, "WORLD");
/// assert_eq!(s, "Hello, WORLD!");
///
/// s.replace_range(7..=11, "you");
/// assert_eq!(s, "Hello, you!");
///
/// s.replace_range(5.., "! Is it me you're looking for?");
/// assert_eq!(s, "Hello! Is it me you're looking for?");
/// ```
#[inline]
pub fn replace_range(&mut self, range: impl RangeBounds<usize>, replace_with: &str) {
let (start, end) = self.ensure_range(range);
let dest_len = end - start;
match dest_len.cmp(&replace_with.len()) {
Ordering::Equal => unsafe { self.replace_range_same_size(start, end, replace_with) },
Ordering::Greater => unsafe { self.replace_range_shrink(start, end, replace_with) },
Ordering::Less => unsafe { self.replace_range_grow(start, end, replace_with) },
}
}
/// Replace into the same size.
unsafe fn replace_range_same_size(&mut self, start: usize, end: usize, replace_with: &str) {
core::ptr::copy_nonoverlapping(
replace_with.as_ptr(),
self.as_mut_ptr().add(start),
end - start,
);
}
/// Replace, so self.len() gets smaller.
unsafe fn replace_range_shrink(&mut self, start: usize, end: usize, replace_with: &str) {
let total_len = self.len();
let dest_len = end - start;
let new_len = total_len - (dest_len - replace_with.len());
let amount = total_len - end;
let data = self.as_mut_ptr();
// first insert the replacement string, overwriting the current content
core::ptr::copy_nonoverlapping(replace_with.as_ptr(), data.add(start), replace_with.len());
// then move the tail of the CompactString forward to its new place, filling the gap
core::ptr::copy(
data.add(total_len - amount),
data.add(new_len - amount),
amount,
);
// and lastly we set the new length
self.set_len(new_len);
}
/// Replace, so self.len() gets bigger.
unsafe fn replace_range_grow(&mut self, start: usize, end: usize, replace_with: &str) {
let dest_len = end - start;
self.reserve(replace_with.len() - dest_len);
let total_len = self.len();
let new_len = total_len + (replace_with.len() - dest_len);
let amount = total_len - end;
// first grow the string, so MIRI knows that the full range is usable
self.set_len(new_len);
let data = self.as_mut_ptr();
// then move the tail of the CompactString back to its new place
core::ptr::copy(
data.add(total_len - amount),
data.add(new_len - amount),
amount,
);
// and lastly insert the replacement string
core::ptr::copy_nonoverlapping(replace_with.as_ptr(), data.add(start), replace_with.len());
}
/// Truncate the [`CompactString`] to a shorter length.
///
/// If the length of the [`CompactString`] is less or equal to `new_len`, the call is a no-op.
///
/// Calling this function does not change the capacity of the [`CompactString`].
///
/// # Panics
///
/// Panics if the new end of the string does not lie on a [`char`] boundary.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("Hello, world!");
/// s.truncate(5);
/// assert_eq!(s, "Hello");
/// ```
pub fn truncate(&mut self, new_len: usize) {
let s = self.as_str();
if new_len >= s.len() {
return;
}
assert!(
s.is_char_boundary(new_len),
"new_len must lie on char boundary",
);
unsafe { self.set_len(new_len) };
}
/// Converts a [`CompactString`] to a raw pointer.
#[inline]
pub fn as_ptr(&self) -> *const u8 {
self.0.as_slice().as_ptr()
}
/// Converts a mutable [`CompactString`] to a raw pointer.
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut u8 {
unsafe { self.0.as_mut_buf().as_mut_ptr() }
}
/// Insert string character at an index.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("Hello!");
/// s.insert_str(5, ", world");
/// assert_eq!(s, "Hello, world!");
/// ```
pub fn insert_str(&mut self, idx: usize, string: &str) {
assert!(self.is_char_boundary(idx), "idx must lie on char boundary");
let new_len = self.len() + string.len();
self.reserve(string.len());
// SAFETY: We just checked that we may split self at idx.
// We set the length only after reserving the memory.
// We fill the gap with valid UTF-8 data.
unsafe {
// first move the tail to the new back
let data = self.as_mut_ptr();
std::ptr::copy(
data.add(idx),
data.add(idx + string.len()),
new_len - idx - string.len(),
);
// then insert the new bytes
std::ptr::copy_nonoverlapping(string.as_ptr(), data.add(idx), string.len());
// and lastly resize the string
self.set_len(new_len);
}
}
/// Insert a character at an index.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("Hello world!");
/// s.insert(5, ',');
/// assert_eq!(s, "Hello, world!");
/// ```
pub fn insert(&mut self, idx: usize, ch: char) {
self.insert_str(idx, ch.encode_utf8(&mut [0; 4]));
}
/// Reduces the length of the [`CompactString`] to zero.
///
/// Calling this function does not change the capacity of the [`CompactString`].
///
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("Rust is the most loved language on Stackoverflow!");
/// assert_eq!(s.capacity(), 49);
///
/// s.clear();
///
/// assert_eq!(s, "");
/// assert_eq!(s.capacity(), 49);
/// ```
pub fn clear(&mut self) {
unsafe { self.set_len(0) };
}
/// Split the [`CompactString`] into at the given byte index.
///
/// Calling this function does not change the capacity of the [`CompactString`].
///
/// # Panics
///
/// Panics if `at` does not lie on a [`char`] boundary.
///
/// Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("Hello, world!");
/// assert_eq!(s.split_off(5), ", world!");
/// assert_eq!(s, "Hello");
/// ```
pub fn split_off(&mut self, at: usize) -> Self {
let result = self[at..].into();
// SAFETY: the previous line `self[at...]` would have panicked if `at` was invalid
unsafe { self.set_len(at) };
result
}
/// Remove a range from the [`CompactString`], and return it as an iterator.
///
/// Calling this function does not change the capacity of the [`CompactString`].
///
/// # Panics
///
/// Panics if the start or end of the range does not lie on a [`char`] boundary.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::new("Hello, world!");
///
/// let mut d = s.drain(5..12);
/// assert_eq!(d.next(), Some(',')); // iterate over the extracted data
/// assert_eq!(d.as_str(), " world"); // or get the whole data as &str
///
/// // The iterator keeps a reference to `s`, so you have to drop() the iterator,
/// // before you can access `s` again.
/// drop(d);
/// assert_eq!(s, "Hello!");
/// ```
pub fn drain(&mut self, range: impl RangeBounds<usize>) -> Drain<'_> {
let (start, end) = self.ensure_range(range);
Drain {
compact_string: self as *mut Self,
start,
end,
chars: self[start..end].chars(),
}
}
/// Shrinks the capacity of this [`CompactString`] with a lower bound.
///
/// The resulting capactity is never less than the size of 3×[`usize`],
/// i.e. the capacity than can be inlined.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::with_capacity(100);
/// assert_eq!(s.capacity(), 100);
///
/// // if the capacity was already bigger than the argument, the call is a no-op
/// s.shrink_to(100);
/// assert_eq!(s.capacity(), 100);
///
/// s.shrink_to(50);
/// assert_eq!(s.capacity(), 50);
///
/// // if the string can be inlined, it is
/// s.shrink_to(10);
/// assert_eq!(s.capacity(), 3 * std::mem::size_of::<usize>());
/// ```
#[inline]
pub fn shrink_to(&mut self, min_capacity: usize) {
self.0.shrink_to(min_capacity);
}
/// Shrinks the capacity of this [`CompactString`] to match its length.
///
/// The resulting capactity is never less than the size of 3×[`usize`],
/// i.e. the capacity than can be inlined.
///
/// This method is effectively the same as calling [`string.shrink_to(0)`].
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::from("This is a string with more than 24 characters.");
///
/// s.reserve(100);
/// assert!(s.capacity() >= 100);
///
/// s.shrink_to_fit();
/// assert_eq!(s.len(), s.capacity());
/// ```
///
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::from("short string");
///
/// s.reserve(100);
/// assert!(s.capacity() >= 100);
///
/// s.shrink_to_fit();
/// assert_eq!(s.capacity(), 3 * std::mem::size_of::<usize>());
/// ```
#[inline]
pub fn shrink_to_fit(&mut self) {
self.0.shrink_to(0);
}
/// Retains only the characters specified by the predicate.
///
/// The method iterates over the characters in the string and calls the `predicate`.
///
/// If the `predicate` returns `false`, then the character gets removed.
/// If the `predicate` returns `true`, then the character is kept.
///
/// # Examples
///
/// ```
/// # use compact_str::CompactString;
/// let mut s = CompactString::from("äb𝄞d€");
///
/// let keep = [false, true, true, false, true];
/// let mut iter = keep.iter();
/// s.retain(|_| *iter.next().unwrap());
///
/// assert_eq!(s, "b𝄞€");
/// ```
pub fn retain(&mut self, mut predicate: impl FnMut(char) -> bool) {
// We iterate over the string, and copy character by character.
let s = self.as_mut_str();
let mut dest_idx = 0;
let mut src_idx = 0;
while let Some(ch) = s[src_idx..].chars().next() {
let ch_len = ch.len_utf8();
if predicate(ch) {
// SAFETY: We know that both indices are valid, and that we don't split a char.
unsafe {
let p = s.as_mut_ptr();
core::ptr::copy(p.add(src_idx), p.add(dest_idx), ch_len);
}
dest_idx += ch_len;
}
src_idx += ch_len;
}
// SAFETY: We know that the index is a valid position to break the string.
unsafe { self.set_len(dest_idx) };
}
/// Decode a bytes slice as UTF-8 string, replacing any illegal codepoints
///
/// # Examples
///
/// ```
/// # use compact_str::CompactString;
/// let chess_knight = b"\xf0\x9f\xa8\x84";
///
/// assert_eq!(
/// "🨄",
/// CompactString::from_utf8_lossy(chess_knight),
/// );
///
/// // For valid UTF-8 slices, this is the same as:
/// assert_eq!(
/// "🨄",
/// CompactString::new(std::str::from_utf8(chess_knight).unwrap()),
/// );
/// ```
///
/// Incorrect bytes:
///
/// ```
/// # use compact_str::CompactString;
/// let broken = b"\xf0\x9f\xc8\x84";
///
/// assert_eq!(
/// "�Ȅ",
/// CompactString::from_utf8_lossy(broken),
/// );
///
/// // For invalid UTF-8 slices, this is an optimized implemented for:
/// assert_eq!(
/// "�Ȅ",
/// CompactString::from(String::from_utf8_lossy(broken)),
/// );
/// ```
pub fn from_utf8_lossy(v: &[u8]) -> Self {
fn next_char<'a>(
iter: &mut <&[u8] as IntoIterator>::IntoIter,
buf: &'a mut [u8; 4],
) -> Option<&'a [u8]> {
const REPLACEMENT: &[u8] = "\u{FFFD}".as_bytes();
macro_rules! ensure_range {
($idx:literal, $range:pat) => {{
let mut i = iter.clone();
match i.next() {
Some(&c) if matches!(c, $range) => {
buf[$idx] = c;
*iter = i;
}
_ => return Some(REPLACEMENT),
}
}};
}
macro_rules! ensure_cont {
($idx:literal) => {{
ensure_range!($idx, 0x80..=0xBF);
}};
}
let c = *iter.next()?;
buf[0] = c;
match c {
0x00..=0x7F => {
// simple ASCII: push as is
Some(&buf[..1])
}
0xC2..=0xDF => {
// two bytes
ensure_cont!(1);
Some(&buf[..2])
}
0xE0..=0xEF => {
// three bytes
match c {
// 0x80..=0x9F encodes surrogate half
0xE0 => ensure_range!(1, 0xA0..=0xBF),
// 0xA0..=0xBF encodes surrogate half
0xED => ensure_range!(1, 0x80..=0x9F),
// all UTF-8 continuation bytes are valid
_ => ensure_cont!(1),
}
ensure_cont!(2);
Some(&buf[..3])
}
0xF0..=0xF4 => {
// four bytes
match c {
// 0x80..=0x8F encodes overlong three byte codepoint
0xF0 => ensure_range!(1, 0x90..=0xBF),
// 0x90..=0xBF encodes codepoint > U+10FFFF
0xF4 => ensure_range!(1, 0x80..=0x8F),
// all UTF-8 continuation bytes are valid
_ => ensure_cont!(1),
}
ensure_cont!(2);
ensure_cont!(3);
Some(&buf[..4])
}
| 0x80..=0xBF // unicode continuation, invalid
| 0xC0..=0xC1 // overlong one byte character
| 0xF5..=0xF7 // four bytes that encode > U+10FFFF
| 0xF8..=0xFB // five bytes, invalid
| 0xFC..=0xFD // six bytes, invalid
| 0xFE..=0xFF => Some(REPLACEMENT), // always invalid
}
}
let mut buf = [0; 4];
let mut result = Self::with_capacity(v.len());
let mut iter = v.iter();
while let Some(s) = next_char(&mut iter, &mut buf) {
// SAFETY: next_char() only returns valid strings
let s = unsafe { std::str::from_utf8_unchecked(s) };
result.push_str(s);
}
result
}
fn from_utf16x(
v: &[u8],
from_int: impl Fn(u16) -> u16,
from_bytes: impl Fn([u8; 2]) -> u16,
) -> Result<Self, Utf16Error> {
if v.len() % 2 != 0 {
// Input had an odd number of bytes.
return Err(Utf16Error(()));
}
// Note: we don't use collect::<Result<_, _>>() because that fails to pre-allocate a buffer,
// even though the size of our iterator, `v`, is known ahead of time.
//
// rustlang issue #48994 is tracking the fix
let mut result = CompactString::with_capacity(v.len() / 2);
// SAFETY: `u8` and `u16` are `Copy`, so if the alignment fits, we can transmute a
// `[u8; 2*N]` to `[u16; N]`. `slice::align_to()` checks if the alignment is right.
match unsafe { v.align_to::<u16>() } {
(&[], v, &[]) => {
// Input is correcty aligned.
for c in std::char::decode_utf16(v.iter().copied().map(from_int)) {
result.push(c.map_err(|_| Utf16Error(()))?);
}
}
_ => {
// Input's alignment is off.
// SAFETY: we can always reinterpret a `[u8; 2*N]` slice as `[[u8; 2]; N]`
let v = unsafe { slice::from_raw_parts(v.as_ptr().cast(), v.len() / 2) };
for c in std::char::decode_utf16(v.iter().copied().map(from_bytes)) {
result.push(c.map_err(|_| Utf16Error(()))?);
}
}
}
Ok(result)
}
fn from_utf16x_lossy(
v: &[u8],
from_int: impl Fn(u16) -> u16,
from_bytes: impl Fn([u8; 2]) -> u16,
) -> Self {
// Notice: We write the string "�" instead of the character '�', so the character does not
// have to be formatted before it can be appended.
let (trailing_extra_byte, v) = match v.len() % 2 != 0 {
true => (true, &v[..v.len() - 1]),
false => (false, v),
};
let mut result = CompactString::with_capacity(v.len() / 2);
// SAFETY: `u8` and `u16` are `Copy`, so if the alignment fits, we can transmute a
// `[u8; 2*N]` to `[u16; N]`. `slice::align_to()` checks if the alignment is right.
match unsafe { v.align_to::<u16>() } {
(&[], v, &[]) => {
// Input is correcty aligned.
for c in std::char::decode_utf16(v.iter().copied().map(from_int)) {
match c {
Ok(c) => result.push(c),
Err(_) => result.push_str("�"),
}
}
}
_ => {
// Input's alignment is off.
// SAFETY: we can always reinterpret a `[u8; 2*N]` slice as `[[u8; 2]; N]`
let v = unsafe { slice::from_raw_parts(v.as_ptr().cast(), v.len() / 2) };
for c in std::char::decode_utf16(v.iter().copied().map(from_bytes)) {
match c {
Ok(c) => result.push(c),
Err(_) => result.push_str("�"),
}
}
}
}
if trailing_extra_byte {
result.push_str("�");
}
result
}
/// Decode a slice of bytes as UTF-16 encoded string, in little endian.
///
/// # Errors
///
/// If the slice has an odd number of bytes, or if it did not contain valid UTF-16 characters,
/// a [`Utf16Error`] is returned.
///
/// # Examples
///
/// ```
/// # use compact_str::CompactString;
/// const DANCING_MEN: &[u8] = b"\x3d\xd8\x6f\xdc\x0d\x20\x42\x26\x0f\xfe";
/// let dancing_men = CompactString::from_utf16le(DANCING_MEN).unwrap();
/// assert_eq!(dancing_men, "👯♂️");
/// ```
#[inline]
pub fn from_utf16le(v: impl AsRef<[u8]>) -> Result<Self, Utf16Error> {
CompactString::from_utf16x(v.as_ref(), u16::from_le, u16::from_le_bytes)
}
/// Decode a slice of bytes as UTF-16 encoded string, in big endian.
///
/// # Errors
///
/// If the slice has an odd number of bytes, or if it did not contain valid UTF-16 characters,
/// a [`Utf16Error`] is returned.
///
/// # Examples
///
/// ```
/// # use compact_str::CompactString;
/// const DANCING_WOMEN: &[u8] = b"\xd8\x3d\xdc\x6f\x20\x0d\x26\x40\xfe\x0f";
/// let dancing_women = CompactString::from_utf16be(DANCING_WOMEN).unwrap();
/// assert_eq!(dancing_women, "👯♀️");
/// ```
#[inline]
pub fn from_utf16be(v: impl AsRef<[u8]>) -> Result<Self, Utf16Error> {
CompactString::from_utf16x(v.as_ref(), u16::from_be, u16::from_be_bytes)
}
/// Lossy decode a slice of bytes as UTF-16 encoded string, in little endian.
///
/// In this context "lossy" means that any broken characters in the input are replaced by the
/// \<REPLACEMENT CHARACTER\> `'�'`. Please notice that, unlike UTF-8, UTF-16 is not self
/// synchronizing. I.e. if a byte in the input is dropped, all following data is broken.
///
/// # Examples
///
/// ```
/// # use compact_str::CompactString;
/// // A "random" bit was flipped in the 4th byte:
/// const DANCING_MEN: &[u8] = b"\x3d\xd8\x6f\xfc\x0d\x20\x42\x26\x0f\xfe";
/// let dancing_men = CompactString::from_utf16le_lossy(DANCING_MEN);
/// assert_eq!(dancing_men, "�\u{fc6f}\u{200d}♂️");
/// ```
#[inline]
pub fn from_utf16le_lossy(v: impl AsRef<[u8]>) -> Self {
CompactString::from_utf16x_lossy(v.as_ref(), u16::from_le, u16::from_le_bytes)
}
/// Lossy decode a slice of bytes as UTF-16 encoded string, in big endian.
///
/// In this context "lossy" means that any broken characters in the input are replaced by the
/// \<REPLACEMENT CHARACTER\> `'�'`. Please notice that, unlike UTF-8, UTF-16 is not self
/// synchronizing. I.e. if a byte in the input is dropped, all following data is broken.
///
/// # Examples
///
/// ```
/// # use compact_str::CompactString;
/// // A "random" bit was flipped in the 9th byte:
/// const DANCING_WOMEN: &[u8] = b"\xd8\x3d\xdc\x6f\x20\x0d\x26\x40\xde\x0f";
/// let dancing_women = CompactString::from_utf16be_lossy(DANCING_WOMEN);
/// assert_eq!(dancing_women, "👯\u{200d}♀�");
/// ```
#[inline]
pub fn from_utf16be_lossy(v: impl AsRef<[u8]>) -> Self {
CompactString::from_utf16x_lossy(v.as_ref(), u16::from_be, u16::from_be_bytes)
}
/// Convert the [`CompactString`] into a [`String`].
///
/// # Examples
///
/// ```
/// # use compact_str::CompactString;
/// let s = CompactString::new("Hello world");
/// let s = s.into_string();
/// assert_eq!(s, "Hello world");
/// ```
pub fn into_string(self) -> String {
self.0.into_string()
}
/// Convert a [`String`] into a [`CompactString`] _without inlining_.
///
/// Note: You probably don't need to use this method, instead you should use `From<String>`
/// which is implemented for [`CompactString`].
///
/// This method exists incase your code is very sensitive to memory allocations. Normally when
/// converting a [`String`] to a [`CompactString`] we'll inline short strings onto the stack.
/// But this results in [`Drop`]-ing the original [`String`], which causes memory it owned on
/// the heap to be deallocated. Instead when using this method, we always reuse the buffer that
/// was previously owned by the [`String`], so no trips to the allocator are needed.
///
/// # Examples
///
/// ### Short Strings
/// ```
/// use compact_str::CompactString;
///
/// let short = "hello world".to_string();
/// let c_heap = CompactString::from_string_buffer(short);
///
/// // using CompactString::from_string_buffer, we'll re-use the String's underlying buffer
/// assert!(c_heap.is_heap_allocated());
///
/// // note: when Clone-ing a short heap allocated string, we'll eagerly inline at that point
/// let c_inline = c_heap.clone();
/// assert!(!c_inline.is_heap_allocated());
///
/// assert_eq!(c_heap, c_inline);
/// ```
///
/// ### Longer Strings
/// ```
/// use compact_str::CompactString;
///
/// let x = "longer string that will be on the heap".to_string();
/// let c1 = CompactString::from(x);
///
/// let y = "longer string that will be on the heap".to_string();
/// let c2 = CompactString::from_string_buffer(y);
///
/// // for longer strings, we re-use the underlying String's buffer in both cases
/// assert!(c1.is_heap_allocated());
/// assert!(c2.is_heap_allocated());
/// ```
///
/// ### Buffer Re-use
/// ```
/// use compact_str::CompactString;
///
/// let og = "hello world".to_string();
/// let og_addr = og.as_ptr();
///
/// let mut c = CompactString::from_string_buffer(og);
/// let ex_addr = c.as_ptr();
///
/// // When converting to/from String and CompactString with from_string_buffer we always re-use
/// // the same underlying allocated memory/buffer
/// assert_eq!(og_addr, ex_addr);
///
/// let long = "this is a long string that will be on the heap".to_string();
/// let long_addr = long.as_ptr();
///
/// let mut long_c = CompactString::from(long);
/// let long_ex_addr = long_c.as_ptr();
///
/// // When converting to/from String and CompactString with From<String>, we'll also re-use the
/// // underlying buffer, if the string is long, otherwise when converting to CompactString we
/// // eagerly inline
/// assert_eq!(long_addr, long_ex_addr);
/// ```
#[inline]
pub fn from_string_buffer(s: String) -> Self {
let repr = Repr::from_string(s, false);
CompactString(repr)
}
}
impl Default for CompactString {
#[inline]
fn default() -> Self {
CompactString::new("")
}
}
impl Deref for CompactString {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.as_str()
}
}
impl DerefMut for CompactString {
#[inline]
fn deref_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
impl AsRef<str> for CompactString {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<OsStr> for CompactString {
#[inline]
fn as_ref(&self) -> &OsStr {
OsStr::new(self.as_str())
}
}
impl AsRef<[u8]> for CompactString {
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Borrow<str> for CompactString {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl BorrowMut<str> for CompactString {
#[inline]
fn borrow_mut(&mut self) -> &mut str {
self.as_mut_str()
}
}
impl Eq for CompactString {}
impl<T: AsRef<str>> PartialEq<T> for CompactString {
fn eq(&self, other: &T) -> bool {
self.as_str() == other.as_ref()
}
}
impl PartialEq<CompactString> for String {
fn eq(&self, other: &CompactString) -> bool {
self.as_str() == other.as_str()
}
}
impl PartialEq<CompactString> for &str {
fn eq(&self, other: &CompactString) -> bool {
*self == other.as_str()
}
}
impl<'a> PartialEq<CompactString> for Cow<'a, str> {
fn eq(&self, other: &CompactString) -> bool {
*self == other.as_str()
}
}
impl Ord for CompactString {
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl PartialOrd for CompactString {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Hash for CompactString {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_str().hash(state)
}
}
impl<'a> From<&'a str> for CompactString {
fn from(s: &'a str) -> Self {
let repr = Repr::new(s);
CompactString(repr)
}
}
impl From<String> for CompactString {
fn from(s: String) -> Self {
let repr = Repr::from_string(s, true);
CompactString(repr)
}
}
impl<'a> From<&'a String> for CompactString {
fn from(s: &'a String) -> Self {
CompactString::new(s)
}
}
impl<'a> From<Cow<'a, str>> for CompactString {
fn from(cow: Cow<'a, str>) -> Self {
match cow {
Cow::Borrowed(s) => s.into(),
// we separate these two so we can re-use the underlying buffer in the owned case
Cow::Owned(s) => s.into(),
}
}
}
impl From<Box<str>> for CompactString {
fn from(b: Box<str>) -> Self {
let s = b.into_string();
let repr = Repr::from_string(s, true);
CompactString(repr)
}
}
impl From<CompactString> for String {
#[inline]
fn from(s: CompactString) -> Self {
s.into_string()
}
}
impl From<CompactString> for Cow<'_, str> {
#[inline]
fn from(s: CompactString) -> Self {
Self::Owned(s.into_string())
}
}
impl<'a> From<&'a CompactString> for Cow<'a, str> {
#[inline]
fn from(s: &'a CompactString) -> Self {
Self::Borrowed(s)
}
}
impl FromStr for CompactString {
type Err = core::convert::Infallible;
fn from_str(s: &str) -> Result<CompactString, Self::Err> {
Ok(CompactString::from(s))
}
}
impl fmt::Debug for CompactString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self.as_str(), f)
}
}
impl fmt::Display for CompactString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.as_str(), f)
}
}
impl FromIterator<char> for CompactString {
fn from_iter<T: IntoIterator<Item = char>>(iter: T) -> Self {
let repr = iter.into_iter().collect();
CompactString(repr)
}
}
impl<'a> FromIterator<&'a char> for CompactString {
fn from_iter<T: IntoIterator<Item = &'a char>>(iter: T) -> Self {
let repr = iter.into_iter().collect();
CompactString(repr)
}
}
impl<'a> FromIterator<&'a str> for CompactString {
fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
let repr = iter.into_iter().collect();
CompactString(repr)
}
}
impl FromIterator<Box<str>> for CompactString {
fn from_iter<T: IntoIterator<Item = Box<str>>>(iter: T) -> Self {
let repr = iter.into_iter().collect();
CompactString(repr)
}
}
impl<'a> FromIterator<Cow<'a, str>> for CompactString {
fn from_iter<T: IntoIterator<Item = Cow<'a, str>>>(iter: T) -> Self {
let repr = iter.into_iter().collect();
CompactString(repr)
}
}
impl FromIterator<String> for CompactString {
fn from_iter<T: IntoIterator<Item = String>>(iter: T) -> Self {
let repr = iter.into_iter().collect();
CompactString(repr)
}
}
impl FromIterator<CompactString> for CompactString {
fn from_iter<T: IntoIterator<Item = CompactString>>(iter: T) -> Self {
let repr = iter.into_iter().collect();
CompactString(repr)
}
}
impl FromIterator<CompactString> for String {
fn from_iter<T: IntoIterator<Item = CompactString>>(iter: T) -> Self {
let mut iterator = iter.into_iter();
match iterator.next() {
None => String::new(),
Some(buf) => {
let mut buf = buf.into_string();
buf.extend(iterator);
buf
}
}
}
}
impl FromIterator<CompactString> for Cow<'_, str> {
fn from_iter<T: IntoIterator<Item = CompactString>>(iter: T) -> Self {
String::from_iter(iter).into()
}
}
impl Extend<char> for CompactString {
fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
self.0.extend(iter)
}
}
impl<'a> Extend<&'a char> for CompactString {
fn extend<T: IntoIterator<Item = &'a char>>(&mut self, iter: T) {
self.0.extend(iter)
}
}
impl<'a> Extend<&'a str> for CompactString {
fn extend<T: IntoIterator<Item = &'a str>>(&mut self, iter: T) {
self.0.extend(iter)
}
}
impl Extend<Box<str>> for CompactString {
fn extend<T: IntoIterator<Item = Box<str>>>(&mut self, iter: T) {
self.0.extend(iter)
}
}
impl<'a> Extend<Cow<'a, str>> for CompactString {
fn extend<T: IntoIterator<Item = Cow<'a, str>>>(&mut self, iter: T) {
iter.into_iter().for_each(move |s| self.push_str(&s));
}
}
impl Extend<String> for CompactString {
fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
self.0.extend(iter)
}
}
impl Extend<CompactString> for String {
fn extend<T: IntoIterator<Item = CompactString>>(&mut self, iter: T) {
for s in iter {
self.push_str(&s);
}
}
}
impl Extend<CompactString> for CompactString {
fn extend<T: IntoIterator<Item = CompactString>>(&mut self, iter: T) {
for s in iter {
self.push_str(&s);
}
}
}
impl<'a> Extend<CompactString> for Cow<'a, str> {
fn extend<T: IntoIterator<Item = CompactString>>(&mut self, iter: T) {
self.to_mut().extend(iter);
}
}
impl fmt::Write for CompactString {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.push_str(s);
Ok(())
}
fn write_fmt(mut self: &mut Self, args: fmt::Arguments<'_>) -> fmt::Result {
match args.as_str() {
Some(s) => {
self.push_str(s);
Ok(())
}
None => fmt::write(&mut self, args),
}
}
}
impl Add<&str> for CompactString {
type Output = Self;
fn add(mut self, rhs: &str) -> Self::Output {
self.push_str(rhs);
self
}
}
impl AddAssign<&str> for CompactString {
fn add_assign(&mut self, rhs: &str) {
self.push_str(rhs);
}
}
/// A possible error value when converting a [`CompactString`] from a UTF-16 byte slice.
///
/// This type is the error type for the [`from_utf16`] method on [`CompactString`].
///
/// [`from_utf16`]: CompactString::from_utf16
/// # Examples
///
/// Basic usage:
///
/// ```
/// # use compact_str::CompactString;
/// // 𝄞mu<invalid>ic
/// let v = &[0xD834, 0xDD1E, 0x006d, 0x0075,
/// 0xD800, 0x0069, 0x0063];
///
/// assert!(CompactString::from_utf16(v).is_err());
/// ```
#[derive(Copy, Clone, Debug)]
pub struct Utf16Error(());
impl fmt::Display for Utf16Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt("invalid utf-16: lone surrogate found", f)
}
}
/// An iterator over the exacted data by [`CompactString::drain()`].
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Drain<'a> {
compact_string: *mut CompactString,
start: usize,
end: usize,
chars: std::str::Chars<'a>,
}
// SAFETY: Drain keeps the lifetime of the CompactString it belongs to.
unsafe impl Send for Drain<'_> {}
unsafe impl Sync for Drain<'_> {}
impl fmt::Debug for Drain<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Drain").field(&self.as_str()).finish()
}
}
impl fmt::Display for Drain<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl Drop for Drain<'_> {
#[inline]
fn drop(&mut self) {
// SAFETY: Drain keeps a mutable reference to compact_string, so one one else can access
// the CompactString, but this function right now. CompactString::drain() ensured
// that the new extracted range does not split a UTF-8 character.
unsafe { (*self.compact_string).replace_range_shrink(self.start, self.end, "") };
}
}
impl Drain<'_> {
/// The remaining, unconsumed characters of the extracted substring.
#[inline]
pub fn as_str(&self) -> &str {
self.chars.as_str()
}
}
impl Deref for Drain<'_> {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl Iterator for Drain<'_> {
type Item = char;
#[inline]
fn next(&mut self) -> Option<char> {
self.chars.next()
}
#[inline]
fn count(self) -> usize {
// <Chars as Iterator>::count() is specialized, and cloning is trivial.
self.chars.clone().count()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.chars.size_hint()
}
#[inline]
fn last(mut self) -> Option<char> {
self.chars.next_back()
}
}
impl DoubleEndedIterator for Drain<'_> {
#[inline]
fn next_back(&mut self) -> Option<char> {
self.chars.next_back()
}
}
impl FusedIterator for Drain<'_> {}
static_assertions::assert_eq_size!(CompactString, String);