Skip to main content

aoc/util/
parse.rs

1//! Extracts and parses signed and unsigned integers from surrounding text and whitespace.
2//!
3//! A common pattern in Advent of Code is to parse and return `123`, `456` and `789` from input
4//! resembling the following form:
5//!
6//! ```none
7//! Lorem ipsum 123 dolor 456 sit 789 amet
8//! ```
9//!
10//! This module provides two [`&str`] extension methods [`iter_signed`] and [`iter_unsigned`]. The
11//! reason for the separate methods is that some Advent of Code inputs contain the `-` character
12//! as a delimiter and this would cause numbers to be incorrectly parsed as negative.
13//!
14//! [`iter_unsigned`]: ParseOps::iter_unsigned
15//! [`iter_signed`]: ParseOps::iter_signed
16use std::marker::PhantomData;
17
18use crate::util::integer::*;
19
20const MINUS: u8 = b'-'.wrapping_sub(b'0');
21
22pub trait ParseByte {
23    fn to_decimal<T: Integer>(self) -> T;
24}
25
26impl ParseByte for u8 {
27    #[inline]
28    fn to_decimal<T: Integer>(self) -> T {
29        T::from(self.wrapping_sub(b'0'))
30    }
31}
32
33pub trait ParseOps {
34    fn unsigned<T: Unsigned>(&self) -> T;
35    fn signed<T: Signed>(&self) -> T;
36    fn iter_unsigned<T: Unsigned>(&self) -> impl Iterator<Item = T>;
37    fn iter_signed<T: Signed>(&self) -> impl Iterator<Item = T>;
38}
39
40impl<S: AsRef<str> + ?Sized> ParseOps for S {
41    #[inline]
42    fn unsigned<T: Unsigned>(&self) -> T {
43        let mut bytes = self.as_ref().bytes();
44        try_unsigned(&mut bytes).unwrap()
45    }
46
47    #[inline]
48    fn signed<T: Signed>(&self) -> T {
49        let mut bytes = self.as_ref().bytes();
50        try_signed(&mut bytes).unwrap()
51    }
52
53    #[inline]
54    fn iter_unsigned<T: Unsigned>(&self) -> impl Iterator<Item = T> {
55        let bytes = self.as_ref().bytes();
56        ParseUnsigned { bytes, phantom: PhantomData }
57    }
58
59    #[inline]
60    fn iter_signed<T: Signed>(&self) -> impl Iterator<Item = T> {
61        let bytes = self.as_ref().bytes();
62        ParseSigned { bytes, phantom: PhantomData }
63    }
64}
65
66struct ParseUnsigned<I, T> {
67    bytes: I,
68    phantom: PhantomData<T>,
69}
70
71impl<I: Iterator<Item = u8>, T: Unsigned> Iterator for ParseUnsigned<I, T> {
72    type Item = T;
73
74    #[inline]
75    fn size_hint(&self) -> (usize, Option<usize>) {
76        let (lower, upper) = self.bytes.size_hint();
77        (lower / 3, upper.map(|u| u / 3))
78    }
79
80    #[inline]
81    fn next(&mut self) -> Option<Self::Item> {
82        try_unsigned(&mut self.bytes)
83    }
84}
85
86struct ParseSigned<I, T> {
87    bytes: I,
88    phantom: PhantomData<T>,
89}
90
91impl<I: Iterator<Item = u8>, T: Signed> Iterator for ParseSigned<I, T> {
92    type Item = T;
93
94    #[inline]
95    fn size_hint(&self) -> (usize, Option<usize>) {
96        let (lower, upper) = self.bytes.size_hint();
97        (lower / 3, upper.map(|u| u / 3))
98    }
99
100    #[inline]
101    fn next(&mut self) -> Option<Self::Item> {
102        try_signed(&mut self.bytes)
103    }
104}
105
106fn try_unsigned<T: Unsigned>(bytes: &mut impl Iterator<Item = u8>) -> Option<T> {
107    let mut n = loop {
108        let digit = bytes.next()?.to_decimal();
109        if digit < 10 {
110            break T::from(digit);
111        }
112    };
113
114    for byte in bytes {
115        let digit = byte.to_decimal();
116        if digit >= 10 {
117            break;
118        }
119        n = T::TEN * n + T::from(digit);
120    }
121
122    Some(n)
123}
124
125fn try_signed<T: Signed>(bytes: &mut impl Iterator<Item = u8>) -> Option<T> {
126    let (mut n, negative) = loop {
127        let digit = bytes.next()?.to_decimal();
128        if digit == MINUS {
129            break (T::ZERO, true);
130        }
131        if digit < 10 {
132            break (T::from(digit), false);
133        }
134    };
135
136    for byte in bytes {
137        let digit = byte.to_decimal();
138        if digit >= 10 {
139            break;
140        }
141        n = T::TEN * n + T::from(digit);
142    }
143
144    Some(if negative { -n } else { n })
145}