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