use core::{
marker::PhantomData,
ops::{Bound, Deref, DerefMut, RangeBounds},
};
pub(crate) trait GenericString: Deref<Target = str> + DerefMut<Target = str> {
fn set_size(&mut self, size: usize);
fn as_mut_capacity_slice(&mut self) -> &mut [u8];
}
macro_rules! string_op_grow {
($action:ty, $target:ident, $($arg:expr),*) => {
match $target.cast_mut() {
StringCastMut::Boxed(this) => {
this.ensure_capacity(<$action>::cap(this, $($arg),*));
<$action>::op(this, $($arg),*)
}
StringCastMut::Inline(this) => {
let new_size = <$action>::cap(this,$($arg),*);
if new_size > MAX_INLINE {
let mut new_str = BoxedString::from_str(new_size, this);
let result = <$action>::op(&mut new_str, $($arg),*);
$target.promote_from(new_str);
result
} else {
<$action>::op(this, $($arg),*)
}
}
}
};
}
pub(crate) use string_op_grow;
macro_rules! string_op_shrink {
($action:ty, $target:ident, $($arg:expr),*) => {{
let result = match $target.cast_mut() {
StringCastMut::Boxed(this) => {
<$action>::op(this, $($arg),*)
}
StringCastMut::Inline(this) => {
<$action>::op(this, $($arg),*)
}
};
$target.try_demote();
result
}};
($action:ty, $target:ident) => {
string_op_shrink!($action, $target,)
}
}
pub(crate) use string_op_shrink;
use crate::{SmartString, SmartStringMode};
pub(crate) fn bounds_for<R>(range: &R, max_len: usize) -> (usize, usize)
where
R: RangeBounds<usize>,
{
let start = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n.checked_add(1).unwrap(),
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n.checked_add(1).unwrap(),
Bound::Excluded(&n) => n,
Bound::Unbounded => max_len,
};
(start, end)
}
fn insert_bytes<S: GenericString>(this: &mut S, index: usize, src: &[u8]) {
let len = this.len();
let src_len = src.len();
let tail_index = index + src_len;
if src_len > 0 {
let buf = this.as_mut_capacity_slice();
buf.copy_within(index..len, tail_index);
buf[index..tail_index].copy_from_slice(src);
this.set_size(len + src_len);
}
}
pub(crate) struct PushStr;
impl PushStr {
pub(crate) fn cap<S: GenericString>(this: &S, string: &str) -> usize {
this.len() + string.len()
}
pub(crate) fn op<S: GenericString>(this: &mut S, string: &str) {
let len = this.len();
let new_len = len + string.len();
this.as_mut_capacity_slice()[len..new_len].copy_from_slice(string.as_bytes());
this.set_size(new_len);
}
}
pub(crate) struct Push;
impl Push {
pub(crate) fn cap<S: GenericString>(this: &S, ch: char) -> usize {
this.len() + ch.len_utf8()
}
pub(crate) fn op<S: GenericString>(this: &mut S, ch: char) {
let len = this.len();
let written = ch
.encode_utf8(&mut this.as_mut_capacity_slice()[len..])
.len();
this.set_size(len + written);
}
}
pub(crate) struct Truncate;
impl Truncate {
pub(crate) fn op<S: GenericString>(this: &mut S, new_len: usize) {
if new_len < this.len() {
assert!(this.deref().is_char_boundary(new_len));
this.set_size(new_len)
}
}
}
pub(crate) struct Pop;
impl Pop {
pub(crate) fn op<S: GenericString>(this: &mut S) -> Option<char> {
let ch = this.deref().chars().rev().next()?;
this.set_size(this.len() - ch.len_utf8());
Some(ch)
}
}
pub(crate) struct Remove;
impl Remove {
pub(crate) fn op<S: GenericString>(this: &mut S, index: usize) -> char {
let ch = match this.deref()[index..].chars().next() {
Some(ch) => ch,
None => panic!("cannot remove a char from the end of a string"),
};
let next = index + ch.len_utf8();
let len = this.len();
let tail_len = len - next;
if tail_len > 0 {
this.as_mut_capacity_slice().copy_within(next..len, index);
}
this.set_size(len - (next - index));
ch
}
}
pub(crate) struct Insert;
impl Insert {
pub(crate) fn cap<S: GenericString>(this: &S, index: usize, ch: char) -> usize {
assert!(this.deref().is_char_boundary(index));
this.len() + ch.len_utf8()
}
pub(crate) fn op<S: GenericString>(this: &mut S, index: usize, ch: char) {
let mut buffer = [0; 4];
let buffer = ch.encode_utf8(&mut buffer).as_bytes();
insert_bytes(this, index, buffer);
}
}
pub(crate) struct InsertStr;
impl InsertStr {
pub(crate) fn cap<S: GenericString>(this: &S, index: usize, string: &str) -> usize {
assert!(this.deref().is_char_boundary(index));
this.len() + string.len()
}
pub(crate) fn op<S: GenericString>(this: &mut S, index: usize, string: &str) {
insert_bytes(this, index, string.as_bytes());
}
}
pub(crate) struct SplitOff<Mode: SmartStringMode>(PhantomData<Mode>);
impl<Mode: SmartStringMode> SplitOff<Mode> {
pub(crate) fn op<S: GenericString>(this: &mut S, index: usize) -> SmartString<Mode> {
assert!(this.deref().is_char_boundary(index));
let result = this.deref()[index..].into();
this.set_size(index);
result
}
}
pub(crate) struct Retain;
impl Retain {
pub(crate) fn op<F, S>(this: &mut S, mut f: F)
where
F: FnMut(char) -> bool,
S: GenericString,
{
let len = this.len();
let mut del_bytes = 0;
let mut index = 0;
while index < len {
let ch = this
.deref_mut()
.get(index..len)
.unwrap()
.chars()
.next()
.unwrap();
let ch_len = ch.len_utf8();
if !f(ch) {
del_bytes += ch_len;
} else if del_bytes > 0 {
this.as_mut_capacity_slice()
.copy_within(index..index + ch_len, index - del_bytes);
}
index += ch_len;
}
if del_bytes > 0 {
this.set_size(len - del_bytes);
}
}
}
pub(crate) struct ReplaceRange;
impl ReplaceRange {
pub(crate) fn cap<R, S>(this: &S, range: &R, replace_with: &str) -> usize
where
R: RangeBounds<usize>,
S: GenericString,
{
let len = this.len();
let (start, end) = bounds_for(range, len);
assert!(end >= start);
assert!(end <= len);
assert!(this.deref().is_char_boundary(start));
assert!(this.deref().is_char_boundary(end));
let replace_len = replace_with.len();
let end_size = len - end;
start + replace_len + end_size
}
pub(crate) fn op<R, S>(this: &mut S, range: &R, replace_with: &str)
where
R: RangeBounds<usize>,
S: GenericString,
{
let len = this.len();
let (start, end) = bounds_for(range, len);
let replace_len = replace_with.len();
let new_end = start + replace_len;
let end_size = len - end;
this.as_mut_capacity_slice().copy_within(end..len, new_end);
if replace_len > 0 {
this.as_mut_capacity_slice()[start..new_end].copy_from_slice(replace_with.as_bytes());
}
this.set_size(start + replace_len + end_size);
}
}