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}