|
|
|
@ -60,6 +60,13 @@ impl HeapString {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline(always)]
|
|
|
|
|
pub fn as_mut_str(&mut self) -> &mut str
|
|
|
|
|
{
|
|
|
|
|
unsafe {
|
|
|
|
|
std::str::from_utf8_unchecked_mut(self.as_bytes_mut())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#[inline(always)]
|
|
|
|
|
pub fn as_str(&self) -> &str
|
|
|
|
|
{
|
|
|
|
@ -297,6 +304,27 @@ impl<const SIZE: usize> SmallString<SIZE>
|
|
|
|
|
current
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Shunt the stack-allocated to heap along with 1 or more `strs`
|
|
|
|
|
///
|
|
|
|
|
/// # Safety
|
|
|
|
|
/// * The caller must guarantee that the current invariant is `inner.stack`.
|
|
|
|
|
/// * the caller must recognise that the invariant after this function returns is `inner.heap`.
|
|
|
|
|
/// * the `fill_ptr` will be updated automacitally. The caller must guarantee that is is `> SIZE` when this function returns.
|
|
|
|
|
#[inline(always)]
|
|
|
|
|
unsafe fn shunt_to_heap_with_unchecked<'i, I>(&'i mut self, strings: I) -> &'i mut HeapString
|
|
|
|
|
where I: IntoIterator<Item = &'i str>,
|
|
|
|
|
{
|
|
|
|
|
let string = {
|
|
|
|
|
let mut string: String = self.inner.stack.as_str().into();
|
|
|
|
|
string.extend(strings);
|
|
|
|
|
string.into_boxed_str()
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let heap = &mut self.inner.heap;
|
|
|
|
|
*heap = ManuallyDrop::new(HeapString::new(string));
|
|
|
|
|
heap
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Extends the memory inside `self` to fit more `bytes`.
|
|
|
|
|
/// The data is moved to the heap first if `self.len() + bytes.len() > SIZE`.
|
|
|
|
|
///
|
|
|
|
@ -309,7 +337,7 @@ impl<const SIZE: usize> SmallString<SIZE>
|
|
|
|
|
unsafe fn extend_from_bytes_unchecked<'i>(&'i mut self, bytes: &[u8]) -> &'i mut [u8]
|
|
|
|
|
{
|
|
|
|
|
let len = self.len();
|
|
|
|
|
if bytes.len() + len > SIZE {
|
|
|
|
|
if !self.inner.is_heap() && bytes.len() + len > SIZE {
|
|
|
|
|
return self.shunt_to_heap_unchecked().extend_from_bytes_unchecked(bytes);
|
|
|
|
|
}
|
|
|
|
|
match self.inner.get_stack_mut() {
|
|
|
|
@ -325,7 +353,30 @@ impl<const SIZE: usize> SmallString<SIZE>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//TODO: extend_from_str()
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
pub fn extend_from_str<'i, 'a: 'i>(&'i mut self, s: &'a str) -> &'i mut str
|
|
|
|
|
{
|
|
|
|
|
if self.inner.is_heap() {
|
|
|
|
|
// Append to heap.
|
|
|
|
|
unsafe {
|
|
|
|
|
std::str::from_utf8_unchecked_mut((&mut *(self.inner.heap)).extend_from_bytes_unchecked(s.as_bytes()))
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Attempt to append to stack
|
|
|
|
|
let appended_stack = (unsafe { &mut self.inner.stack }).append_from_str(s);
|
|
|
|
|
if appended_stack != s.len() {
|
|
|
|
|
// Shunt to heap, along with the rest of `s`.
|
|
|
|
|
let s = &s[appended_stack..];
|
|
|
|
|
unsafe {
|
|
|
|
|
self.shunt_to_heap_with_unchecked(std::iter::once(s)).as_mut_str()
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Fits in stack, return that.
|
|
|
|
|
(unsafe {&mut self.inner.stack}).as_mut_str()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<const SIZE: usize> Borrow<str> for SmallString<SIZE>
|
|
|
|
@ -414,4 +465,15 @@ impl<const SIZE: usize> fmt::Display for SmallString<SIZE>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<const SIZE: usize, T: AsRef<str>> Extend<T> for SmallString<SIZE>
|
|
|
|
|
{
|
|
|
|
|
#[inline]
|
|
|
|
|
fn extend<U: IntoIterator<Item = T>>(&mut self, iter: U) {
|
|
|
|
|
for s in iter {
|
|
|
|
|
//XXX: There's gotta be a more efficient way for this, like we have in `shunt_with_unchecked<I>()`.
|
|
|
|
|
self.extend_from_str(s.as_ref());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//TODO: SmallString: fmt::Write, io::Write impls when extending functions have been written
|
|
|
|
|