1use std::error::{Error};
2use std::fmt;
3
4use base::{FromAscii};
5
6#[derive(Debug, Clone, PartialEq)]
8pub struct ParseIntError {
9 kind: IntErrorKind
10}
11
12#[derive(Debug, Clone, PartialEq)]
13enum IntErrorKind {
14 Empty,
15 InvalidDigit(u8),
16 Overflow,
17 Underflow,
18 InvalidRadix(u8),
19}
20
21impl IntErrorKind {
22 fn description(&self) -> &str {
23 match self {
24 &IntErrorKind::Empty => "cannot parse integer from empty string",
25 &IntErrorKind::InvalidDigit(_) => "invalid digit found in string",
26 &IntErrorKind::Overflow => "number too large to fit in target type",
27 &IntErrorKind::Underflow => "number too small to fit in target type",
28 &IntErrorKind::InvalidRadix(_) => "radix should be in 2..36 range",
29 }
30 }
31}
32
33impl fmt::Display for ParseIntError {
34 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35 self.kind.description().fmt(f)
36 }
37}
38
39impl Error for ParseIntError {
40 fn description(&self) -> &str {
41 self.kind.description()
42 }
43}
44
45#[inline]
46pub fn dec_to_digit(c: u8, radix: u8) -> Option<u8> {
47 let val = match c {
48 b'0' ... b'9' => c - b'0',
49 b'a' ... b'z' => c - b'a' + 10,
50 b'A' ... b'Z' => c - b'A' + 10,
51 _ => return None,
52 };
53 if val < radix {
54 Some(val)
55 } else {
56 None
57 }
58}
59
60trait FromAsciiHelper: {
61 fn signed() -> bool;
62}
63
64pub trait FromAsciiRadix: Sized {
69 type Err;
71
72 fn from_ascii_radix(s: &[u8], radix: u8) -> Result<Self, Self::Err>;
91}
92
93macro_rules! implement {
94 ($t:ty, $signed: expr) => {
95 impl FromAsciiHelper for $t {
96 #[inline]
97 fn signed() -> bool { $signed }
98 }
99
100 impl FromAsciiRadix for $t {
101 type Err = ParseIntError;
102
103 #[inline]
104 fn from_ascii_radix(src: &[u8], radix: u8) -> Result<Self, Self::Err> {
105 if radix < 2 || radix > 36 {
106 return Err(ParseIntError { kind: IntErrorKind::InvalidRadix(radix) })
107 }
108
109 let (is_positive, digits) = match src.get(0) {
110 Some(&b'+') => (true, &src[1..]),
111 Some(&b'-') if <$t>::signed() => (false, &src[1..]),
112 Some(_) => (true, src),
113 None => return Err(ParseIntError { kind: IntErrorKind::Empty }),
114 };
115
116 if digits.is_empty() {
117 return Err(ParseIntError { kind: IntErrorKind::Empty });
118 }
119
120 let mut result: $t = 0;
121
122 if is_positive {
123 for &c in digits {
124 let x = match dec_to_digit(c, radix) {
125 Some(x) => x as $t,
126 None => return Err(ParseIntError { kind: IntErrorKind::InvalidDigit(c) } ),
127 };
128 result = match result.checked_mul(radix as $t) {
129 Some(result) => result,
130 None => return Err(ParseIntError { kind: IntErrorKind::Overflow }),
131 };
132 result = match result.checked_add(x) {
133 Some(result) => result,
134 None => return Err(ParseIntError { kind: IntErrorKind::Overflow }),
135 };
136 }
137 } else {
138 for &c in digits {
139 let x = match dec_to_digit(c, radix) {
140 Some(x) => x as $t,
141 None => return Err(ParseIntError { kind: IntErrorKind::InvalidDigit(c) }),
142 };
143 result = match result.checked_mul(radix as $t) {
144 Some(result) => result,
145 None => return Err(ParseIntError { kind: IntErrorKind::Underflow }),
146 };
147 result = match result.checked_sub(x) {
148 Some(result) => result,
149 None => return Err(ParseIntError { kind: IntErrorKind::Underflow }),
150 };
151 }
152 }
153 Ok(result)
154 }
155 }
156
157 impl FromAscii for $t {
158 type Err = ParseIntError;
159
160 #[inline]
161 fn from_ascii(src: &[u8]) -> Result<Self, Self::Err> {
162 <$t>::from_ascii_radix(src, 10)
163 }
164 }
165 }
166}
167
168implement!(i8, true);
169implement!(i16, true);
170implement!(i32, true);
171implement!(i64, true);
172implement!(isize, true);
173implement!(u8, false);
174implement!(u16, false);
175implement!(u32, false);
176implement!(u64, false);
177implement!(usize, false);
178
179#[cfg(test)]
180mod tests {
181 use super::super::base::FromAscii;
182 use super::FromAsciiRadix;
183
184 #[test]
185 fn test_from_ascii() {
186 assert_eq!(i8::from_ascii(b"10"), Ok(10));
187 assert_eq!(u8::from_ascii(b"10"), Ok(10));
188 assert_eq!(i64::from_ascii(b"10"), Ok(10));
189 assert_eq!(u64::from_ascii(b"10"), Ok(10));
190
191 assert_eq!(i8::from_ascii(b"-10"), Ok(-10));
192 assert_eq!(u8::from_ascii(b"-10").is_err(), true);
193 assert_eq!(i64::from_ascii(b"-10"), Ok(-10));
194 assert_eq!(u64::from_ascii(b"-10").is_err(), true);
195
196 assert_eq!(i8::from_ascii(b"1000").is_err(), true);
197 assert_eq!(u8::from_ascii(b"1000").is_err(), true);
198 assert_eq!(i64::from_ascii(b"1000"), Ok(1000));
199 assert_eq!(u64::from_ascii(b"1000"), Ok(1000));
200
201 assert_eq!(i8::from_ascii(b"-1000").is_err(), true);
202 assert_eq!(u8::from_ascii(b"-1000").is_err(), true);
203 assert_eq!(i64::from_ascii(b"-1000"), Ok(-1000));
204 assert_eq!(u64::from_ascii(b"-1000").is_err(), true);
205 }
206
207 #[test]
208 fn test_from_ascii_radix() {
209 assert_eq!(i8::from_ascii_radix(b"ff", 16).is_err(), true);
210 assert_eq!(u8::from_ascii_radix(b"ff", 16), Ok(255));
211 assert_eq!(i8::from_ascii_radix(b"FF", 16).is_err(), true);
212 assert_eq!(u8::from_ascii_radix(b"FF", 16), Ok(255));
213 }
214}