smartstring/
ops.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5//! Generic string ops.
6//!
7//! `string_op_grow` is for ops which may grow but not shrink the target
8//! string, and should have a `cap` method which will return the new
9//! minimum required capacity.
10//!
11//! `string_op_shrink` is for ops which may shrinl but not grow the target
12//! string. They don't need a `cap` method, and will try to demote the
13//! string as appropriate after calling `op`.
14
15use core::{
16    marker::PhantomData,
17    ops::{Bound, Deref, DerefMut, RangeBounds},
18};
19
20pub(crate) trait GenericString: Deref<Target = str> + DerefMut<Target = str> {
21    fn set_size(&mut self, size: usize);
22    fn as_mut_capacity_slice(&mut self) -> &mut [u8];
23}
24
25macro_rules! string_op_grow {
26    ($action:ty, $target:ident, $($arg:expr),*) => {
27        match $target.cast_mut() {
28            StringCastMut::Boxed(this) => {
29                this.ensure_capacity(<$action>::cap(this, $($arg),*));
30                <$action>::op(this, $($arg),*)
31            }
32            StringCastMut::Inline(this) => {
33                let new_size = <$action>::cap(this,$($arg),*);
34                if new_size > MAX_INLINE {
35                    let mut new_str = BoxedString::from_str(new_size, this);
36                    let result = <$action>::op(&mut new_str, $($arg),*);
37                    $target.promote_from(new_str);
38                    result
39                } else {
40                    <$action>::op(this, $($arg),*)
41                }
42            }
43        }
44    };
45}
46pub(crate) use string_op_grow;
47
48macro_rules! string_op_shrink {
49    ($action:ty, $target:ident, $($arg:expr),*) => {{
50        let result = match $target.cast_mut() {
51            StringCastMut::Boxed(this) => {
52                <$action>::op(this, $($arg),*)
53            }
54            StringCastMut::Inline(this) => {
55                <$action>::op(this, $($arg),*)
56            }
57        };
58        $target.try_demote();
59        result
60    }};
61
62    ($action:ty, $target:ident) => {
63        string_op_shrink!($action, $target,)
64    }
65}
66pub(crate) use string_op_shrink;
67
68use crate::{SmartString, SmartStringMode};
69
70pub(crate) fn bounds_for<R>(range: &R, max_len: usize) -> (usize, usize)
71where
72    R: RangeBounds<usize>,
73{
74    let start = match range.start_bound() {
75        Bound::Included(&n) => n,
76        Bound::Excluded(&n) => n.checked_add(1).unwrap(),
77        Bound::Unbounded => 0,
78    };
79    let end = match range.end_bound() {
80        Bound::Included(&n) => n.checked_add(1).unwrap(),
81        Bound::Excluded(&n) => n,
82        Bound::Unbounded => max_len,
83    };
84    (start, end)
85}
86
87fn insert_bytes<S: GenericString>(this: &mut S, index: usize, src: &[u8]) {
88    let len = this.len();
89    let src_len = src.len();
90    let tail_index = index + src_len;
91    if src_len > 0 {
92        let buf = this.as_mut_capacity_slice();
93        buf.copy_within(index..len, tail_index);
94        buf[index..tail_index].copy_from_slice(src);
95        this.set_size(len + src_len);
96    }
97}
98
99pub(crate) struct PushStr;
100impl PushStr {
101    pub(crate) fn cap<S: GenericString>(this: &S, string: &str) -> usize {
102        this.len() + string.len()
103    }
104
105    pub(crate) fn op<S: GenericString>(this: &mut S, string: &str) {
106        let len = this.len();
107        let new_len = len + string.len();
108        this.as_mut_capacity_slice()[len..new_len].copy_from_slice(string.as_bytes());
109        this.set_size(new_len);
110    }
111}
112
113pub(crate) struct Push;
114impl Push {
115    pub(crate) fn cap<S: GenericString>(this: &S, ch: char) -> usize {
116        this.len() + ch.len_utf8()
117    }
118
119    pub(crate) fn op<S: GenericString>(this: &mut S, ch: char) {
120        let len = this.len();
121        let written = ch
122            .encode_utf8(&mut this.as_mut_capacity_slice()[len..])
123            .len();
124        this.set_size(len + written);
125    }
126}
127
128pub(crate) struct Truncate;
129impl Truncate {
130    pub(crate) fn op<S: GenericString>(this: &mut S, new_len: usize) {
131        if new_len < this.len() {
132            assert!(this.deref().is_char_boundary(new_len));
133            this.set_size(new_len)
134        }
135    }
136}
137
138pub(crate) struct Pop;
139impl Pop {
140    pub(crate) fn op<S: GenericString>(this: &mut S) -> Option<char> {
141        let ch = this.deref().chars().rev().next()?;
142        this.set_size(this.len() - ch.len_utf8());
143        Some(ch)
144    }
145}
146
147pub(crate) struct Remove;
148impl Remove {
149    pub(crate) fn op<S: GenericString>(this: &mut S, index: usize) -> char {
150        let ch = match this.deref()[index..].chars().next() {
151            Some(ch) => ch,
152            None => panic!("cannot remove a char from the end of a string"),
153        };
154        let next = index + ch.len_utf8();
155        let len = this.len();
156        let tail_len = len - next;
157        if tail_len > 0 {
158            this.as_mut_capacity_slice().copy_within(next..len, index);
159        }
160        this.set_size(len - (next - index));
161        ch
162    }
163}
164
165pub(crate) struct Insert;
166impl Insert {
167    pub(crate) fn cap<S: GenericString>(this: &S, index: usize, ch: char) -> usize {
168        assert!(this.deref().is_char_boundary(index));
169        this.len() + ch.len_utf8()
170    }
171
172    pub(crate) fn op<S: GenericString>(this: &mut S, index: usize, ch: char) {
173        let mut buffer = [0; 4];
174        let buffer = ch.encode_utf8(&mut buffer).as_bytes();
175        insert_bytes(this, index, buffer);
176    }
177}
178
179pub(crate) struct InsertStr;
180impl InsertStr {
181    pub(crate) fn cap<S: GenericString>(this: &S, index: usize, string: &str) -> usize {
182        assert!(this.deref().is_char_boundary(index));
183        this.len() + string.len()
184    }
185
186    pub(crate) fn op<S: GenericString>(this: &mut S, index: usize, string: &str) {
187        insert_bytes(this, index, string.as_bytes());
188    }
189}
190
191pub(crate) struct SplitOff<Mode: SmartStringMode>(PhantomData<Mode>);
192impl<Mode: SmartStringMode> SplitOff<Mode> {
193    pub(crate) fn op<S: GenericString>(this: &mut S, index: usize) -> SmartString<Mode> {
194        assert!(this.deref().is_char_boundary(index));
195        let result = this.deref()[index..].into();
196        this.set_size(index);
197        result
198    }
199}
200
201pub(crate) struct Retain;
202impl Retain {
203    pub(crate) fn op<F, S>(this: &mut S, mut f: F)
204    where
205        F: FnMut(char) -> bool,
206        S: GenericString,
207    {
208        let len = this.len();
209        let mut del_bytes = 0;
210        let mut index = 0;
211
212        while index < len {
213            let ch = this
214                .deref_mut()
215                .get(index..len)
216                .unwrap()
217                .chars()
218                .next()
219                .unwrap();
220            let ch_len = ch.len_utf8();
221
222            if !f(ch) {
223                del_bytes += ch_len;
224            } else if del_bytes > 0 {
225                this.as_mut_capacity_slice()
226                    .copy_within(index..index + ch_len, index - del_bytes);
227            }
228            index += ch_len;
229        }
230
231        if del_bytes > 0 {
232            this.set_size(len - del_bytes);
233        }
234    }
235}
236
237pub(crate) struct ReplaceRange;
238impl ReplaceRange {
239    pub(crate) fn cap<R, S>(this: &S, range: &R, replace_with: &str) -> usize
240    where
241        R: RangeBounds<usize>,
242        S: GenericString,
243    {
244        let len = this.len();
245        let (start, end) = bounds_for(range, len);
246        assert!(end >= start);
247        assert!(end <= len);
248        assert!(this.deref().is_char_boundary(start));
249        assert!(this.deref().is_char_boundary(end));
250        let replace_len = replace_with.len();
251        let end_size = len - end;
252        start + replace_len + end_size
253    }
254
255    pub(crate) fn op<R, S>(this: &mut S, range: &R, replace_with: &str)
256    where
257        R: RangeBounds<usize>,
258        S: GenericString,
259    {
260        let len = this.len();
261        let (start, end) = bounds_for(range, len);
262        let replace_len = replace_with.len();
263        let new_end = start + replace_len;
264        let end_size = len - end;
265        this.as_mut_capacity_slice().copy_within(end..len, new_end);
266        if replace_len > 0 {
267            this.as_mut_capacity_slice()[start..new_end].copy_from_slice(replace_with.as_bytes());
268        }
269        this.set_size(start + replace_len + end_size);
270    }
271}