tiny_str/
drain.rs

1//! [drain](TinyString::drain) implementation
2
3use core::iter::FusedIterator;
4use core::marker::PhantomData;
5use core::mem::ManuallyDrop;
6use core::ops::{Range, RangeBounds};
7use core::ptr::{self, NonNull};
8use core::{slice, str};
9use crate::TinyString;
10
11/// A draining iterator for [TinyString]
12///
13/// This struct is created by the [TinyString::drain] method
14pub struct Drain<'a, const N: usize> {
15    string: NonNull<TinyString<N>>,
16    remaining_start: usize,
17    remaining_len: usize,
18    tail_start: usize,
19    tail_len: usize,
20    _marker: PhantomData<&'a mut TinyString<N>>,
21}
22
23impl<const N: usize> Drain<'_, N> {
24
25    /// Returns the remaining slice
26    pub const fn as_str(&mut self) -> &str {
27        if self.remaining_len == 0 {
28            return ""
29        }
30        unsafe {
31            let str = self.string.as_ref().as_ptr().add(self.remaining_start);
32            let slice = slice::from_raw_parts(str, self.remaining_len);
33            str::from_utf8_unchecked(slice)
34        }
35    }
36
37    /// Consumes this [Drain], preserving any un-yielded characters
38    ///
39    /// # Example
40    /// ```
41    /// use tiny_str::TinyString;
42    ///
43    /// let mut tv = TinyString::<10>::from("abcdefghijk");
44    ///
45    /// let mut drain = tv.drain(3..7);
46    ///
47    /// assert_eq!(drain.next(), Some('d'));
48    /// assert_eq!(drain.next(), Some('e'));
49    ///
50    /// drain.keep_rest();
51    ///
52    /// assert_eq!(tv.as_str(), "abcfghijk");
53    /// ```
54    pub fn keep_rest(self) {
55        let mut slf = ManuallyDrop::new(self);
56
57        /* [ HEAD ] [ yieled  | remaining | yielded_back ] [ TAIL ] */
58        unsafe {
59            let string = slf.string.as_mut();
60            let start = string.len();
61
62            let buf = string.as_mut_ptr();
63
64            let dst = buf.add(start);
65            let src = buf.add(slf.remaining_start);
66            /* First: We move the remaining chunk to the start */
67            ptr::copy(src, dst, slf.remaining_len);
68
69            let src = buf.add(slf.tail_start);
70            let dst = dst.add(slf.remaining_len);
71            /* Now we move the tail */
72            ptr::copy(src, dst, slf.tail_len);
73
74            string.buf.set_len(start + slf.remaining_len + slf.tail_len);
75        }
76    }
77}
78
79impl<const N: usize> Iterator for Drain<'_, N> {
80    type Item = char;
81
82    fn next(&mut self) -> Option<Self::Item> {
83        let c = self.as_str().chars().next()?;
84        self.remaining_start += c.len_utf8();
85        self.remaining_len -= c.len_utf8();
86        Some(c)
87    }
88
89    fn size_hint(&self) -> (usize, Option<usize>) {
90        (self.remaining_len, Some(self.remaining_len))
91    }
92}
93
94impl<const N: usize> DoubleEndedIterator for Drain<'_, N> {
95    fn next_back(&mut self) -> Option<Self::Item> {
96        let c = self.as_str().chars().next_back()?;
97        self.remaining_len -= c.len_utf8();
98        Some(c)
99    }
100}
101
102impl<const N: usize> ExactSizeIterator for Drain<'_, N> {}
103
104impl<const N: usize> FusedIterator for Drain<'_, N> {}
105
106impl<const N: usize> Drop for Drain<'_, N> {
107    fn drop(&mut self) {
108        unsafe {
109            let string = self.string.as_mut();
110            let len = string.len();
111            let ptr = string.as_mut_ptr();
112
113            let src = ptr.add(self.tail_start);
114            let dst = ptr.add(len);
115            ptr::copy(src, dst, self.tail_len);
116
117            string.buf.set_len(len + self.tail_len);
118        }
119    }
120}
121
122impl<const N: usize> TinyString<N> {
123
124    /// Removes the substring indicated by the given range from the string,
125    /// returning a double-ended iterator over the removed substring.
126    ///
127    /// If the iterator is dropped before being fully consumed,
128    /// it drops the remaining removed elements.
129    ///
130    /// # Panics
131    ///
132    /// Panics if the starting point is greater than the end point, if
133    /// the end point is greater than the length of the vector,
134    /// or if any of the ends of the range fall outside a char boundary.
135    ///
136    /// # Leaking
137    ///
138    /// If the returned iterator goes out of scope without being dropped (due to
139    /// [`core::mem::forget`], for example), the vector may have lost characters
140    /// arbitrarily, including characters outside the range.
141    ///
142    /// # Examples
143    ///
144    /// ```
145    /// use tiny_str::TinyString;
146    /// let mut s = TinyString::<10>::from("abcdefghijk");
147    ///
148    /// let mut drain = s.drain(2..=4);
149    /// assert_eq!(drain.next(), Some('c'));
150    /// assert_eq!(drain.next(), Some('d'));
151    /// assert_eq!(drain.next(), Some('e'));
152    /// assert_eq!(drain.next(), None);
153    /// drop(drain);
154    ///
155    /// assert_eq!(s.as_str(), "abfghijk");
156    ///
157    /// // A full range clears the string, like `clear()` does
158    /// s.drain(..);
159    /// assert_eq!(s.as_str(), "");
160    /// ```
161    pub fn drain<R>(&mut self, range: R) -> Drain<'_, N>
162    where
163        R: RangeBounds<usize>
164    {
165        let len = self.len();
166        let Range { start, end } = self.slice_range(range, len);
167        unsafe {
168            self.buf.set_len(start);
169
170            let string = NonNull::new_unchecked(self as *mut _);
171            Drain {
172                tail_len: len - end,
173                tail_start: end,
174                remaining_start: start,
175                remaining_len: end - start,
176                string,
177                _marker: PhantomData,
178            }
179        }
180    }
181}