nanvm_lib/vm/
iterator_record.rs1use core::iter::FusedIterator;
2
3use crate::{
4 common::{index_iter::IndexIter, sized_index::SizedIndex},
5 vm::{
6 Any, Array, IVm, String, ToAny, ToString,
7 string::code_unit::{is_high_surrogate, is_low_surrogate},
8 },
9};
10
11pub enum IteratorRecord<A: IVm> {
37 Array(IndexIter<u32, Array<A>>),
39 String { string: String<A>, next: u32 },
42}
43
44impl<A: IVm> Iterator for IteratorRecord<A> {
45 type Item = Any<A>;
46
47 fn next(&mut self) -> Option<Any<A>> {
48 match self {
49 IteratorRecord::Array(elements) => elements.next(),
50 IteratorRecord::String { string, next } => {
51 let length = string.length();
52 if *next >= length {
53 return None;
54 }
55 let first = string[*next];
56 let pair = is_high_surrogate(first)
57 && *next + 1 < length
58 && is_low_surrogate(string[*next + 1]);
59 let code_point: String<A> = if pair {
60 [first, string[*next + 1]].to_string()
61 } else {
62 [first].to_string()
63 };
64 *next += if pair { 2 } else { 1 };
65 Some(code_point.to_any())
66 }
67 }
68 }
69
70 fn size_hint(&self) -> (usize, Option<usize>) {
76 match self {
77 IteratorRecord::Array(elements) => elements.size_hint(),
78 IteratorRecord::String { string, next } => {
79 let left = string.length().saturating_sub(*next) as usize;
80 (left.div_ceil(2), Some(left))
81 }
82 }
83 }
84}
85
86impl<A: IVm> FusedIterator for IteratorRecord<A> {}
87
88#[cfg(test)]
89mod tests {
90 use crate::{
91 naive::Naive,
92 vm::{Any, String, ToAny, ToArray, ToString},
93 };
94
95 type A = Naive;
96
97 fn n(x: f64) -> Any<A> {
98 x.to_any()
99 }
100
101 fn s(x: &str) -> Any<A> {
102 x.into()
103 }
104
105 fn units(u: &[u16]) -> Any<A> {
107 let s: String<A> = u.iter().copied().to_string();
108 s.to_any()
109 }
110
111 fn array(items: &[Any<A>]) -> Any<A> {
112 items.to_vec().to_array().to_any()
113 }
114
115 fn spread(v: Any<A>) -> Vec<Any<A>> {
117 v.get_iterator().unwrap().collect()
118 }
119
120 #[test]
121 fn array_elements_in_order() {
122 let items = [n(1.0), s("b"), array(&[])];
123 assert_eq!(spread(array(&items)), items);
124 assert!(spread(array(&[])).is_empty());
125 }
126
127 #[test]
129 fn string_code_points() {
130 assert_eq!(spread(s("ab")), [s("a"), s("b")]);
131 assert_eq!(spread(s("๐")), [s("๐")]);
132 assert_eq!(spread(s("a๐b")), [s("a"), s("๐"), s("b")]);
133 assert!(spread(s("")).is_empty());
134 }
135
136 #[test]
139 fn lone_surrogates() {
140 assert_eq!(
141 spread(units(&[0xDE00, 0xD83D])),
142 [units(&[0xDE00]), units(&[0xD83D])]
143 );
144 assert_eq!(spread(units(&[0xD83D, 0x61])), [units(&[0xD83D]), s("a")]);
145 }
146
147 #[test]
148 fn size_hints() {
149 let mut a = array(&[n(1.0), n(2.0)]).get_iterator().unwrap();
150 assert_eq!(a.size_hint(), (2, Some(2)));
151 a.next();
152 assert_eq!(a.size_hint(), (1, Some(1)));
153 let mut t = s("a๐").get_iterator().unwrap();
154 assert_eq!(t.size_hint(), (2, Some(3)));
155 t.next();
156 assert_eq!(t.size_hint(), (1, Some(2)));
157 t.next();
158 assert_eq!(t.size_hint(), (0, Some(0)));
159 }
160
161 #[test]
163 fn fused() {
164 for v in [s("a"), array(&[n(1.0)])] {
165 let mut i = v.get_iterator().unwrap();
166 assert!(i.next().is_some());
167 assert!(i.next().is_none());
168 assert!(i.next().is_none());
169 }
170 }
171}