Skip to main content

smol_bytes/
utf8_drain.rs

1use core::{fmt, iter::FusedIterator};
2
3use crate::utf8_edit::{RawUtf8Storage, scalar_width};
4
5/// A borrowing iterator over a UTF-8 range removed when the iterator drops.
6///
7/// The original range stays in place while this iterator is alive. Dropping it
8/// removes the entire selected range, including any characters that were not
9/// yielded. Forgetting it leaves the original value unchanged.
10///
11/// # Examples
12///
13/// ```
14/// use smol_bytes::Utf8Buffer;
15///
16/// let mut text = Utf8Buffer::try_from("aéz").unwrap();
17/// let removed: String = text.drain(1..3).collect();
18/// assert_eq!(removed, "é");
19/// assert_eq!(text.as_str(), "az");
20/// ```
21pub struct Utf8Drain<'a> {
22  storage: RawUtf8Storage<'a>,
23  start: usize,
24  end: usize,
25  original_len: usize,
26  front: usize,
27  back: usize,
28}
29
30impl<'a> Utf8Drain<'a> {
31  pub(crate) fn new(storage: RawUtf8Storage<'a>, start: usize, end: usize) -> Self {
32    let original_len = storage.len();
33    Self {
34      storage,
35      start,
36      end,
37      original_len,
38      front: start,
39      back: end,
40    }
41  }
42
43  /// Returns the not-yet-yielded portion of the selected range.
44  pub fn as_str(&self) -> &str {
45    core::str::from_utf8(&self.storage.as_slice()[self.front..self.back])
46      .expect("UTF-8 invariant violated")
47  }
48}
49
50impl fmt::Debug for Utf8Drain<'_> {
51  fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
52    formatter
53      .debug_tuple("Utf8Drain")
54      .field(&self.as_str())
55      .finish()
56  }
57}
58
59impl AsRef<str> for Utf8Drain<'_> {
60  fn as_ref(&self) -> &str {
61    self.as_str()
62  }
63}
64
65impl AsRef<[u8]> for Utf8Drain<'_> {
66  fn as_ref(&self) -> &[u8] {
67    self.as_str().as_bytes()
68  }
69}
70
71impl Iterator for Utf8Drain<'_> {
72  type Item = char;
73
74  fn next(&mut self) -> Option<Self::Item> {
75    if self.front == self.back {
76      return None;
77    }
78
79    let next = {
80      let bytes = self.storage.as_slice();
81      self.front + scalar_width(bytes[self.front])
82    };
83    let character = {
84      let bytes = self.storage.as_slice();
85      core::str::from_utf8(&bytes[self.front..next])
86        .expect("UTF-8 invariant violated")
87        .chars()
88        .next()
89        .expect("UTF-8 invariant violated")
90    };
91    self.front = next;
92    Some(character)
93  }
94
95  fn size_hint(&self) -> (usize, Option<usize>) {
96    let bytes = self.back - self.front;
97    let minimum = bytes / 4 + usize::from(!bytes.is_multiple_of(4));
98    (minimum, Some(bytes))
99  }
100}
101
102impl DoubleEndedIterator for Utf8Drain<'_> {
103  fn next_back(&mut self) -> Option<Self::Item> {
104    if self.front == self.back {
105      return None;
106    }
107
108    let start = {
109      let bytes = self.storage.as_slice();
110      let mut start = self.back - 1;
111      for _ in 0..3 {
112        if start == self.front || bytes[start] & 0b1100_0000 != 0b1000_0000 {
113          break;
114        }
115        start -= 1;
116      }
117      start
118    };
119    let character = {
120      let bytes = self.storage.as_slice();
121      core::str::from_utf8(&bytes[start..self.back])
122        .expect("UTF-8 invariant violated")
123        .chars()
124        .next()
125        .expect("UTF-8 invariant violated")
126    };
127    self.back = start;
128    Some(character)
129  }
130}
131
132impl FusedIterator for Utf8Drain<'_> {}
133
134impl Drop for Utf8Drain<'_> {
135  fn drop(&mut self) {
136    self
137      .storage
138      .copy_within(self.end..self.original_len, self.start);
139    self
140      .storage
141      .truncate(self.original_len - (self.end - self.start));
142  }
143}