|
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