granit_parser/input/
buffered.rs1use crate::char_traits::is_breakz;
2use crate::error::ErrorKind;
3use crate::input::{BorrowedInput, Input};
4
5use arraydeque::ArrayDeque;
6
7const BUFFER_LEN: usize = 16;
18
19#[allow(clippy::module_name_repetitions)]
27pub struct BufferedInput<T: Iterator<Item = char>> {
28 input: T,
30 buffer: ArrayDeque<char, BUFFER_LEN>,
32 real_buffered: usize,
34 lookahead: usize,
36 source_exhausted: bool,
38}
39
40impl<T: Iterator<Item = char>> BufferedInput<T> {
41 #[must_use]
43 pub fn new(input: T) -> Self {
44 Self {
45 input,
46 buffer: ArrayDeque::default(),
47 real_buffered: 0,
48 lookahead: 0,
49 source_exhausted: false,
50 }
51 }
52
53 fn push_source_or_padding(&mut self) {
54 let c = if self.source_exhausted {
55 '\0'
56 } else if let Some(c) = self.input.next() {
57 self.real_buffered += 1;
58 c
59 } else {
60 self.source_exhausted = true;
61 '\0'
62 };
63 self.buffer.push_back(c).unwrap();
64 }
65
66 fn fill_lookahead(&mut self) {
67 while self.buffer.len() < self.lookahead {
68 self.push_source_or_padding();
69 }
70 }
71
72 fn pop_buffered(&mut self) -> Option<(char, bool)> {
73 let c = self.buffer.pop_front()?;
74 let is_real = self.real_buffered > 0;
75 if is_real {
76 self.real_buffered -= 1;
77 }
78 Some((c, is_real))
79 }
80
81 fn read_source_or_eof(&mut self) -> (char, bool) {
82 if self.source_exhausted {
83 ('\0', false)
84 } else if let Some(c) = self.input.next() {
85 (c, true)
86 } else {
87 self.source_exhausted = true;
88 ('\0', false)
89 }
90 }
91
92 fn raw_read_front(&mut self) -> (char, bool) {
93 let read = self
94 .pop_buffered()
95 .unwrap_or_else(|| self.read_source_or_eof());
96 self.fill_lookahead();
97 read
98 }
99
100 fn skip_one(&mut self) -> bool {
101 let skipped = match self.pop_buffered() {
102 Some((_, true)) => true,
103 Some((_, false)) => {
104 self.buffer.push_front('\0').unwrap();
105 false
106 }
107 None => self.read_source_or_eof().1,
108 };
109
110 if skipped {
111 self.fill_lookahead();
112 }
113 skipped
114 }
115}
116
117impl<T: Iterator<Item = char>> Input for BufferedInput<T> {
118 #[inline]
119 fn lookahead(&mut self, count: usize) {
120 self.lookahead = self.lookahead.max(count.min(BUFFER_LEN));
121 self.fill_lookahead();
122 }
123
124 #[inline]
125 fn buflen(&self) -> usize {
126 self.lookahead
127 }
128
129 #[inline]
130 fn bufmaxlen(&self) -> usize {
131 BUFFER_LEN
132 }
133
134 #[inline]
135 fn raw_read_ch(&mut self) -> char {
136 self.raw_read_front().0
137 }
138
139 #[inline]
140 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
141 if let Some(c) = self.buffer.front().copied() {
142 if is_breakz(c) {
143 None
144 } else {
145 Some(self.raw_read_front().0)
146 }
147 } else {
148 let (c, is_real) = self.read_source_or_eof();
149 if !is_real {
150 None
151 } else if is_breakz(c) {
152 self.buffer.push_back(c).unwrap();
153 self.real_buffered += 1;
154 None
155 } else {
156 self.fill_lookahead();
157 Some(c)
158 }
159 }
160 }
161
162 #[inline]
163 fn skip(&mut self) {
164 self.skip_one();
165 }
166
167 #[inline]
168 fn skip_n(&mut self, count: usize) {
169 for _ in 0..count {
170 if !self.skip_one() {
171 break;
172 }
173 }
174 }
175
176 #[inline]
177 fn peek(&self) -> char {
178 self.buffer.front().copied().unwrap_or('\0')
179 }
180
181 #[inline]
182 fn peek_nth(&self, n: usize) -> char {
183 self.buffer.get(n).copied().unwrap_or('\0')
184 }
185
186 #[inline]
187 fn next_is_z(&self) -> bool {
188 self.source_exhausted && self.real_buffered == 0
189 }
190}
191
192impl<T: Iterator<Item = char>> BorrowedInput<'static> for BufferedInput<T> {
195 #[inline]
196 fn slice_borrowed(&self, _start: usize, _end: usize) -> Option<&'static str> {
197 None
198 }
199}
200
201struct FallibleChars<T: Iterator<Item = Result<char, ErrorKind>>> {
203 input: T,
204 error: Option<ErrorKind>,
205 finished: bool,
206}
207
208impl<T: Iterator<Item = Result<char, ErrorKind>>> FallibleChars<T> {
209 fn new(input: T) -> Self {
210 Self {
211 input,
212 error: None,
213 finished: false,
214 }
215 }
216}
217
218impl<T: Iterator<Item = Result<char, ErrorKind>>> Iterator for FallibleChars<T> {
219 type Item = char;
220
221 fn next(&mut self) -> Option<Self::Item> {
222 if self.finished {
223 return None;
224 }
225
226 match self.input.next() {
227 Some(Ok(c)) => Some(c),
228 Some(Err(error)) => {
229 self.error = Some(error);
230 self.finished = true;
231 None
232 }
233 None => {
234 self.finished = true;
235 None
236 }
237 }
238 }
239}
240
241#[allow(clippy::module_name_repetitions)]
247pub struct FallibleBufferedInput<T: Iterator<Item = Result<char, ErrorKind>>> {
248 inner: BufferedInput<FallibleChars<T>>,
249}
250
251impl<T: Iterator<Item = Result<char, ErrorKind>>> FallibleBufferedInput<T> {
252 #[must_use]
254 pub fn new(input: T) -> Self {
255 Self {
256 inner: BufferedInput::new(FallibleChars::new(input)),
257 }
258 }
259}
260
261impl<T: Iterator<Item = Result<char, ErrorKind>>> Input for FallibleBufferedInput<T> {
262 #[inline]
263 fn lookahead(&mut self, count: usize) {
264 self.inner.lookahead(count);
265 }
266
267 #[inline]
268 fn buflen(&self) -> usize {
269 self.inner.buflen()
270 }
271
272 #[inline]
273 fn bufmaxlen(&self) -> usize {
274 self.inner.bufmaxlen()
275 }
276
277 #[inline]
278 fn raw_read_ch(&mut self) -> char {
279 self.inner.raw_read_ch()
280 }
281
282 #[inline]
283 fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
284 self.inner.raw_read_non_breakz_ch()
285 }
286
287 #[inline]
288 fn skip(&mut self) {
289 self.inner.skip();
290 }
291
292 #[inline]
293 fn skip_n(&mut self, count: usize) {
294 self.inner.skip_n(count);
295 }
296
297 #[inline]
298 fn peek(&self) -> char {
299 self.inner.peek()
300 }
301
302 #[inline]
303 fn peek_nth(&self, n: usize) -> char {
304 self.inner.peek_nth(n)
305 }
306
307 #[inline]
308 fn next_is_z(&self) -> bool {
309 self.inner.next_is_z()
310 }
311
312 #[inline]
313 fn take_source_error(&mut self) -> Option<ErrorKind> {
314 self.inner.input.error.take()
315 }
316}
317
318impl<T: Iterator<Item = Result<char, ErrorKind>>> BorrowedInput<'static>
320 for FallibleBufferedInput<T>
321{
322 #[inline]
323 fn slice_borrowed(&self, _start: usize, _end: usize) -> Option<&'static str> {
324 None
325 }
326}
327
328#[cfg(test)]
329mod tests {
330 use crate::input::str::StrInput;
331
332 use super::*;
333
334 #[test]
335 fn lookahead_larger_than_buffer_is_clamped() {
336 let mut input = BufferedInput::new("abc".chars());
337
338 input.lookahead(BUFFER_LEN + 8);
339
340 assert_eq!(input.buflen(), BUFFER_LEN);
341 assert_eq!(input.peek(), 'a');
342 assert_eq!(input.peek_nth(1), 'b');
343 assert_eq!(input.peek_nth(2), 'c');
344 assert_eq!(input.peek_nth(3), '\0');
345 }
346
347 #[test]
348 fn raw_reads_use_stream_front_and_report_eof() {
349 let mut input = BufferedInput::new("a".chars());
350
351 assert_eq!(input.raw_read_ch(), 'a');
352 assert_eq!(input.raw_read_ch(), '\0');
353
354 let mut input = BufferedInput::new("ab".chars());
355 input.lookahead(1);
356 assert_eq!(input.raw_read_ch(), 'a');
357 assert_eq!(input.peek(), 'b');
358 }
359
360 #[test]
361 fn raw_read_non_breakz_leaves_break_at_stream_front() {
362 let mut input = BufferedInput::new("a\n".chars());
363
364 assert_eq!(input.raw_read_non_breakz_ch(), Some('a'));
365 assert_eq!(input.raw_read_non_breakz_ch(), None);
366 assert_eq!(input.peek(), '\n');
367 input.lookahead(1);
368 assert_eq!(input.buflen(), 1);
369 assert_eq!(input.peek(), '\n');
370
371 let mut empty = BufferedInput::new("".chars());
372 assert_eq!(empty.raw_read_non_breakz_ch(), None);
373 }
374
375 #[test]
376 fn skip_n_consumes_stream_front_and_preserves_lookahead_window() {
377 let mut input = BufferedInput::new("abcdef".chars());
378
379 input.lookahead(5);
380 input.skip_n(2);
381
382 assert_eq!(input.buflen(), 5);
383 assert_eq!(input.peek(), 'c');
384 assert_eq!(input.peek_nth(3), 'f');
385 assert_eq!(input.peek_nth(4), '\0');
386 }
387
388 #[test]
389 fn skip_without_lookahead_consumes_like_str_input() {
390 let mut buffered = BufferedInput::new("ab".chars());
391 buffered.skip();
392 buffered.lookahead(1);
393
394 let mut str_input = StrInput::new("ab");
395 str_input.skip();
396 str_input.lookahead(1);
397
398 assert_eq!(buffered.peek(), str_input.peek());
399 }
400
401 #[test]
402 fn skip_n_saturates_at_eof_like_str_input() {
403 let mut buffered = BufferedInput::new("abc".chars());
404 buffered.lookahead(1);
405 buffered.skip_n(8);
406 buffered.lookahead(1);
407
408 let mut str_input = StrInput::new("abc");
409 str_input.lookahead(1);
410 str_input.skip_n(8);
411 str_input.lookahead(1);
412
413 assert_eq!(buffered.peek(), str_input.peek());
414 }
415
416 #[test]
417 fn buflen_matches_str_input_lookahead_window_after_consumption() {
418 let mut buffered = BufferedInput::new("ab".chars());
419 buffered.lookahead(2);
420 buffered.skip();
421 buffered.skip();
422
423 let mut str_input = StrInput::new("ab");
424 str_input.lookahead(2);
425 str_input.skip();
426 str_input.skip();
427
428 assert_eq!(buffered.buflen(), str_input.buflen());
429 assert_eq!(buffered.buf_is_empty(), str_input.buf_is_empty());
430 assert_eq!(buffered.peek(), str_input.peek());
431 }
432
433 #[test]
434 fn streaming_input_never_borrows_slices() {
435 let input = BufferedInput::new("abc".chars());
436
437 assert_eq!(BorrowedInput::slice_borrowed(&input, 0, 1), None);
438 }
439}