|
12 | 12 | //!
|
13 | 13 | //! [`iter_unsigned`]: ParseUnsigned::iter_unsigned
|
14 | 14 | //! [`iter_signed`]: ParseSigned::iter_signed
|
15 |
| -use std::iter::{Filter, Map}; |
16 |
| -use std::str::{FromStr, Split}; |
17 |
| - |
18 |
| -/// Much shorter alias for the trait return type. |
19 |
| -type Wrapper<'a, T> = Map<Filter<Split<'a, fn(char) -> bool>, fn(&&str) -> bool>, fn(&str) -> T>; |
20 |
| - |
21 |
| -/// This convenience method does the same thing as `s.parse().unwrap()` but without the |
22 |
| -/// extra `<F as FromStr>::Err` type bound so that we can have shorter type signatures. |
23 |
| -pub fn from<T: FromStr>(s: &str) -> T { |
24 |
| - match s.parse() { |
25 |
| - Ok(t) => t, |
26 |
| - Err(_) => panic!("Unable to parse \"{s}\""), |
27 |
| - } |
28 |
| -} |
| 15 | +use std::marker::PhantomData; |
| 16 | +use std::ops::{Add, Neg, Shl}; |
| 17 | +use std::str::Bytes; |
| 18 | + |
| 19 | +/// Traits allow us to keep type safety, restricting the possiblities to only integer types. |
| 20 | +pub trait Common: Copy + From<u8> + Add<Output = Self> + Shl<u8, Output = Self> {} |
| 21 | +impl Common for u8 {} |
| 22 | +impl Common for u16 {} |
| 23 | +impl Common for u32 {} |
| 24 | +impl Common for u64 {} |
| 25 | +impl Common for usize {} |
| 26 | +impl Common for i16 {} |
| 27 | +impl Common for i32 {} |
| 28 | +impl Common for i64 {} |
29 | 29 |
|
30 |
| -/// This trait allows us to keep type safety, restricting the possiblities to only |
31 |
| -/// `u32`, `u64` and `usize`. |
32 |
| -pub trait Unsigned: FromStr {} |
| 30 | +pub trait Unsigned: Common {} |
33 | 31 | impl Unsigned for u8 {}
|
| 32 | +impl Unsigned for u16 {} |
34 | 33 | impl Unsigned for u32 {}
|
35 | 34 | impl Unsigned for u64 {}
|
36 | 35 | impl Unsigned for usize {}
|
37 | 36 |
|
38 |
| -/// Rust closures have an unique type that only the compiler knows and that us mere |
39 |
| -/// mortals are not allowed to ascertain. Fortunately we can coerce the type to a known |
40 |
| -/// function signature by using intermediate variables. |
41 |
| -pub trait ParseUnsigned { |
42 |
| - fn iter_unsigned<T: Unsigned>(&self) -> Wrapper<'_, T>; |
| 37 | +pub trait Signed: Common + Neg<Output = Self> {} |
| 38 | +impl Signed for i16 {} |
| 39 | +impl Signed for i32 {} |
| 40 | +impl Signed for i64 {} |
| 41 | + |
| 42 | +pub struct ParseUnsigned<'a, T> { |
| 43 | + bytes: Bytes<'a>, |
| 44 | + phantom: PhantomData<T>, |
| 45 | +} |
| 46 | + |
| 47 | +pub struct ParseSigned<'a, T> { |
| 48 | + bytes: Bytes<'a>, |
| 49 | + phantom: PhantomData<T>, |
| 50 | +} |
| 51 | + |
| 52 | +pub trait ParseOps { |
| 53 | + fn unsigned<T: Unsigned>(&self) -> T; |
| 54 | + fn signed<T: Signed>(&self) -> T; |
| 55 | + fn iter_unsigned<T: Unsigned>(&self) -> ParseUnsigned<'_, T>; |
| 56 | + fn iter_signed<T: Signed>(&self) -> ParseSigned<'_, T>; |
43 | 57 | }
|
44 | 58 |
|
45 |
| -impl ParseUnsigned for &str { |
46 |
| - fn iter_unsigned<T: Unsigned>(&self) -> Wrapper<'_, T> { |
47 |
| - let not_numeric: fn(char) -> bool = |c| !c.is_ascii_digit(); |
48 |
| - let not_empty: fn(&&str) -> bool = |s| !s.is_empty(); |
49 |
| - self.split(not_numeric).filter(not_empty).map(from) |
| 59 | +impl ParseOps for &str { |
| 60 | + fn unsigned<T: Unsigned>(&self) -> T { |
| 61 | + match try_unsigned(&mut self.bytes()) { |
| 62 | + Some(t) => t, |
| 63 | + None => panic!("Unable to parse \"{self}\""), |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + fn signed<T: Signed>(&self) -> T { |
| 68 | + match try_signed(&mut self.bytes()) { |
| 69 | + Some(t) => t, |
| 70 | + None => panic!("Unable to parse \"{self}\""), |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + fn iter_unsigned<T: Unsigned>(&self) -> ParseUnsigned<'_, T> { |
| 75 | + ParseUnsigned { bytes: self.bytes(), phantom: PhantomData } |
| 76 | + } |
| 77 | + |
| 78 | + fn iter_signed<T: Signed>(&self) -> ParseSigned<'_, T> { |
| 79 | + ParseSigned { bytes: self.bytes(), phantom: PhantomData } |
50 | 80 | }
|
51 | 81 | }
|
52 | 82 |
|
53 |
| -/// This trait allows us to keep type safety, restricting the possiblities to only |
54 |
| -/// `i32` and `i64`. |
55 |
| -pub trait Signed: FromStr {} |
56 |
| -impl Signed for i32 {} |
57 |
| -impl Signed for i64 {} |
| 83 | +impl<T: Unsigned> Iterator for ParseUnsigned<'_, T> { |
| 84 | + type Item = T; |
58 | 85 |
|
59 |
| -/// Essentially the same as `ParseUnsigned` but also considers the `-` character as part |
60 |
| -/// of a number. |
61 |
| -pub trait ParseSigned { |
62 |
| - fn iter_signed<T: Signed>(&self) -> Wrapper<'_, T>; |
| 86 | + fn size_hint(&self) -> (usize, Option<usize>) { |
| 87 | + let (lower, upper) = self.bytes.size_hint(); |
| 88 | + (lower / 3, upper.map(|u| u / 3)) |
| 89 | + } |
| 90 | + |
| 91 | + fn next(&mut self) -> Option<Self::Item> { |
| 92 | + try_unsigned(&mut self.bytes) |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +impl<T: Signed> Iterator for ParseSigned<'_, T> { |
| 97 | + type Item = T; |
| 98 | + |
| 99 | + fn size_hint(&self) -> (usize, Option<usize>) { |
| 100 | + let (lower, upper) = self.bytes.size_hint(); |
| 101 | + (lower / 3, upper.map(|u| u / 3)) |
| 102 | + } |
| 103 | + |
| 104 | + fn next(&mut self) -> Option<Self::Item> { |
| 105 | + try_signed(&mut self.bytes) |
| 106 | + } |
63 | 107 | }
|
64 | 108 |
|
65 |
| -impl ParseSigned for &str { |
66 |
| - fn iter_signed<T: Signed>(&self) -> Wrapper<'_, T> { |
67 |
| - let not_numeric: fn(char) -> bool = |c| !c.is_ascii_digit() && c != '-'; |
68 |
| - let not_empty: fn(&&str) -> bool = |s| !s.is_empty(); |
69 |
| - self.split(not_numeric).filter(not_empty).map(from) |
| 109 | +fn try_unsigned<T: Unsigned>(bytes: &mut Bytes) -> Option<T> { |
| 110 | + let mut n = loop { |
| 111 | + let b = bytes.next()?; |
| 112 | + let d = b.wrapping_sub(b'0'); |
| 113 | + |
| 114 | + if d < 10 { |
| 115 | + break T::from(d); |
| 116 | + } |
| 117 | + }; |
| 118 | + |
| 119 | + loop { |
| 120 | + let Some(b) = bytes.next() else { break Some(n) }; |
| 121 | + let d = b.wrapping_sub(b'0'); |
| 122 | + |
| 123 | + if d < 10 { |
| 124 | + n = (n << 3) + (n << 1) + T::from(d); |
| 125 | + } else { |
| 126 | + break Some(n); |
| 127 | + } |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +fn try_signed<T: Signed>(bytes: &mut Bytes) -> Option<T> { |
| 132 | + let (mut n, negative) = loop { |
| 133 | + let b = bytes.next()?; |
| 134 | + let d = b.wrapping_sub(b'0'); |
| 135 | + |
| 136 | + if d == 253 { |
| 137 | + break (T::from(0), true); |
| 138 | + } |
| 139 | + if d < 10 { |
| 140 | + break (T::from(d), false); |
| 141 | + } |
| 142 | + }; |
| 143 | + |
| 144 | + loop { |
| 145 | + let Some(b) = bytes.next() else { |
| 146 | + break Some(if negative { -n } else { n }); |
| 147 | + }; |
| 148 | + let d = b.wrapping_sub(b'0'); |
| 149 | + |
| 150 | + if d < 10 { |
| 151 | + n = (n << 3) + (n << 1) + T::from(d); |
| 152 | + } else { |
| 153 | + break Some(if negative { -n } else { n }); |
| 154 | + } |
70 | 155 | }
|
71 | 156 | }
|
0 commit comments