Skip to main content

scanner_rust/
scanner_u8_slice_ascii.rs

1use std::{
2    char::REPLACEMENT_CHARACTER,
3    str::{FromStr, from_utf8_unchecked},
4};
5
6use crate::{ScannerError, whitespaces::*};
7
8/// A simple text scanner which can in-memory-ly parse primitive types and strings using ASCII from a byte slice.
9#[derive(Debug)]
10pub struct ScannerU8SliceAscii<'a> {
11    data:        &'a [u8],
12    data_length: usize,
13    position:    usize,
14}
15
16impl<'a> ScannerU8SliceAscii<'a> {
17    /// Create a scanner from in-memory bytes.
18    ///
19    /// ```rust
20    /// use std::io;
21    ///
22    /// use scanner_rust::ScannerU8SliceAscii;
23    ///
24    /// let mut sc = ScannerU8SliceAscii::new(b"123 456");
25    /// ```
26    #[inline]
27    pub fn new<D: ?Sized + AsRef<[u8]>>(data: &D) -> ScannerU8SliceAscii<'_> {
28        let data = data.as_ref();
29
30        ScannerU8SliceAscii {
31            data,
32            data_length: data.len(),
33            position: 0,
34        }
35    }
36}
37
38impl<'a> ScannerU8SliceAscii<'a> {
39    /// Read the next char. If the data is not a correct char, it will return a `Ok(Some(REPLACEMENT_CHARACTER))` which is �. If there is nothing to read, it will return `Ok(None)`.
40    ///
41    /// ```rust
42    /// use scanner_rust::ScannerU8SliceAscii;
43    ///
44    /// let mut sc = ScannerU8SliceAscii::new("5 c ab".as_bytes());
45    ///
46    /// assert_eq!(Some('5'), sc.next_char().unwrap());
47    /// assert_eq!(Some(' '), sc.next_char().unwrap());
48    /// assert_eq!(Some('c'), sc.next_char().unwrap());
49    /// assert_eq!(Some(' '), sc.next_char().unwrap());
50    /// assert_eq!(Some('a'), sc.next_char().unwrap());
51    /// assert_eq!(Some('b'), sc.next_char().unwrap());
52    /// assert_eq!(None, sc.next_char().unwrap());
53    /// ```
54    pub fn next_char(&mut self) -> Result<Option<char>, ScannerError> {
55        if self.position == self.data_length {
56            return Ok(None);
57        }
58
59        let e = self.data[self.position];
60
61        self.position += 1;
62
63        if e >= 128 { Ok(Some(REPLACEMENT_CHARACTER)) } else { Ok(Some(e as char)) }
64    }
65
66    /// Read the next line but not include the trailing line character (or line characters like `CrLf`(`\r\n`)). If there is nothing to read, it will return `Ok(None)`.
67    ///
68    /// ```rust
69    /// use scanner_rust::ScannerU8SliceAscii;
70    ///
71    /// let mut sc = ScannerU8SliceAscii::new("123 456\r\n789 \n\n ab ".as_bytes());
72    ///
73    /// assert_eq!(Some("123 456".as_bytes()), sc.next_line().unwrap());
74    /// assert_eq!(Some("789 ".as_bytes()), sc.next_line().unwrap());
75    /// assert_eq!(Some("".as_bytes()), sc.next_line().unwrap());
76    /// assert_eq!(Some(" ab ".as_bytes()), sc.next_line().unwrap());
77    /// ```
78    pub fn next_line(&mut self) -> Result<Option<&'a [u8]>, ScannerError> {
79        if self.position == self.data_length {
80            return Ok(None);
81        }
82
83        let mut p = self.position;
84
85        loop {
86            let e = self.data[p];
87
88            match e {
89                b'\n' => {
90                    let data = &self.data[self.position..p];
91
92                    if p + 1 < self.data_length && self.data[p + 1] == b'\r' {
93                        self.position = p + 2;
94                    } else {
95                        self.position = p + 1;
96                    }
97
98                    return Ok(Some(data));
99                },
100                b'\r' => {
101                    let data = &self.data[self.position..p];
102
103                    if p + 1 < self.data_length && self.data[p + 1] == b'\n' {
104                        self.position = p + 2;
105                    } else {
106                        self.position = p + 1;
107                    }
108
109                    return Ok(Some(data));
110                },
111                _ => (),
112            }
113
114            p += 1;
115
116            if p == self.data_length {
117                break;
118            }
119        }
120
121        let data = &self.data[self.position..p];
122
123        self.position = p;
124
125        Ok(Some(data))
126    }
127}
128
129impl<'a> ScannerU8SliceAscii<'a> {
130    /// Skip the next whitespaces (`javaWhitespace`). If there is nothing to read, it will return `Ok(false)`.
131    ///
132    /// ```rust
133    /// use scanner_rust::ScannerU8SliceAscii;
134    ///
135    /// let mut sc = ScannerU8SliceAscii::new("1 2   c".as_bytes());
136    ///
137    /// assert_eq!(Some('1'), sc.next_char().unwrap());
138    /// assert_eq!(Some(' '), sc.next_char().unwrap());
139    /// assert_eq!(Some('2'), sc.next_char().unwrap());
140    /// assert_eq!(true, sc.skip_whitespaces().unwrap());
141    /// assert_eq!(Some('c'), sc.next_char().unwrap());
142    /// assert_eq!(false, sc.skip_whitespaces().unwrap());
143    /// ```
144    pub fn skip_whitespaces(&mut self) -> Result<bool, ScannerError> {
145        if self.position == self.data_length {
146            return Ok(false);
147        }
148
149        loop {
150            if !is_whitespace_1(self.data[self.position]) {
151                break;
152            }
153
154            self.position += 1;
155
156            if self.position == self.data_length {
157                break;
158            }
159        }
160
161        Ok(true)
162    }
163
164    /// Read the next token separated by whitespaces. If there is nothing to read, it will return `Ok(None)`.
165    ///
166    /// ```rust
167    /// use scanner_rust::ScannerU8SliceAscii;
168    ///
169    /// let mut sc = ScannerU8SliceAscii::new("123 456\r\n789 \n\n ab ".as_bytes());
170    ///
171    /// assert_eq!(Some("123".as_bytes()), sc.next().unwrap());
172    /// assert_eq!(Some("456".as_bytes()), sc.next().unwrap());
173    /// assert_eq!(Some("789".as_bytes()), sc.next().unwrap());
174    /// assert_eq!(Some("ab".as_bytes()), sc.next().unwrap());
175    /// assert_eq!(None, sc.next().unwrap());
176    /// ```
177    #[allow(clippy::should_implement_trait)]
178    pub fn next(&mut self) -> Result<Option<&'a [u8]>, ScannerError> {
179        if !self.skip_whitespaces()? {
180            return Ok(None);
181        }
182
183        if self.position == self.data_length {
184            return Ok(None);
185        }
186
187        let mut p = self.position;
188
189        loop {
190            if is_whitespace_1(self.data[p]) {
191                let data = &self.data[self.position..p];
192
193                self.position = p;
194
195                return Ok(Some(data));
196            }
197
198            p += 1;
199
200            if p == self.data_length {
201                break;
202            }
203        }
204
205        let data = &self.data[self.position..p];
206
207        self.position = p;
208
209        Ok(Some(data))
210    }
211}
212
213impl<'a> ScannerU8SliceAscii<'a> {
214    /// Read the next bytes. If there is nothing to read, it will return `Ok(None)`.
215    ///
216    /// ```rust
217    /// use scanner_rust::ScannerU8SliceAscii;
218    ///
219    /// let mut sc = ScannerU8SliceAscii::new("123 456\r\n789 \n\n ab ".as_bytes());
220    ///
221    /// assert_eq!(Some("123".as_bytes()), sc.next_bytes(3).unwrap());
222    /// assert_eq!(Some(" 456".as_bytes()), sc.next_bytes(4).unwrap());
223    /// assert_eq!(Some("\r\n789 ".as_bytes()), sc.next_bytes(6).unwrap());
224    /// assert_eq!(Some("ab".as_bytes()), sc.next().unwrap());
225    /// assert_eq!(Some(" ".as_bytes()), sc.next_bytes(2).unwrap());
226    /// assert_eq!(None, sc.next_bytes(2).unwrap());
227    /// ```
228    pub fn next_bytes(
229        &mut self,
230        max_number_of_bytes: usize,
231    ) -> Result<Option<&'a [u8]>, ScannerError> {
232        if self.position == self.data_length {
233            return Ok(None);
234        }
235
236        let dropping_bytes = max_number_of_bytes.min(self.data_length - self.position);
237
238        let data = &self.data[self.position..(self.position + dropping_bytes)];
239
240        self.position += dropping_bytes;
241
242        Ok(Some(data))
243    }
244
245    /// Drop the next N bytes. If there is nothing to read, it will return `Ok(None)`. If there is something to read, it will return `Ok(Some(i))`. The `i` is the length of the actually dropped bytes.
246    ///
247    /// ```rust
248    /// use scanner_rust::ScannerU8SliceAscii;
249    ///
250    /// let mut sc = ScannerU8SliceAscii::new("123 456\r\n789 \n\n ab ".as_bytes());
251    ///
252    /// assert_eq!(Some(7), sc.drop_next_bytes(7).unwrap());
253    /// assert_eq!(Some("".as_bytes()), sc.next_line().unwrap());
254    /// assert_eq!(Some("789 ".as_bytes()), sc.next_line().unwrap());
255    /// assert_eq!(Some(1), sc.drop_next_bytes(1).unwrap());
256    /// assert_eq!(Some(" ab ".as_bytes()), sc.next_line().unwrap());
257    /// assert_eq!(None, sc.drop_next_bytes(1).unwrap());
258    /// ```
259    pub fn drop_next_bytes(
260        &mut self,
261        max_number_of_bytes: usize,
262    ) -> Result<Option<usize>, ScannerError> {
263        if self.position == self.data_length {
264            return Ok(None);
265        }
266
267        let dropping_bytes = max_number_of_bytes.min(self.data_length - self.position);
268
269        self.position += dropping_bytes;
270
271        Ok(Some(dropping_bytes))
272    }
273}
274
275impl<'a> ScannerU8SliceAscii<'a> {
276    /// Read the next data until it reaches a specific boundary. If there is nothing to read, it will return `Ok(None)`.
277    ///
278    /// ```rust
279    /// use scanner_rust::ScannerU8SliceAscii;
280    ///
281    /// let mut sc = ScannerU8SliceAscii::new("123 456\r\n789 \n\n ab ".as_bytes());
282    ///
283    /// assert_eq!(Some("123".as_bytes()), sc.next_until(" ").unwrap());
284    /// assert_eq!(Some("456\r".as_bytes()), sc.next_until("\n").unwrap());
285    /// assert_eq!(Some("78".as_bytes()), sc.next_until("9 ").unwrap());
286    /// assert_eq!(Some("\n\n ab ".as_bytes()), sc.next_until("kk").unwrap());
287    /// assert_eq!(None, sc.next().unwrap());
288    /// ```
289    pub fn next_until<D: ?Sized + AsRef<[u8]>>(
290        &mut self,
291        boundary: &D,
292    ) -> Result<Option<&'a [u8]>, ScannerError> {
293        if self.position == self.data_length {
294            return Ok(None);
295        }
296
297        let boundary = boundary.as_ref();
298        let boundary_length = boundary.len();
299
300        if boundary_length == 0 || boundary_length > self.data_length - self.position {
301            let data = &self.data[self.position..];
302
303            self.position = self.data_length;
304
305            return Ok(Some(data));
306        }
307
308        for i in self.position..=(self.data_length - boundary_length) {
309            let e = i + boundary_length;
310
311            if &self.data[i..e] == boundary {
312                let data = &self.data[self.position..i];
313
314                self.position = e;
315
316                return Ok(Some(data));
317            }
318        }
319
320        let data = &self.data[self.position..];
321
322        self.position = self.data_length;
323
324        Ok(Some(data))
325    }
326}
327
328impl<'a> ScannerU8SliceAscii<'a> {
329    #[inline]
330    fn next_parse<T: FromStr>(&mut self) -> Result<Option<T>, ScannerError>
331    where
332        ScannerError: From<<T as FromStr>::Err>, {
333        let result = self.next()?;
334
335        match result {
336            // SAFETY: for malformed input `s` may not be valid UTF-8 (technically UB to treat as a `&str`), but it is only fed to a primitive `FromStr` that reads it as bytes, so an invalid token merely fails to parse; validation is skipped for speed.
337            Some(s) => Ok(Some(unsafe { from_utf8_unchecked(s) }.parse()?)),
338            None => Ok(None),
339        }
340    }
341}
342
343impl<'a> ScannerU8SliceAscii<'a> {
344    #[inline]
345    fn next_until_parse<T: FromStr, D: ?Sized + AsRef<[u8]>>(
346        &mut self,
347        boundary: &D,
348    ) -> Result<Option<T>, ScannerError>
349    where
350        ScannerError: From<<T as FromStr>::Err>, {
351        let result = self.next_until(boundary)?;
352
353        match result {
354            // SAFETY: for malformed input `s` may not be valid UTF-8 (technically UB to treat as a `&str`), but it is only fed to a primitive `FromStr` that reads it as bytes, so an invalid token merely fails to parse; validation is skipped for speed.
355            Some(s) => Ok(Some(unsafe { from_utf8_unchecked(s) }.parse()?)),
356            None => Ok(None),
357        }
358    }
359}
360
361macro_rules! scanner_u8_slice_ascii_number_methods {
362    ($(($t:ty, $next:ident, $next_until:ident, $sample:literal, $v1:literal, $v2:literal)),+ $(,)?) => {
363        impl<'a> ScannerU8SliceAscii<'a> {
364            $(
365                #[doc = concat!(
366                    "Read the next token separated by whitespaces and parse it to a `", stringify!($t), "` value. If there is nothing to read, it will return `Ok(None)`.\n\n```rust\nuse scanner_rust::ScannerU8SliceAscii;\n\nlet mut sc = ScannerU8SliceAscii::new(", stringify!($sample), ".as_bytes());\n\nassert_eq!(Some(", stringify!($v1), "), sc.", stringify!($next), "().unwrap());\nassert_eq!(Some(", stringify!($v2), "), sc.", stringify!($next), "().unwrap());\n```"
367                )]
368                #[inline]
369                pub fn $next(&mut self) -> Result<Option<$t>, ScannerError> {
370                    self.next_parse()
371                }
372            )+
373        }
374
375        impl<'a> ScannerU8SliceAscii<'a> {
376            $(
377                #[doc = concat!(
378                    "Read the next text until it reaches a specific boundary and parse it to a `", stringify!($t), "` value. If there is nothing to read, it will return `Ok(None)`.\n\n```rust\nuse scanner_rust::ScannerU8SliceAscii;\n\nlet mut sc = ScannerU8SliceAscii::new(", stringify!($sample), ".as_bytes());\n\nassert_eq!(Some(", stringify!($v1), "), sc.", stringify!($next_until), "(\" \").unwrap());\nassert_eq!(Some(", stringify!($v2), "), sc.", stringify!($next_until), "(\" \").unwrap());\n```"
379                )]
380                #[inline]
381                pub fn $next_until<D: ?Sized + AsRef<[u8]>>(
382                    &mut self,
383                    boundary: &D,
384                ) -> Result<Option<$t>, ScannerError> {
385                    self.next_until_parse(boundary)
386                }
387            )+
388        }
389    };
390}
391
392scanner_u8_slice_ascii_number_methods! {
393    (u8, next_u8, next_u8_until, "1 2", 1, 2),
394    (u16, next_u16, next_u16_until, "1 2", 1, 2),
395    (u32, next_u32, next_u32_until, "1 2", 1, 2),
396    (u64, next_u64, next_u64_until, "1 2", 1, 2),
397    (u128, next_u128, next_u128_until, "1 2", 1, 2),
398    (usize, next_usize, next_usize_until, "1 2", 1, 2),
399    (i8, next_i8, next_i8_until, "1 2", 1, 2),
400    (i16, next_i16, next_i16_until, "1 2", 1, 2),
401    (i32, next_i32, next_i32_until, "1 2", 1, 2),
402    (i64, next_i64, next_i64_until, "1 2", 1, 2),
403    (i128, next_i128, next_i128_until, "1 2", 1, 2),
404    (isize, next_isize, next_isize_until, "1 2", 1, 2),
405    (f32, next_f32, next_f32_until, "1 2.5", 1.0, 2.5),
406    (f64, next_f64, next_f64_until, "1 2.5", 1.0, 2.5),
407}
408
409impl<'a> ScannerU8SliceAscii<'a> {
410    /// Drop the next line but not include the trailing line character (or line characters like `CrLf`(`\r\n`)). If there is nothing to read, it will return `Ok(None)`. If there is something to read, it will return `Ok(Some(i))`. The `i` is the length (in bytes) of the dropped line.
411    ///
412    /// ```rust
413    /// use scanner_rust::ScannerU8SliceAscii;
414    ///
415    /// let mut sc = ScannerU8SliceAscii::new("123 456\r\n789 \n\n ab ".as_bytes());
416    ///
417    /// assert_eq!(Some(7), sc.drop_next_line().unwrap());
418    /// assert_eq!(Some("789 ".as_bytes()), sc.next_line().unwrap());
419    /// assert_eq!(Some(0), sc.drop_next_line().unwrap());
420    /// assert_eq!(Some(" ab ".as_bytes()), sc.next_line().unwrap());
421    /// assert_eq!(None, sc.drop_next_line().unwrap());
422    /// ```
423    #[inline]
424    pub fn drop_next_line(&mut self) -> Result<Option<usize>, ScannerError> {
425        Ok(self.next_line()?.map(<[u8]>::len))
426    }
427
428    /// Drop the next token separated by whitespaces. If there is nothing to read, it will return `Ok(None)`. If there is something to read, it will return `Ok(Some(i))`. The `i` is the length (in bytes) of the dropped token.
429    ///
430    /// ```rust
431    /// use scanner_rust::ScannerU8SliceAscii;
432    ///
433    /// let mut sc = ScannerU8SliceAscii::new("123 456\r\n789 \n\n ab ".as_bytes());
434    ///
435    /// assert_eq!(Some(3), sc.drop_next().unwrap());
436    /// assert_eq!(Some("456".as_bytes()), sc.next().unwrap());
437    /// assert_eq!(Some(3), sc.drop_next().unwrap());
438    /// assert_eq!(Some("ab".as_bytes()), sc.next().unwrap());
439    /// assert_eq!(None, sc.drop_next().unwrap());
440    /// ```
441    #[inline]
442    pub fn drop_next(&mut self) -> Result<Option<usize>, ScannerError> {
443        Ok(self.next()?.map(<[u8]>::len))
444    }
445
446    /// Drop the next data until it reaches a specific boundary. If there is nothing to read, it will return `Ok(None)`. If there is something to read, it will return `Ok(Some(i))`. The `i` is the length (in bytes) of the dropped data, excluding the boundary.
447    ///
448    /// ```rust
449    /// use scanner_rust::ScannerU8SliceAscii;
450    ///
451    /// let mut sc = ScannerU8SliceAscii::new("123 456\r\n789 \n\n ab ".as_bytes());
452    ///
453    /// assert_eq!(Some(7), sc.drop_next_until("\r\n").unwrap());
454    /// assert_eq!(Some("789 ".as_bytes()), sc.next_line().unwrap());
455    /// assert_eq!(Some(0), sc.drop_next_until("\n").unwrap());
456    /// assert_eq!(Some(" ab ".as_bytes()), sc.next_line().unwrap());
457    /// assert_eq!(None, sc.drop_next_until("").unwrap());
458    /// ```
459    #[inline]
460    pub fn drop_next_until<D: ?Sized + AsRef<[u8]>>(
461        &mut self,
462        boundary: &D,
463    ) -> Result<Option<usize>, ScannerError> {
464        Ok(self.next_until(boundary)?.map(<[u8]>::len))
465    }
466}
467
468impl<'a> Iterator for ScannerU8SliceAscii<'a> {
469    type Item = &'a [u8];
470
471    #[inline]
472    fn next(&mut self) -> Option<Self::Item> {
473        self.next().unwrap_or(None)
474    }
475}