Skip to main content

arraystring/
arraystring.rs

1//! `ArrayString` definition and Api implementation
2
3use crate::utils::{encode_char_utf8_unchecked, is_char_boundary, is_inside_boundary, never};
4use crate::utils::{shift_left_unchecked, shift_right_unchecked, truncate_str, IntoLossy};
5use crate::{error::Error, generic::ArraySlice, prelude::*};
6use core::char::{decode_utf16, REPLACEMENT_CHARACTER};
7use core::str::{from_utf8, from_utf8_unchecked};
8use core::{cmp::min, ops::*, ptr::copy_nonoverlapping};
9#[cfg(feature = "logs")]
10use log::{debug, trace};
11
12use crate::generic::Capacity;
13
14/// String based on a generic array (size defined at compile time through `typenum`)
15///
16/// Can't outgrow capacity (defined at compile time), always occupies [`capacity`] `+ 1` bytes of memory
17///
18/// *Doesn't allocate memory on the heap and never panics (all panic branches are stripped at compile time)*
19///
20/// [`capacity`]: ./struct.ArrayString.html#method.capacity
21#[derive(Clone)]
22pub struct ArrayString<SIZE: Capacity> {
23    /// Array type corresponding to specified `SIZE`
24    pub(crate) array: SIZE::Array,
25    /// Current string size
26    pub(crate) size: u8,
27}
28
29impl<SIZE: Capacity> ArrayString<SIZE> {
30    /// Creates new empty string.
31    ///
32    /// ```rust
33    /// # use arraystring::prelude::*;
34    /// # let _ = env_logger::try_init();
35    /// let string = SmallString::new();
36    /// assert!(string.is_empty());
37    /// ```
38    #[inline]
39    pub fn new() -> Self {
40        trace!("New empty ArrayString");
41        Self::default()
42    }
43
44    /// Creates new `ArrayString` from string slice if length is lower or equal to [`capacity`], otherwise returns an error.
45    ///
46    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
47    /// ```rust
48    /// # use arraystring::{error::Error, prelude::*};
49    /// # fn main() -> Result<(), Error> {
50    /// # let _ = env_logger::try_init();
51    /// let string = SmallString::try_from_str("My String")?;
52    /// assert_eq!(string.as_str(), "My String");
53    ///
54    /// assert_eq!(SmallString::try_from_str("")?.as_str(), "");
55    ///
56    /// let out_of_bounds = "0".repeat(SmallString::capacity() as usize + 1);
57    /// assert!(SmallString::try_from_str(out_of_bounds).is_err());
58    /// # Ok(())
59    /// # }
60    /// ```
61    #[inline]
62    pub fn try_from_str<S>(s: S) -> Result<Self, OutOfBounds>
63    where
64        S: AsRef<str>,
65    {
66        trace!("Try from str: {:?}", s.as_ref());
67        is_inside_boundary(s.as_ref().len(), Self::capacity())?;
68        unsafe { Ok(Self::from_str_unchecked(s.as_ref())) }
69    }
70
71    /// Creates new `ArrayString` from string slice truncating size if bigger than [`capacity`].
72    ///
73    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
74    ///
75    /// ```rust
76    /// # use arraystring::prelude::*;
77    /// # let _ = env_logger::try_init();
78    /// let string = SmallString::from_str_truncate("My String");
79    /// # assert_eq!(string.as_str(), "My String");
80    /// println!("{}", string);
81    ///
82    /// let truncate = "0".repeat(SmallString::capacity() as usize + 1);
83    /// let truncated = "0".repeat(SmallString::capacity().into());
84    /// let string = SmallString::from_str_truncate(&truncate);
85    /// assert_eq!(string.as_str(), truncated);
86    /// ```
87    #[inline]
88    pub fn from_str_truncate<S>(string: S) -> Self
89    where
90        S: AsRef<str>,
91    {
92        trace!("FromStr truncate");
93        unsafe { Self::from_str_unchecked(truncate_str(string.as_ref(), Self::capacity())) }
94    }
95
96    /// Creates new `ArrayString` from string slice assuming length is appropriate.
97    ///
98    /// # Safety
99    ///
100    /// It's UB if `string.len()` > [`capacity`].
101    ///
102    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
103    ///
104    /// ```rust
105    /// # use arraystring::prelude::*;
106    /// let filled = "0".repeat(SmallString::capacity().into());
107    /// let string = unsafe {
108    ///     SmallString::from_str_unchecked(&filled)
109    /// };
110    /// assert_eq!(string.as_str(), filled.as_str());
111    ///
112    /// // Undefined behavior, don't do it
113    /// // let out_of_bounds = "0".repeat(SmallString::capacity().into() + 1);
114    /// // let ub = unsafe { SmallString::from_str_unchecked(out_of_bounds) };
115    /// ```
116    #[inline]
117    pub unsafe fn from_str_unchecked<S>(string: S) -> Self
118    where
119        S: AsRef<str>,
120    {
121        trace!("FromStr unchecked");
122        let mut out = Self::default();
123        out.push_str_unchecked(string);
124        out
125    }
126
127    /// Creates new `ArrayString` from string slice iterator if total length is lower or equal to [`capacity`], otherwise returns an error.
128    ///
129    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
130    ///
131    /// ```rust
132    /// # use arraystring::prelude::*;
133    /// # fn main() -> Result<(), OutOfBounds> {
134    /// let string = MaxString::try_from_iterator(&["My String", " My Other String"][..])?;
135    /// assert_eq!(string.as_str(), "My String My Other String");
136    ///
137    /// let out_of_bounds = (0..100).map(|_| "000");
138    /// assert!(SmallString::try_from_iterator(out_of_bounds).is_err());
139    /// # Ok(())
140    /// # }
141    /// ```
142    #[inline]
143    pub fn try_from_iterator<U, I>(iter: I) -> Result<Self, OutOfBounds>
144    where
145        U: AsRef<str>,
146        I: IntoIterator<Item = U>,
147    {
148        trace!("FromIterator");
149        let mut out = Self::default();
150        for s in iter {
151            out.try_push_str(s)?;
152        }
153        Ok(out)
154    }
155
156    /// Creates new `ArrayString` from string slice iterator truncating size if bigger than [`capacity`].
157    ///
158    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
159    ///
160    /// ```rust
161    /// # use arraystring::prelude::*;
162    /// # fn main() -> Result<(), OutOfBounds> {
163    /// # let _ = env_logger::try_init();
164    /// let string = MaxString::from_iterator(&["My String", " Other String"][..]);
165    /// assert_eq!(string.as_str(), "My String Other String");
166    ///
167    /// let out_of_bounds = (0..400).map(|_| "000");
168    /// let truncated = "0".repeat(SmallString::capacity().into());
169    ///
170    /// let truncate = SmallString::from_iterator(out_of_bounds);
171    /// assert_eq!(truncate.as_str(), truncated.as_str());
172    /// # Ok(())
173    /// # }
174    /// ```
175    #[inline]
176    pub fn from_iterator<U, I>(iter: I) -> Self
177    where
178        U: AsRef<str>,
179        I: IntoIterator<Item = U>,
180    {
181        trace!("FromIterator truncate");
182        let mut out = Self::default();
183        for s in iter {
184            if out.try_push_str(s.as_ref()).is_err() {
185                out.push_str(s);
186                break;
187            }
188        }
189        out
190    }
191
192    /// Creates new `ArrayString` from string slice iterator assuming length is appropriate.
193    ///
194    /// # Safety
195    ///
196    /// It's UB if `iter.map(|c| c.len()).sum()` > [`capacity`].
197    ///
198    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
199    ///
200    /// ```rust
201    /// # use arraystring::prelude::*;
202    /// let string = unsafe {
203    ///     MaxString::from_iterator_unchecked(&["My String", " My Other String"][..])
204    /// };
205    /// assert_eq!(string.as_str(), "My String My Other String");
206    ///
207    /// // Undefined behavior, don't do it
208    /// // let out_of_bounds = (0..400).map(|_| "000");
209    /// // let undefined_behavior = unsafe {
210    /// //     SmallString::from_iterator_unchecked(out_of_bounds)
211    /// // };
212    /// ```
213    #[inline]
214    pub unsafe fn from_iterator_unchecked<U, I>(iter: I) -> Self
215    where
216        U: AsRef<str>,
217        I: IntoIterator<Item = U>,
218    {
219        trace!("FromIterator unchecked");
220        let mut out = Self::default();
221        for s in iter {
222            out.push_str_unchecked(s);
223        }
224        out
225    }
226
227    /// Creates new `ArrayString` from char iterator if total length is lower or equal to [`capacity`], otherwise returns an error.
228    ///
229    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
230    ///
231    /// ```rust
232    /// # use arraystring::{error::Error, prelude::*};
233    /// # fn main() -> Result<(), Error> {
234    /// # let _ = env_logger::try_init();
235    /// let string = SmallString::try_from_chars("My String".chars())?;
236    /// assert_eq!(string.as_str(), "My String");
237    ///
238    /// let out_of_bounds = "0".repeat(SmallString::capacity() as usize + 1);
239    /// assert!(SmallString::try_from_chars(out_of_bounds.chars()).is_err());
240    /// # Ok(())
241    /// # }
242    /// ```
243    #[inline]
244    pub fn try_from_chars<I>(iter: I) -> Result<Self, OutOfBounds>
245    where
246        I: IntoIterator<Item = char>,
247    {
248        trace!("TryFrom chars");
249        let mut out = Self::default();
250        for c in iter {
251            out.try_push(c)?;
252        }
253        Ok(out)
254    }
255
256    /// Creates new `ArrayString` from char iterator truncating size if bigger than [`capacity`].
257    ///
258    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
259    ///
260    /// ```rust
261    /// # use arraystring::prelude::*;
262    /// # let _ = env_logger::try_init();
263    /// let string = SmallString::from_chars("My String".chars());
264    /// assert_eq!(string.as_str(), "My String");
265    ///
266    /// let out_of_bounds = "0".repeat(SmallString::capacity() as usize + 1);
267    /// let truncated = "0".repeat(SmallString::capacity().into());
268    ///
269    /// let truncate = SmallString::from_chars(out_of_bounds.chars());
270    /// assert_eq!(truncate.as_str(), truncated.as_str());
271    /// ```
272    #[inline]
273    pub fn from_chars<I>(iter: I) -> Self
274    where
275        I: IntoIterator<Item = char>,
276    {
277        trace!("From chars truncate");
278        let mut out = Self::default();
279        for c in iter {
280            if out.try_push(c).is_err() {
281                break;
282            }
283        }
284        out
285    }
286
287    /// Creates new `ArrayString` from char iterator assuming length is appropriate.
288    ///
289    /// # Safety
290    ///
291    /// It's UB if `iter.map(|c| c.len_utf8()).sum()` > [`capacity`].
292    ///
293    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
294    ///
295    /// ```rust
296    /// # use arraystring::prelude::*;
297    /// let string = unsafe { SmallString::from_chars_unchecked("My String".chars()) };
298    /// assert_eq!(string.as_str(), "My String");
299    ///
300    /// // Undefined behavior, don't do it
301    /// // let out_of_bounds = "000".repeat(400);
302    /// // let undefined_behavior = unsafe { SmallString::from_chars_unchecked(out_of_bounds.chars()) };
303    /// ```
304    #[inline]
305    pub unsafe fn from_chars_unchecked<I>(iter: I) -> Self
306    where
307        I: IntoIterator<Item = char>,
308    {
309        trace!("From chars unchecked");
310        let mut out = Self::default();
311        for c in iter {
312            out.push_unchecked(c)
313        }
314        out
315    }
316
317    /// Creates new `ArrayString` from byte slice, returning [`Utf8`] on invalid utf-8 data or [`OutOfBounds`] if bigger than [`capacity`]
318    ///
319    /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
320    /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
321    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
322    ///
323    /// ```rust
324    /// # use arraystring::{error::Error, prelude::*};
325    /// # fn main() -> Result<(), Error> {
326    /// # let _ = env_logger::try_init();
327    /// let string = SmallString::try_from_utf8("My String")?;
328    /// assert_eq!(string.as_str(), "My String");
329    ///
330    /// let invalid_utf8 = [0, 159, 146, 150];
331    /// assert_eq!(SmallString::try_from_utf8(invalid_utf8), Err(Error::Utf8));
332    ///
333    /// let out_of_bounds = "0000".repeat(400);
334    /// assert_eq!(SmallString::try_from_utf8(out_of_bounds.as_bytes()), Err(Error::OutOfBounds));
335    /// # Ok(())
336    /// # }
337    /// ```
338    #[inline]
339    pub fn try_from_utf8<B>(slice: B) -> Result<Self, Error>
340    where
341        B: AsRef<[u8]>,
342    {
343        debug!("From utf8: {:?}", slice.as_ref());
344        Ok(Self::try_from_str(from_utf8(slice.as_ref())?)?)
345    }
346
347    /// Creates new `ArrayString` from byte slice, returning [`Utf8`] on invalid utf-8 data, truncating if bigger than [`capacity`].
348    ///
349    /// [`Utf8`]: ./error/struct.Utf8.html
350    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
351    ///
352    /// ```rust
353    /// # use arraystring::{error::Error, prelude::*};
354    /// # fn main() -> Result<(), Error> {
355    /// # let _ = env_logger::try_init();
356    /// let string = SmallString::from_utf8("My String")?;
357    /// assert_eq!(string.as_str(), "My String");
358    ///
359    /// let invalid_utf8 = [0, 159, 146, 150];
360    /// assert_eq!(SmallString::from_utf8(invalid_utf8), Err(Utf8));
361    ///
362    /// let out_of_bounds = "0".repeat(300);
363    /// assert_eq!(SmallString::from_utf8(out_of_bounds.as_bytes())?.as_str(),
364    ///            "0".repeat(SmallString::capacity().into()).as_str());
365    /// # Ok(())
366    /// # }
367    /// ```
368    #[inline]
369    pub fn from_utf8<B>(slice: B) -> Result<Self, Utf8>
370    where
371        B: AsRef<[u8]>,
372    {
373        debug!("From utf8: {:?}", slice.as_ref());
374        Ok(Self::from_str_truncate(from_utf8(slice.as_ref())?))
375    }
376
377    /// Creates new `ArrayString` from byte slice assuming it's utf-8 and of a appropriate size.
378    ///
379    /// # Safety
380    ///
381    /// It's UB if `slice` is not a valid utf-8 string or `slice.len()` > [`capacity`].
382    ///
383    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
384    ///
385    /// ```rust
386    /// # use arraystring::prelude::*;
387    /// let string = unsafe { SmallString::from_utf8_unchecked("My String") };
388    /// assert_eq!(string.as_str(), "My String");
389    ///
390    /// // Undefined behavior, don't do it
391    /// // let out_of_bounds = "0".repeat(300);
392    /// // let ub = unsafe { SmallString::from_utf8_unchecked(out_of_bounds)) };
393    /// ```
394    #[inline]
395    pub unsafe fn from_utf8_unchecked<B>(slice: B) -> Self
396    where
397        B: AsRef<[u8]>,
398    {
399        trace!("From utf8 unchecked");
400        debug_assert!(from_utf8(slice.as_ref()).is_ok());
401        Self::from_str_unchecked(from_utf8_unchecked(slice.as_ref()))
402    }
403
404    /// Creates new `ArrayString` from `u16` slice, returning [`Utf16`] on invalid utf-16 data or [`OutOfBounds`] if bigger than [`capacity`]
405    ///
406    /// [`Utf16`]: ./error/enum.Error.html#variant.Utf16
407    /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
408    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
409    ///
410    /// ```rust
411    /// # use arraystring::{error::Error, prelude::*};
412    /// # fn main() -> Result<(), Error> {
413    /// # let _ = env_logger::try_init();
414    /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
415    /// let string = SmallString::try_from_utf16(music)?;
416    /// assert_eq!(string.as_str(), "𝄞music");
417    ///
418    /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
419    /// assert_eq!(SmallString::try_from_utf16(invalid_utf16), Err(Error::Utf16));
420    ///
421    /// let out_of_bounds: Vec<_> = (0..300).map(|_| 0).collect();
422    /// assert_eq!(SmallString::try_from_utf16(out_of_bounds), Err(Error::OutOfBounds));
423    /// # Ok(())
424    /// # }
425    /// ```
426    #[inline]
427    pub fn try_from_utf16<B>(slice: B) -> Result<Self, Error>
428    where
429        B: AsRef<[u16]>,
430    {
431        debug!("From utf16: {:?}", slice.as_ref());
432        let mut out = Self::default();
433        for c in decode_utf16(slice.as_ref().iter().cloned()) {
434            out.try_push(c?)?;
435        }
436        Ok(out)
437    }
438
439    /// Creates new `ArrayString` from `u16` slice, returning [`Utf16`] on invalid utf-16 data, truncating if bigger than [`capacity`].
440    ///
441    /// [`Utf16`]: ./error/struct.Utf16.html
442    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
443    ///
444    /// ```rust
445    /// # use arraystring::{error::Error, prelude::*};
446    /// # fn main() -> Result<(), Error> {
447    /// # let _ = env_logger::try_init();
448    /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
449    /// let string = SmallString::from_utf16(music)?;
450    /// assert_eq!(string.as_str(), "𝄞music");
451    ///
452    /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
453    /// assert_eq!(SmallString::from_utf16(invalid_utf16), Err(Utf16));
454    ///
455    /// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect();
456    /// assert_eq!(SmallString::from_utf16(out_of_bounds)?.as_str(),
457    ///            "\0".repeat(SmallString::capacity().into()).as_str());
458    /// # Ok(())
459    /// # }
460    /// ```
461    #[inline]
462    pub fn from_utf16<B>(slice: B) -> Result<Self, Utf16>
463    where
464        B: AsRef<[u16]>,
465    {
466        debug!("From utf16: {:?}", slice.as_ref());
467        let mut out = Self::default();
468        for c in decode_utf16(slice.as_ref().iter().cloned()) {
469            if out.try_push(c?).is_err() {
470                break;
471            }
472        }
473        Ok(out)
474    }
475
476    /// Creates new `ArrayString` from `u16` slice, replacing invalid utf-16 data with `REPLACEMENT_CHARACTER` (\u{FFFD}) and truncating size if bigger than [`capacity`]
477    ///
478    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
479    ///
480    /// ```rust
481    /// # use arraystring::{error::Error, prelude::*};
482    /// # fn main() -> Result<(), Error> {
483    /// # let _ = env_logger::try_init();
484    /// let music = [0xD834, 0xDD1E, 0x006d, 0x0075, 0x0073, 0x0069, 0x0063];
485    /// let string = SmallString::from_utf16_lossy(music);
486    /// assert_eq!(string.as_str(), "𝄞music");
487    ///
488    /// let invalid_utf16 = [0xD834, 0xDD1E, 0x006d, 0x0075, 0xD800, 0x0069, 0x0063];
489    /// assert_eq!(SmallString::from_utf16_lossy(invalid_utf16).as_str(), "𝄞mu\u{FFFD}ic");
490    ///
491    /// let out_of_bounds: Vec<u16> = (0..300).map(|_| 0).collect();
492    /// assert_eq!(SmallString::from_utf16_lossy(&out_of_bounds).as_str(),
493    ///            "\0".repeat(SmallString::capacity().into()).as_str());
494    /// # Ok(())
495    /// # }
496    /// ```
497    #[inline]
498    pub fn from_utf16_lossy<B>(slice: B) -> Self
499    where
500        B: AsRef<[u16]>,
501    {
502        debug!("From utf16 lossy: {:?}", slice.as_ref());
503        let mut out = Self::default();
504        for c in decode_utf16(slice.as_ref().iter().cloned()) {
505            if out.try_push(c.unwrap_or(REPLACEMENT_CHARACTER)).is_err() {
506                break;
507            }
508        }
509        out
510    }
511
512    /// Extracts a string slice containing the entire `ArrayString`
513    ///
514    /// ```rust
515    /// # use arraystring::{error::Error, prelude::*};
516    /// # fn main() -> Result<(), Error> {
517    /// # let _ = env_logger::try_init();
518    /// let s = SmallString::try_from_str("My String")?;
519    /// assert_eq!(s.as_str(), "My String");
520    /// # Ok(())
521    /// # }
522    /// ```
523    #[inline]
524    pub fn as_str(&self) -> &str {
525        trace!("As str: {}", <Self as AsRef<str>>::as_ref(self));
526        self.as_ref()
527    }
528
529    /// Extracts a mutable string slice containing the entire `ArrayString`
530    ///
531    /// ```rust
532    /// # use arraystring::{error::Error, prelude::*};
533    /// # fn main() -> Result<(), Error> {
534    /// # let _ = env_logger::try_init();
535    /// let mut s = SmallString::try_from_str("My String")?;
536    /// assert_eq!(s.as_mut_str(), "My String");
537    /// # Ok(())
538    /// # }
539    /// ```
540    #[inline]
541    pub fn as_mut_str(&mut self) -> &mut str {
542        trace!("As mut str: {}", self.as_mut());
543        self.as_mut()
544    }
545
546    /// Extracts a byte slice containing the entire `ArrayString`
547    ///
548    /// ```rust
549    /// # use arraystring::{error::Error, prelude::*};
550    /// # fn main() -> Result<(), Error> {
551    /// # let _ = env_logger::try_init();
552    /// let s = SmallString::try_from_str("My String")?;
553    /// assert_eq!(s.as_bytes(), "My String".as_bytes());
554    /// # Ok(())
555    /// # }
556    /// ```
557    #[inline]
558    pub fn as_bytes(&self) -> &[u8] {
559        trace!("As str: {}", self.as_str());
560        self.as_ref()
561    }
562
563    /// Extracts a mutable string slice containing the entire `ArrayString`
564    ///
565    /// ```rust
566    /// # use arraystring::{error::Error, prelude::*};
567    /// # fn main() -> Result<(), Error> {
568    /// let mut s = SmallString::try_from_str("My String")?;
569    /// assert_eq!(unsafe { s.as_mut_bytes() }, "My String".as_bytes());
570    /// # Ok(())
571    /// # }
572    /// ```
573    #[inline]
574    pub unsafe fn as_mut_bytes(&mut self) -> &mut [u8] {
575        trace!("As mut str: {}", self.as_str());
576        let len = self.len();
577        self.array.as_mut_slice().get_unchecked_mut(..len.into())
578    }
579
580    /// Returns maximum string capacity, defined at compile time, it will never change
581    ///
582    /// ```rust
583    /// # use arraystring::prelude::*;
584    /// # let _ = env_logger::try_init();
585    /// assert_eq!(ArrayString::<typenum::U32>::capacity(), 32);
586    /// ```
587    #[inline]
588    pub fn capacity() -> u8 {
589        SIZE::to_u8()
590    }
591
592    /// Pushes string slice to the end of the `ArrayString` if total size is lower or equal to [`capacity`], otherwise returns an error.
593    ///
594    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
595    ///
596    /// ```rust
597    /// # use arraystring::{error::Error, prelude::*};
598    /// # fn main() -> Result<(), Error> {
599    /// # let _ = env_logger::try_init();
600    /// let mut s = MaxString::try_from_str("My String")?;
601    /// s.try_push_str(" My other String")?;
602    /// assert_eq!(s.as_str(), "My String My other String");
603    ///
604    /// assert!(s.try_push_str("0".repeat(MaxString::capacity().into())).is_err());
605    /// # Ok(())
606    /// # }
607    /// ```
608    #[inline]
609    pub fn try_push_str<S>(&mut self, string: S) -> Result<(), OutOfBounds>
610    where
611        S: AsRef<str>,
612    {
613        trace!("Push str");
614        let new_end = string.as_ref().len().saturating_add(self.len().into());
615        is_inside_boundary(new_end, Self::capacity())?;
616        unsafe { self.push_str_unchecked(string) };
617        Ok(())
618    }
619
620    /// Pushes string slice to the end of the `ArrayString` truncating total size if bigger than [`capacity`].
621    ///
622    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
623    ///
624    /// ```rust
625    /// # use arraystring::{error::Error, prelude::*};
626    /// # fn main() -> Result<(), Error> {
627    /// # let _ = env_logger::try_init();
628    /// let mut s = MaxString::try_from_str("My String")?;
629    /// s.push_str(" My other String");
630    /// assert_eq!(s.as_str(), "My String My other String");
631    ///
632    /// let mut s = SmallString::default();
633    /// s.push_str("0".repeat(SmallString::capacity() as usize + 1));
634    /// assert_eq!(s.as_str(), "0".repeat(SmallString::capacity().into()).as_str());
635    /// # Ok(())
636    /// # }
637    /// ```
638    #[inline]
639    pub fn push_str<S>(&mut self, string: S)
640    where
641        S: AsRef<str>,
642    {
643        trace!("Push str truncate");
644        let size = Self::capacity().saturating_sub(self.len());
645        unsafe { self.push_str_unchecked(truncate_str(string.as_ref(), size)) }
646    }
647
648    /// Pushes string slice to the end of the `ArrayString` assuming total size is appropriate.
649    ///
650    /// # Safety
651    ///
652    /// It's UB if `self.len() + string.len()` > [`capacity`].
653    ///
654    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
655    ///
656    /// ```rust
657    /// # use arraystring::{error::Error, prelude::*};
658    /// # fn main() -> Result<(), Error> {
659    /// let mut s = MaxString::try_from_str("My String")?;
660    /// unsafe { s.push_str_unchecked(" My other String") };
661    /// assert_eq!(s.as_str(), "My String My other String");
662    ///
663    /// // Undefined behavior, don't do it
664    /// // let mut undefined_behavior = SmallString::default();
665    /// // undefined_behavior.push_str_unchecked("0".repeat(SmallString::capacity().into() + 1));
666    /// # Ok(())
667    /// # }
668    /// ```
669    #[inline]
670    pub unsafe fn push_str_unchecked<S>(&mut self, string: S)
671    where
672        S: AsRef<str>,
673    {
674        let (s, len) = (string.as_ref(), string.as_ref().len());
675        debug!("Push str unchecked: {} ({} + {})", s, self.len(), len);
676        debug_assert!(len.saturating_add(self.len().into()) <= Self::capacity() as usize);
677
678        let dest = self.as_mut_bytes().as_mut_ptr().add(self.len().into());
679        copy_nonoverlapping(s.as_ptr(), dest, len);
680        self.size = self.size.saturating_add(len.into_lossy());
681    }
682
683    /// Inserts character to the end of the `ArrayString` erroring if total size if bigger than [`capacity`].
684    ///
685    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
686    ///
687    /// ```rust
688    /// # use arraystring::{error::Error, prelude::*};
689    /// # fn main() -> Result<(), Error> {
690    /// # let _ = env_logger::try_init();
691    /// let mut s = SmallString::try_from_str("My String")?;
692    /// s.try_push('!')?;
693    /// assert_eq!(s.as_str(), "My String!");
694    ///
695    /// let mut s = SmallString::try_from_str(&"0".repeat(SmallString::capacity().into()))?;
696    /// assert!(s.try_push('!').is_err());
697    /// # Ok(())
698    /// # }
699    /// ```
700    #[inline]
701    pub fn try_push(&mut self, character: char) -> Result<(), OutOfBounds> {
702        trace!("Push: {}", character);
703        let new_end = character.len_utf8().saturating_add(self.len().into());
704        is_inside_boundary(new_end, Self::capacity())?;
705        unsafe { self.push_unchecked(character) };
706        Ok(())
707    }
708
709    /// Inserts character to the end of the `ArrayString` assuming length is appropriate
710    ///
711    /// # Safety
712    ///
713    /// It's UB if `self.len() + character.len_utf8()` > [`capacity`]
714    ///
715    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
716    ///
717    /// ```rust
718    /// # use arraystring::{error::Error, prelude::*};
719    /// # fn main() -> Result<(), Error> {
720    /// let mut s = SmallString::try_from_str("My String")?;
721    /// unsafe { s.push_unchecked('!') };
722    /// assert_eq!(s.as_str(), "My String!");
723    ///
724    /// // s = SmallString::try_from_str(&"0".repeat(SmallString::capacity().into()))?;
725    /// // Undefined behavior, don't do it
726    /// // s.push_unchecked('!');
727    /// # Ok(())
728    /// # }
729    /// ```
730    #[inline]
731    pub unsafe fn push_unchecked(&mut self, ch: char) {
732        let (len, chlen) = (self.len(), ch.len_utf8().into_lossy());
733        debug!("Push unchecked (len: {}): {} (len: {})", len, ch, chlen);
734        encode_char_utf8_unchecked(self, ch, len);
735        self.size = self.size.saturating_add(chlen);
736    }
737
738    /// Truncates `ArrayString` to specified size (if smaller than current size and a valid utf-8 char index).
739    ///
740    /// ```rust
741    /// # use arraystring::{error::Error, prelude::*};
742    /// # fn main() -> Result<(), Error> {
743    /// # let _ = env_logger::try_init();
744    /// let mut s = SmallString::try_from_str("My String")?;
745    /// s.truncate(5)?;
746    /// assert_eq!(s.as_str(), "My St");
747    ///
748    /// // Does nothing
749    /// s.truncate(6)?;
750    /// assert_eq!(s.as_str(), "My St");
751    ///
752    /// // Index is not at a valid char
753    /// let mut s = SmallString::try_from_str("🤔")?;
754    /// assert!(s.truncate(1).is_err());
755    /// # Ok(())
756    /// # }
757    /// ```
758    #[inline]
759    pub fn truncate(&mut self, size: u8) -> Result<(), Utf8> {
760        debug!("Truncate: {}", size);
761        let len = min(self.len(), size);
762        is_char_boundary(self, len).map(|()| self.size = len)
763    }
764
765    /// Removes last character from `ArrayString`, if any.
766    ///
767    /// ```rust
768    /// # use arraystring::{error::Error, prelude::*};
769    /// # fn main() -> Result<(), Error> {
770    /// # let _ = env_logger::try_init();
771    /// let mut s = SmallString::try_from_str("A🤔")?;
772    /// assert_eq!(s.pop(), Some('🤔'));
773    /// assert_eq!(s.pop(), Some('A'));
774    /// assert_eq!(s.pop(), None);
775    /// # Ok(())
776    /// # }
777    /// ```
778    #[inline]
779    pub fn pop(&mut self) -> Option<char> {
780        debug!("Pop");
781        self.as_str().chars().last().map(|ch| {
782            self.size = self.size.saturating_sub(ch.len_utf8().into_lossy());
783            ch
784        })
785    }
786
787    /// Removes spaces from the beggining and end of the string
788    ///
789    /// ```rust
790    /// # use arraystring::prelude::*;
791    /// # fn main() -> Result<(), OutOfBounds> {
792    /// # let _ = env_logger::try_init();
793    /// let mut string = MaxString::try_from_str("   to be trimmed     ")?;
794    /// string.trim();
795    /// assert_eq!(string.as_str(), "to be trimmed");
796    ///
797    /// let mut string = SmallString::try_from_str("   🤔")?;
798    /// string.trim();
799    /// assert_eq!(string.as_str(), "🤔");
800    /// # Ok(())
801    /// # }
802    /// ```
803    #[inline]
804    pub fn trim(&mut self) {
805        trace!("Trim");
806        let is_whitespace = |s: &[u8], index: usize| {
807            debug_assert!(index < s.len());
808            unsafe { s.get_unchecked(index) == &b' ' }
809        };
810        let (mut start, mut end, mut leave) = (0_u8, self.len(), 0_u8);
811        while start < end && leave < 2 {
812            leave = 0;
813
814            if is_whitespace(self.as_bytes(), start.into()) {
815                start = start.saturating_add(1);
816                if start >= end {
817                    continue;
818                };
819            } else {
820                leave = leave.saturating_add(1);
821            }
822
823            if start < end && is_whitespace(self.as_bytes(), end.saturating_sub(1).into()) {
824                end = end.saturating_sub(1);
825            } else {
826                leave = leave.saturating_add(1);
827            }
828        }
829
830        unsafe { shift_left_unchecked(self, start, 0u8) };
831        self.size = end.saturating_sub(start);
832    }
833
834    /// Removes specified char from `ArrayString`
835    ///
836    /// ```rust
837    /// # use arraystring::{error::Error, prelude::*};
838    /// # fn main() -> Result<(), Error> {
839    /// # let _ = env_logger::try_init();
840    /// let mut s = SmallString::try_from_str("ABCD🤔")?;
841    /// assert_eq!(s.remove("ABCD🤔".len() as u8), Err(Error::OutOfBounds));
842    /// assert_eq!(s.remove(10), Err(Error::OutOfBounds));
843    /// assert_eq!(s.remove(6), Err(Error::Utf8));
844    /// assert_eq!(s.remove(0), Ok('A'));
845    /// assert_eq!(s.as_str(), "BCD🤔");
846    /// assert_eq!(s.remove(2), Ok('D'));
847    /// assert_eq!(s.as_str(), "BC🤔");
848    /// # Ok(())
849    /// # }
850    /// ```
851    #[inline]
852    pub fn remove(&mut self, idx: u8) -> Result<char, Error> {
853        debug!("Remove: {}", idx);
854        is_inside_boundary(idx, self.len().saturating_sub(1))?;
855        is_char_boundary(self, idx)?;
856        debug_assert!(idx < self.len() && self.as_str().is_char_boundary(idx.into()));
857        let ch = unsafe { self.as_str().get_unchecked(idx.into()..).chars().next() };
858        let ch = ch.unwrap_or_else(|| unsafe { never("Missing char") });
859        unsafe { shift_left_unchecked(self, idx.saturating_add(ch.len_utf8().into_lossy()), idx) };
860        self.size = self.size.saturating_sub(ch.len_utf8().into_lossy());
861        Ok(ch)
862    }
863
864    /// Retains only the characters specified by the predicate.
865    ///
866    /// ```rust
867    /// # use arraystring::{error::Error, prelude::*};
868    /// # fn main() -> Result<(), Error> {
869    /// # let _ = env_logger::try_init();
870    /// let mut s = SmallString::try_from_str("ABCD🤔")?;
871    /// s.retain(|c| c != '🤔');
872    /// assert_eq!(s.as_str(), "ABCD");
873    /// # Ok(())
874    /// # }
875    /// ```
876    #[inline]
877    pub fn retain<F: FnMut(char) -> bool>(&mut self, mut f: F) {
878        trace!("Retain");
879        // Not the most efficient solution, we could shift left during batch mismatch
880        *self = unsafe { Self::from_chars_unchecked(self.as_str().chars().filter(|c| f(*c))) };
881    }
882
883    /// Inserts character at specified index, returning error if total length is bigger than [`capacity`].
884    ///
885    /// Returns [`OutOfBounds`] if `idx` is out of bounds and [`Utf8`] if `idx` is not a char position
886    ///
887    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
888    /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
889    /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
890    ///
891    /// ```rust
892    /// # use arraystring::{error::Error, prelude::*};
893    /// # fn main() -> Result<(), Error> {
894    /// # let _ = env_logger::try_init();
895    /// let mut s = SmallString::try_from_str("ABCD🤔")?;
896    /// s.try_insert(1, 'A')?;
897    /// s.try_insert(2, 'B')?;
898    /// assert_eq!(s.as_str(), "AABBCD🤔");
899    /// assert_eq!(s.try_insert(20, 'C'), Err(Error::OutOfBounds));
900    /// assert_eq!(s.try_insert(8, 'D'), Err(Error::Utf8));
901    ///
902    /// let mut s = SmallString::try_from_str(&"0".repeat(SmallString::capacity().into()))?;
903    /// assert_eq!(s.try_insert(0, 'C'), Err(Error::OutOfBounds));
904    /// # Ok(())
905    /// # }
906    /// ```
907    #[inline]
908    pub fn try_insert(&mut self, idx: u8, ch: char) -> Result<(), Error> {
909        trace!("Insert {} to {}", ch, idx);
910        is_inside_boundary(idx, self.len())?;
911        let new_end = ch.len_utf8().saturating_add(self.len().into());
912        is_inside_boundary(new_end, Self::capacity())?;
913        is_char_boundary(self, idx)?;
914        unsafe { self.insert_unchecked(idx, ch) };
915        Ok(())
916    }
917
918    /// Inserts character at specified index assuming length is appropriate
919    ///
920    /// # Safety
921    ///
922    /// It's UB if `idx` does not lie on a utf-8 `char` boundary
923    ///
924    /// It's UB if `self.len() + character.len_utf8()` > [`capacity`]
925    ///
926    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
927    ///
928    /// ```rust
929    /// # use arraystring::{error::Error, prelude::*};
930    /// # fn main() -> Result<(), Error> {
931    /// let mut s = SmallString::try_from_str("ABCD🤔")?;
932    /// unsafe { s.insert_unchecked(1, 'A') };
933    /// unsafe { s.insert_unchecked(1, 'B') };
934    /// assert_eq!(s.as_str(), "ABABCD🤔");
935    ///
936    /// // Undefined behavior, don't do it
937    /// // s.insert(20, 'C');
938    /// // s.insert(8, 'D');
939    /// # Ok(())
940    /// # }
941    /// ```
942    #[inline]
943    pub unsafe fn insert_unchecked(&mut self, idx: u8, ch: char) {
944        let clen = ch.len_utf8().into_lossy();
945        debug!("Insert uncheck ({}+{}) {} at {}", self.len(), clen, ch, idx);
946        shift_right_unchecked(self, idx, idx.saturating_add(clen));
947        encode_char_utf8_unchecked(self, ch, idx);
948        self.size = self.size.saturating_add(clen);
949    }
950
951    /// Inserts string slice at specified index, returning error if total length is bigger than [`capacity`].
952    ///
953    /// Returns [`OutOfBounds`] if `idx` is out of bounds
954    /// Returns [`Utf8`] if `idx` is not a char position
955    ///
956    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
957    /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
958    /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
959    ///
960    /// ```rust
961    /// # use arraystring::{error::Error, prelude::*};
962    /// # fn main() -> Result<(), Error> {
963    /// # let _ = env_logger::try_init();
964    /// let mut s = SmallString::try_from_str("ABCD🤔")?;
965    /// s.try_insert_str(1, "AB")?;
966    /// s.try_insert_str(1, "BC")?;
967    /// assert_eq!(s.try_insert_str(1, "0".repeat(SmallString::capacity().into())),
968    ///            Err(Error::OutOfBounds));
969    /// assert_eq!(s.as_str(), "ABCABBCD🤔");
970    /// assert_eq!(s.try_insert_str(20, "C"), Err(Error::OutOfBounds));
971    /// assert_eq!(s.try_insert_str(10, "D"), Err(Error::Utf8));
972    /// # Ok(())
973    /// # }
974    /// ```
975    #[inline]
976    pub fn try_insert_str<S>(&mut self, idx: u8, s: S) -> Result<(), Error>
977    where
978        S: AsRef<str>,
979    {
980        trace!("Try insert str");
981        is_inside_boundary(idx, self.len())?;
982        let new_end = s.as_ref().len().saturating_add(self.len().into());
983        is_inside_boundary(new_end, Self::capacity())?;
984        is_char_boundary(self, idx)?;
985        unsafe { self.insert_str_unchecked(idx, s.as_ref()) };
986        Ok(())
987    }
988
989    /// Inserts string slice at specified index, truncating size if bigger than [`capacity`].
990    ///
991    /// Returns [`OutOfBounds`] if `idx` is out of bounds and [`Utf8`] if `idx` is not a char position
992    ///
993    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
994    /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
995    /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
996    ///
997    /// ```rust
998    /// # use arraystring::{error::Error, prelude::*};
999    /// # fn main() -> Result<(), Error> {
1000    /// # let _ = env_logger::try_init();
1001    /// let mut s = SmallString::try_from_str("ABCD🤔")?;
1002    /// s.insert_str(1, "AB")?;
1003    /// s.insert_str(1, "BC")?;
1004    /// assert_eq!(s.as_str(), "ABCABBCD🤔");
1005    ///
1006    /// assert_eq!(s.insert_str(20, "C"), Err(Error::OutOfBounds));
1007    /// assert_eq!(s.insert_str(10, "D"), Err(Error::Utf8));
1008    ///
1009    /// s.clear();
1010    /// s.insert_str(0, "0".repeat(SmallString::capacity() as usize + 10))?;
1011    /// assert_eq!(s.as_str(), "0".repeat(SmallString::capacity().into()).as_str());
1012    /// # Ok(())
1013    /// # }
1014    /// ```
1015    #[inline]
1016    pub fn insert_str<S>(&mut self, idx: u8, string: S) -> Result<(), Error>
1017    where
1018        S: AsRef<str>,
1019    {
1020        trace!("Insert str");
1021        is_inside_boundary(idx, self.len())?;
1022        is_char_boundary(self, idx)?;
1023        let size = Self::capacity().saturating_sub(self.len());
1024        unsafe { self.insert_str_unchecked(idx, truncate_str(string.as_ref(), size)) };
1025        Ok(())
1026    }
1027
1028    /// Inserts string slice at specified index, assuming total length is appropriate.
1029    ///
1030    /// # Safety
1031    ///
1032    /// It's UB if `idx` does not lie on a utf-8 `char` boundary
1033    ///
1034    /// It's UB if `self.len() + string.len()` > [`capacity`]
1035    ///
1036    /// [`capacity`]: ./struct.ArrayString.html#method.capacity
1037    ///
1038    /// ```rust
1039    /// # use arraystring::{error::Error, prelude::*};
1040    /// # fn main() -> Result<(), Error> {
1041    /// let mut s = SmallString::try_from_str("ABCD🤔")?;
1042    /// unsafe { s.insert_str_unchecked(1, "AB") };
1043    /// unsafe { s.insert_str_unchecked(1, "BC") };
1044    /// assert_eq!(s.as_str(), "ABCABBCD🤔");
1045    ///
1046    /// // Undefined behavior, don't do it
1047    /// // unsafe { s.insert_str_unchecked(20, "C") };
1048    /// // unsafe { s.insert_str_unchecked(10, "D") };
1049    /// // unsafe { s.insert_str_unchecked(1, "0".repeat(SmallString::capacity().into())) };
1050    /// # Ok(())
1051    /// # }
1052    /// ```
1053    #[inline]
1054    pub unsafe fn insert_str_unchecked<S>(&mut self, idx: u8, string: S)
1055    where
1056        S: AsRef<str>,
1057    {
1058        let (s, slen) = (string.as_ref(), string.as_ref().len().into_lossy());
1059        let ptr = s.as_ptr();
1060        trace!("InsertStr uncheck {}+{} {} at {}", self.len(), slen, s, idx);
1061        debug_assert!(self.len().saturating_add(slen) <= Self::capacity());
1062        debug_assert!(idx <= self.len());
1063        debug_assert!(self.as_str().is_char_boundary(idx.into()));
1064
1065        shift_right_unchecked(self, idx, idx.saturating_add(slen));
1066        let dest = self.as_mut_bytes().as_mut_ptr().add(idx.into());
1067        copy_nonoverlapping(ptr, dest, slen.into());
1068        self.size = self.size.saturating_add(slen);
1069    }
1070
1071    /// Returns `ArrayString` length.
1072    ///
1073    /// ```rust
1074    /// # use arraystring::{error::Error, prelude::*};
1075    /// # fn main() -> Result<(), Error> {
1076    /// # let _ = env_logger::try_init();
1077    /// let mut s = SmallString::try_from_str("ABCD")?;
1078    /// assert_eq!(s.len(), 4);
1079    /// s.try_push('🤔')?;
1080    /// // Emojis use 4 bytes (this is the default rust behavior, length of u8)
1081    /// assert_eq!(s.len(), 8);
1082    /// # Ok(())
1083    /// # }
1084    /// ```
1085    #[inline]
1086    pub fn len(&self) -> u8 {
1087        trace!("Len");
1088        self.size
1089    }
1090
1091    /// Checks if `ArrayString` is empty.
1092    ///
1093    /// ```rust
1094    /// # use arraystring::{error::Error, prelude::*};
1095    /// # fn main() -> Result<(), Error> {
1096    /// # let _ = env_logger::try_init();
1097    /// let mut s = SmallString::try_from_str("ABCD")?;
1098    /// assert!(!s.is_empty());
1099    /// s.clear();
1100    /// assert!(s.is_empty());
1101    /// # Ok(())
1102    /// # }
1103    /// ```
1104    #[inline]
1105    pub fn is_empty(&self) -> bool {
1106        trace!("Is empty");
1107        self.len() == 0
1108    }
1109
1110    /// Splits `ArrayString` in two if `at` is smaller than `self.len()`.
1111    ///
1112    /// Returns [`Utf8`] if `at` does not lie at a valid utf-8 char boundary and [`OutOfBounds`] if it's out of bounds
1113    ///
1114    /// [`OutOfBounds`]: ./error/enum.Error.html#variant.OutOfBounds
1115    /// [`Utf8`]: ./error/enum.Error.html#variant.Utf8
1116    ///
1117    /// ```rust
1118    /// # use arraystring::{error::Error, prelude::*};
1119    /// # fn main() -> Result<(), Error> {
1120    /// # let _ = env_logger::try_init();
1121    /// let mut s = SmallString::try_from_str("AB🤔CD")?;
1122    /// assert_eq!(s.split_off(6)?.as_str(), "CD");
1123    /// assert_eq!(s.as_str(), "AB🤔");
1124    /// assert_eq!(s.split_off(20), Err(Error::OutOfBounds));
1125    /// assert_eq!(s.split_off(4), Err(Error::Utf8));
1126    /// # Ok(())
1127    /// # }
1128    /// ```
1129    #[inline]
1130    pub fn split_off(&mut self, at: u8) -> Result<Self, Error> {
1131        debug!("Split off");
1132        is_inside_boundary(at, self.len())?;
1133        is_char_boundary(self, at)?;
1134        debug_assert!(at <= self.len() && self.as_str().is_char_boundary(at.into()));
1135        let new = unsafe { Self::from_utf8_unchecked(self.as_str().get_unchecked(at.into()..)) };
1136        self.size = at;
1137        Ok(new)
1138    }
1139
1140    /// Empties `ArrayString`
1141    ///
1142    /// ```rust
1143    /// # use arraystring::{error::Error, prelude::*};
1144    /// # fn main() -> Result<(), Error> {
1145    /// # let _ = env_logger::try_init();
1146    /// let mut s = SmallString::try_from_str("ABCD")?;
1147    /// assert!(!s.is_empty());
1148    /// s.clear();
1149    /// assert!(s.is_empty());
1150    /// # Ok(())
1151    /// # }
1152    /// ```
1153    #[inline]
1154    pub fn clear(&mut self) {
1155        trace!("Clear");
1156        self.size = 0;
1157    }
1158
1159    /// Creates a draining iterator that removes the specified range in the `ArrayString` and yields the removed chars.
1160    ///
1161    /// Note: The element range is removed even if the iterator is not consumed until the end.
1162    ///
1163    /// ```rust
1164    /// # use arraystring::{error::Error, prelude::*};
1165    /// # fn main() -> Result<(), Error> {
1166    /// # let _ = env_logger::try_init();
1167    /// let mut s = SmallString::try_from_str("ABCD🤔")?;
1168    /// assert_eq!(s.drain(..3)?.collect::<Vec<_>>(), vec!['A', 'B', 'C']);
1169    /// assert_eq!(s.as_str(), "D🤔");
1170    ///
1171    /// assert_eq!(s.drain(3..), Err(Error::Utf8));
1172    /// assert_eq!(s.drain(10..), Err(Error::OutOfBounds));
1173    /// # Ok(())
1174    /// # }
1175    /// ```
1176    #[inline]
1177    pub fn drain<R>(&mut self, range: R) -> Result<Drain<SIZE>, Error>
1178    where
1179        R: RangeBounds<u8>,
1180    {
1181        let start = match range.start_bound() {
1182            Bound::Included(t) => *t,
1183            Bound::Excluded(t) => t.saturating_add(1),
1184            Bound::Unbounded => 0,
1185        };
1186        let end = match range.end_bound() {
1187            Bound::Included(t) => t.saturating_add(1),
1188            Bound::Excluded(t) => *t,
1189            Bound::Unbounded => self.len(),
1190        };
1191
1192        debug!("Drain iterator (len: {}): {}..{}", self.len(), start, end);
1193        is_inside_boundary(start, end)?;
1194        is_inside_boundary(end, self.len())?;
1195        is_char_boundary(self, start)?;
1196        is_char_boundary(self, end)?;
1197        debug_assert!(start <= end && end <= self.len());
1198        debug_assert!(self.as_str().is_char_boundary(start.into()));
1199        debug_assert!(self.as_str().is_char_boundary(end.into()));
1200
1201        let drain = unsafe {
1202            let slice = self.as_str().get_unchecked(start.into()..end.into());
1203            Self::from_str_unchecked(slice)
1204        };
1205        unsafe { shift_left_unchecked(self, end, start) };
1206        self.size = self.size.saturating_sub(end.saturating_sub(start));
1207        Ok(Drain(drain, 0))
1208    }
1209
1210    /// Removes the specified range of the `ArrayString`, and replaces it with the given string. The given string doesn't need to have the same length as the range.
1211    ///
1212    /// ```rust
1213    /// # use arraystring::{error::Error, prelude::*};
1214    /// # fn main() -> Result<(), Error> {
1215    /// # let _ = env_logger::try_init();
1216    /// let mut s = SmallString::try_from_str("ABCD🤔")?;
1217    /// s.replace_range(2..4, "EFGHI")?;
1218    /// assert_eq!(s.as_str(), "ABEFGHI🤔");
1219    ///
1220    /// assert_eq!(s.replace_range(9.., "J"), Err(Error::Utf8));
1221    /// assert_eq!(s.replace_range(..90, "K"), Err(Error::OutOfBounds));
1222    /// assert_eq!(s.replace_range(0..1, "0".repeat(SmallString::capacity().into())),
1223    ///            Err(Error::OutOfBounds));
1224    /// # Ok(())
1225    /// # }
1226    /// ```
1227    #[inline]
1228    pub fn replace_range<S, R>(&mut self, r: R, with: S) -> Result<(), Error>
1229    where
1230        S: AsRef<str>,
1231        R: RangeBounds<u8>,
1232    {
1233        let replace_with = with.as_ref();
1234        let start = match r.start_bound() {
1235            Bound::Included(t) => *t,
1236            Bound::Excluded(t) => t.saturating_add(1),
1237            Bound::Unbounded => 0,
1238        };
1239        let end = match r.end_bound() {
1240            Bound::Included(t) => t.saturating_add(1),
1241            Bound::Excluded(t) => *t,
1242            Bound::Unbounded => self.len(),
1243        };
1244
1245        let len = replace_with.len().into_lossy();
1246        debug!(
1247            "Replace range (len: {}) ({}..{}) with (len: {}) {}",
1248            self.len(),
1249            start,
1250            end,
1251            len,
1252            replace_with
1253        );
1254
1255        is_inside_boundary(start, end)?;
1256        is_inside_boundary(end, self.len())?;
1257        let replaced = (end as usize).saturating_sub(start.into());
1258        is_inside_boundary(replaced.saturating_add(len.into()), Self::capacity())?;
1259        is_char_boundary(self, start)?;
1260        is_char_boundary(self, end)?;
1261
1262        debug_assert!(start <= end && end <= self.len());
1263        debug_assert!(len.saturating_sub(end).saturating_add(start) <= Self::capacity());
1264        debug_assert!(self.as_str().is_char_boundary(start.into()));
1265        debug_assert!(self.as_str().is_char_boundary(end.into()));
1266
1267        if start.saturating_add(len) > end {
1268            unsafe { shift_right_unchecked(self, end, start.saturating_add(len)) };
1269        } else {
1270            unsafe { shift_left_unchecked(self, end, start.saturating_add(len)) };
1271        }
1272
1273        let grow = len.saturating_sub(replaced.into_lossy());
1274        self.size = self.size.saturating_add(grow);
1275        let ptr = replace_with.as_ptr();
1276        let dest = unsafe { self.as_mut_bytes().as_mut_ptr().add(start.into()) };
1277        unsafe { copy_nonoverlapping(ptr, dest, len.into()) };
1278        Ok(())
1279    }
1280}