|
3 | 3 | //! This module is "publicly exported" through the `FromStr` implementations |
4 | 4 | //! below. |
5 | 5 |
|
6 | | -use crate::convert::TryInto; |
| 6 | +use crate::convert::{TryFrom, TryInto}; |
7 | 7 | use crate::error::Error; |
8 | 8 | use crate::fmt; |
9 | 9 | use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; |
@@ -104,36 +104,66 @@ impl<'a> Parser<'a> { |
104 | 104 | // Read a number off the front of the input in the given radix, stopping |
105 | 105 | // at the first non-digit character or eof. Fails if the number has more |
106 | 106 | // digits than max_digits or if there is no number. |
107 | | - fn read_number<T: ReadNumberHelper>( |
| 107 | + // |
| 108 | + // INVARIANT: `max_digits` must be less than the number of digits that `u32` |
| 109 | + // can represent. |
| 110 | + fn read_number<T: ReadNumberHelper + TryFrom<u32>>( |
108 | 111 | &mut self, |
109 | 112 | radix: u32, |
110 | 113 | max_digits: Option<usize>, |
111 | 114 | allow_zero_prefix: bool, |
112 | 115 | ) -> Option<T> { |
113 | | - self.read_atomically(move |p| { |
114 | | - let mut result = T::ZERO; |
115 | | - let mut digit_count = 0; |
116 | | - let has_leading_zero = p.peek_char() == Some('0'); |
117 | | - |
118 | | - while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { |
119 | | - result = result.checked_mul(radix)?; |
120 | | - result = result.checked_add(digit)?; |
121 | | - digit_count += 1; |
122 | | - if let Some(max_digits) = max_digits { |
| 116 | + // If max_digits.is_some(), then we are parsing a `u8` or `u16` and |
| 117 | + // don't need to use checked arithmetic since it fits within a `u32`. |
| 118 | + if let Some(max_digits) = max_digits { |
| 119 | + // u32::MAX = 4_294_967_295u32, which is 10 digits long. |
| 120 | + // `max_digits` must be less than 10 to not overflow a `u32`. |
| 121 | + debug_assert!(max_digits < 10); |
| 122 | + |
| 123 | + self.read_atomically(move |p| { |
| 124 | + let mut result = 0_u32; |
| 125 | + let mut digit_count = 0; |
| 126 | + let has_leading_zero = p.peek_char() == Some('0'); |
| 127 | + |
| 128 | + while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { |
| 129 | + result *= radix; |
| 130 | + result += digit; |
| 131 | + digit_count += 1; |
| 132 | + |
123 | 133 | if digit_count > max_digits { |
124 | 134 | return None; |
125 | 135 | } |
126 | 136 | } |
127 | | - } |
128 | 137 |
|
129 | | - if digit_count == 0 { |
130 | | - None |
131 | | - } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { |
132 | | - None |
133 | | - } else { |
134 | | - Some(result) |
135 | | - } |
136 | | - }) |
| 138 | + if digit_count == 0 { |
| 139 | + None |
| 140 | + } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { |
| 141 | + None |
| 142 | + } else { |
| 143 | + result.try_into().ok() |
| 144 | + } |
| 145 | + }) |
| 146 | + } else { |
| 147 | + self.read_atomically(move |p| { |
| 148 | + let mut result = T::ZERO; |
| 149 | + let mut digit_count = 0; |
| 150 | + let has_leading_zero = p.peek_char() == Some('0'); |
| 151 | + |
| 152 | + while let Some(digit) = p.read_atomically(|p| p.read_char()?.to_digit(radix)) { |
| 153 | + result = result.checked_mul(radix)?; |
| 154 | + result = result.checked_add(digit)?; |
| 155 | + digit_count += 1; |
| 156 | + } |
| 157 | + |
| 158 | + if digit_count == 0 { |
| 159 | + None |
| 160 | + } else if !allow_zero_prefix && has_leading_zero && digit_count > 1 { |
| 161 | + None |
| 162 | + } else { |
| 163 | + Some(result) |
| 164 | + } |
| 165 | + }) |
| 166 | + } |
137 | 167 | } |
138 | 168 |
|
139 | 169 | /// Read an IPv4 address. |
|
0 commit comments