atoi/lib.rs
1//! A crate for parsing integers directly from ASCII (`[u8]`) without encoding them into utf8
2//! first. The name is inspired by the famous C function.
3//!
4//! Using `str::from_utf8` and `str::parse`
5//! is likely to be more idiomatic. Use this crate if you want to avoid decoding bytes into utf8
6//! (e.g. for performance reasons).
7//!
8//! Note that if you want to know how much of the input has been used, you can use the
9//! `FromRadix10` trait, for example:
10//!
11//! ```rust
12//! use atoi::FromRadix10;
13//!
14//! /// Return the parsed integer and remaining slice if successful.
15//! fn atoi_with_rest<I: FromRadix10>(text: &[u8]) -> ((&[u8], Option<I>)) {
16//! match I::from_radix_10(text) {
17//! (_, 0) => (text, None),
18//! (n, used) => (&text[used..], Some(n)),
19//! }
20//! }
21//! ```
22#![cfg_attr(not(std), no_std)]
23
24use num_traits::{
25 ops::checked::{CheckedAdd, CheckedMul},
26 Bounded, CheckedSub, One, Signed, Zero,
27};
28use core::{
29 cmp::{max, min},
30 ops::{AddAssign, DivAssign, MulAssign, SubAssign},
31};
32
33/// Parses an integer from a slice.
34///
35/// Contrary to its 'C' counterpart atoi is generic and will require a type argument if the type
36/// inference can not determine its result. It will also check for overflow / underflow and allow
37/// for Signs.
38///
39/// Use [`FromRadix10`] or [`FromRadix10Checked`] directly if you do not want to allow signs. Use
40/// [`FromRadix10`] or [`FromRadix10Signed`] if you want to opt out overflow / underflow checking.
41///
42/// # Example
43///
44/// ```
45/// use atoi::atoi;
46/// // Parsing to digits from a slice
47/// assert_eq!(Some(42), atoi::<u32>(b"42"));
48/// // Additional bytes after the number are ignored. If you want to know how many bytes were used
49/// // to parse the number use `FromRadix10::from_radix_10`.
50/// assert_eq!(Some(42), atoi::<u32>(b"42 is the answer to life, the universe and everything"));
51/// // `None` is returned if the slice does not start with a digit
52/// assert_eq!(None, atoi::<u32>(b"Sadly we do not know the question"));
53/// // While signed integer types are supported...
54/// assert_eq!(Some(42), atoi::<i32>(b"42"));
55/// // Signs are allowed.
56/// assert_eq!(Some(-42), atoi::<i32>(b"-42"));
57/// // Leading zeros are allowed
58/// assert_eq!(Some(42), atoi::<u32>(b"0042"));
59/// // Overflows will return `None`
60/// assert_eq!(None, atoi::<u8>(b"256"));
61/// ```
62///
63/// # Return
64///
65/// Returns a a number if the slice started with a number, otherwise `None` is returned.
66pub fn atoi<I>(text: &[u8]) -> Option<I>
67where
68 I: FromRadix10SignedChecked,
69{
70 match I::from_radix_10_signed_checked(text) {
71 (_, 0) | (None, _) => None,
72 (Some(n), _) => Some(n),
73 }
74}
75
76/// Types implementing this trait can be parsed from a positional numeral system with radix 10
77pub trait FromRadix10: Sized {
78 /// Parses an integer from a slice.
79 ///
80 /// # Example
81 ///
82 /// ```
83 /// use atoi::FromRadix10;
84 /// // Parsing to digits from a slice
85 /// assert_eq!((42,2), u32::from_radix_10(b"42"));
86 /// // Additional bytes after the number are ignored
87 /// assert_eq!((42,2), u32::from_radix_10(b"42 is the answer to life, the universe and everything"));
88 /// // (0,0) is returned if the slice does not start with a digit
89 /// assert_eq!((0,0), u32::from_radix_10(b"Sadly we do not know the question"));
90 /// // While signed integer types are supported...
91 /// assert_eq!((42,2), i32::from_radix_10(b"42"));
92 /// // Signs are not allowed (even for signed integer types)
93 /// assert_eq!((0,0), i32::from_radix_10(b"-42"));
94 /// // Leading zeros are allowed
95 /// assert_eq!((42,4), u32::from_radix_10(b"0042"));
96 /// ```
97 ///
98 /// # Return
99 ///
100 /// Returns a tuple with two numbers. The first is the integer parsed or zero, the second is the
101 /// index of the byte right after the parsed number. If the second element is zero the slice
102 /// did not start with an ASCII digit.
103 fn from_radix_10(_: &[u8]) -> (Self, usize);
104}
105
106/// Types implementing this trait can be parsed from a positional numeral system with radix 10.
107/// Acts much like `FromRadix10`, but performs additional checks for overflows.
108pub trait FromRadix10Checked: FromRadix10 {
109 /// Parses an integer from a slice.
110 ///
111 /// # Example
112 ///
113 /// ```
114 /// use atoi::FromRadix10Checked;
115 /// // Parsing to digits from a slice
116 /// assert_eq!((Some(42),2), u32::from_radix_10_checked(b"42"));
117 /// // Additional bytes after the number are ignored
118 /// assert_eq!((Some(42),2), u32::from_radix_10_checked(b"42 is the answer to life, the universe and everything"));
119 /// // (0,0) is returned if the slice does not start with a digit
120 /// assert_eq!((Some(0),0), u32::from_radix_10_checked(b"Sadly we do not know the question"));
121 /// // While signed integer types are supported...
122 /// assert_eq!((Some(42),2), i32::from_radix_10_checked(b"42"));
123 /// // Signs are not allowed (even for signed integer types)
124 /// assert_eq!((Some(0),0), i32::from_radix_10_checked(b"-42"));
125 /// // Leading zeros are allowed
126 /// assert_eq!((Some(42),4), u32::from_radix_10_checked(b"0042"));
127 /// // Overflow is indicated by `None`
128 /// assert_eq!((None, 3), u8::from_radix_10_checked(b"256"));
129 /// ```
130 ///
131 /// # Return
132 ///
133 /// Returns a tuple with two numbers. The first is the integer parsed or zero if no digit has
134 /// been found. None, if there were too many, or too high dighits and the parsing overflowed.
135 /// The second is the index of the byte right after the parsed number. If the second element is
136 /// zero the slice did not start with an ASCII digit.
137 fn from_radix_10_checked(_: &[u8]) -> (Option<Self>, usize);
138}
139
140/// Types implementing this trait can be parsed from a positional numeral system with radix 16
141pub trait FromRadix16: Sized {
142 /// Parses an integer from a slice.
143 ///
144 /// # Example
145 ///
146 /// ```
147 /// use atoi::FromRadix16;
148 /// // Parsing to digits from a slice
149 /// assert_eq!((42,2), u32::from_radix_16(b"2a"));
150 /// // Additional bytes after the number are ignored
151 /// assert_eq!((42,2), u32::from_radix_16(b"2a is the answer to life, the universe and everything"));
152 /// // (0,0) is returned if the slice does not start with a digit
153 /// assert_eq!((0,0), u32::from_radix_16(b"Sadly we do not know the question"));
154 /// // While signed integer types are supported...
155 /// assert_eq!((42,2), i32::from_radix_16(b"2a"));
156 /// // Signs are not allowed (even for signed integer types)
157 /// assert_eq!((0,0), i32::from_radix_16(b"-2a"));
158 /// // Leading zeros are allowed
159 /// assert_eq!((42,4), u32::from_radix_16(b"002a"));
160 /// // so are uppercase letters
161 /// assert_eq!((42,4), u32::from_radix_16(b"002A"));
162 /// ```
163 ///
164 /// # Return
165 ///
166 /// Returns a tuple with two numbers. The first is the integer parsed or zero, the second is the
167 /// index of the byte right after the parsed number. If the second element is zero the slice
168 /// did not start with an ASCII digit.
169 fn from_radix_16(_: &[u8]) -> (Self, usize);
170}
171
172/// Types implementing this trait can be parsed from a positional numeral system with radix 16.
173/// Acts much like `FromRadix16`, but performs additional checks for overflows.
174pub trait FromRadix16Checked: FromRadix16 {
175 /// Parses an integer from a slice.
176 ///
177 /// # Example
178 ///
179 /// ```
180 /// use atoi::FromRadix16Checked;
181 /// // Parsing to digits from a slice
182 /// assert_eq!((Some(42),2), u32::from_radix_16_checked(b"2a"));
183 /// // Additional bytes after the number are ignored
184 /// assert_eq!((Some(42),2), u32::from_radix_16_checked(b"2a is the answer to life, the \
185 /// universe and everything"));
186 /// // (0,0) is returned if the slice does not start with a digit
187 /// assert_eq!((Some(0),0), u32::from_radix_16_checked(b"Sadly we do not know the question"));
188 /// // While signed integer types are supported...
189 /// assert_eq!((Some(42),2), i32::from_radix_16_checked(b"2a"));
190 /// // Signs are not allowed (even for signed integer types)
191 /// assert_eq!((Some(0),0), i32::from_radix_16_checked(b"-2a"));
192 /// // Leading zeros are allowed
193 /// assert_eq!((Some(42),4), u32::from_radix_16_checked(b"002a"));
194 /// // So are uppercase letters
195 /// assert_eq!((Some(42),2), u32::from_radix_16_checked(b"2A"))
196 /// ```
197 ///
198 /// # Return
199 ///
200 /// Returns a tuple with two numbers. The first is the integer parsed or zero if no digit has
201 /// been found. None, if there were too many, or too high dighits and the parsing overflowed.
202 /// The second is the index of the byte right after the parsed number. If the second element is
203 /// zero the slice did not start with an ASCII digit.
204 fn from_radix_16_checked(_: &[u8]) -> (Option<Self>, usize);
205}
206
207/// Types implementing this trait can be parsed from a positional numeral system with radix 10. This
208/// trait allows for an additional sign character (`+` or `-`) in front of the actual number in
209/// order, to allow for parsing negative values.
210pub trait FromRadix10Signed: Sized {
211 /// Parses an integer from a slice.
212 ///
213 /// # Example
214 ///
215 /// ```
216 /// use atoi::FromRadix10Signed;
217 /// // Parsing to digits from a slice
218 /// assert_eq!((42,2), i32::from_radix_10_signed(b"42"));
219 /// // Additional bytes after the number are ignored
220 /// assert_eq!((42,2), i32::from_radix_10_signed(b"42 is the answer to life, the universe and everything"));
221 /// // (0,0) is returned if the slice does not start with a digit
222 /// assert_eq!((0,0), i32::from_radix_10_signed(b"Sadly we do not know the question"));
223 /// // Signs are allowed
224 /// assert_eq!((-42,3), i32::from_radix_10_signed(b"-42"));
225 /// // Signs are allowed
226 /// assert_eq!((42,3), i32::from_radix_10_signed(b"+42"));
227 /// // Even on unsigned types.
228 /// assert_eq!((0,2), u32::from_radix_10_signed(b"-0"));
229 /// // Leading zeros are allowed
230 /// assert_eq!((42,4), i32::from_radix_10_signed(b"0042"));
231 /// ```
232 ///
233 /// # Return
234 ///
235 /// Returns a tuple with two numbers. The first is the integer parsed or zero, the second is the
236 /// index of the byte right after the parsed number. If the second element is zero the slice
237 /// did not start with an ASCII digit.
238 fn from_radix_10_signed(_: &[u8]) -> (Self, usize);
239}
240
241/// Types implementing this trait can be parsed from a positional numeral system with radix 10.
242/// Acts much like `FromRadix10Signed`, but performs additional checks for overflows.
243pub trait FromRadix10SignedChecked: FromRadix10Signed {
244 /// Parses an integer from a slice.
245 ///
246 /// # Example
247 ///
248 /// ```
249 /// use atoi::FromRadix10SignedChecked;
250 /// // Parsing to digits from a slice
251 /// assert_eq!((Some(42),2), u32::from_radix_10_signed_checked(b"42"));
252 /// // Additional bytes after the number are ignored
253 /// assert_eq!((Some(42),2), u32::from_radix_10_signed_checked(b"42 is the answer to life, the universe and everything"));
254 /// // (0,0) is returned if the slice does not start with a digit
255 /// assert_eq!((Some(0),0), u32::from_radix_10_signed_checked(b"Sadly we do not know the question"));
256 /// // While signed integer types are supported...
257 /// assert_eq!((Some(42),2), i32::from_radix_10_signed_checked(b"42"));
258 /// // Signs are allowed
259 /// assert_eq!((Some(-42),3), i32::from_radix_10_signed_checked(b"-42"));
260 /// // -0 is ok, even for an unsigned type
261 /// assert_eq!((Some(0),2), u32::from_radix_10_signed_checked(b"-0"));
262 /// // -1 is an Underflow
263 /// assert_eq!((None,2), u32::from_radix_10_signed_checked(b"-1"));
264 /// // Negative values for unsigned types are handled as `None`.
265 /// assert_eq!((None,3), u32::from_radix_10_signed_checked(b"-42"));
266 /// // Leading zeros are allowed
267 /// assert_eq!((Some(42),4), u32::from_radix_10_signed_checked(b"0042"));
268 /// // Overflow is indicated by `None`
269 /// assert_eq!((None, 3), u8::from_radix_10_signed_checked(b"256"));
270 /// assert_eq!((None, 4), i8::from_radix_10_signed_checked(b"+128"));
271 /// assert_eq!((None, 4), i8::from_radix_10_signed_checked(b"-129"));
272 /// ```
273 ///
274 /// # Return
275 ///
276 /// Returns a tuple with two numbers. The first is the integer parsed or zero if no digit has
277 /// been found. None, if there were too many, or too high dighits and the parsing overflowed.
278 /// The second is the index of the byte right after the parsed number. If the second element is
279 /// zero the slice did not start with an ASCII digit.
280 fn from_radix_10_signed_checked(_: &[u8]) -> (Option<Self>, usize);
281}
282
283/// A bounded integer, whose representation can overflow and therefore can only store a maximum
284/// number of digits
285pub trait MaxNumDigits {
286 /// Given a representation with a radix character I, what is the maximum number of digits we can
287 /// parse without the integer overflowing for sure?
288 fn max_num_digits(radix: Self) -> usize;
289
290 /// Returns the maximum number of digits a negative representation of `I` can have depending on
291 /// `radix`.
292 fn max_num_digits_negative(radix: Self) -> usize;
293}
294
295impl<I> MaxNumDigits for I
296where
297 I: Bounded + Zero + DivAssign + Ord + Copy,
298{
299 /// Returns the maximum number of digits a nonnegative representation of `I` can have depending
300 /// on `radix`.
301 fn max_num_digits(radix: I) -> usize {
302 let mut max = I::max_value();
303 let mut d = 0;
304 while max > I::zero() {
305 d += 1;
306 max /= radix;
307 }
308 d
309 }
310
311 /// Returns the maximum number of digits a negative representation of `I` can have depending
312 /// on `radix`.
313 fn max_num_digits_negative(radix: I) -> usize {
314 let mut min = I::min_value();
315 let mut d = 0;
316 while min < I::zero() {
317 d += 1;
318 min /= radix;
319 }
320 d
321 }
322}
323
324/// Converts an ascii character to digit
325///
326/// # Example
327///
328/// ```
329/// use atoi::ascii_to_digit;
330/// assert_eq!(Some(5), ascii_to_digit(b'5'));
331/// assert_eq!(None, ascii_to_digit::<u32>(b'x'));
332/// ```
333pub fn ascii_to_digit<I>(character: u8) -> Option<I>
334where
335 I: Zero + One,
336{
337 match character {
338 b'0' => Some(nth(0)),
339 b'1' => Some(nth(1)),
340 b'2' => Some(nth(2)),
341 b'3' => Some(nth(3)),
342 b'4' => Some(nth(4)),
343 b'5' => Some(nth(5)),
344 b'6' => Some(nth(6)),
345 b'7' => Some(nth(7)),
346 b'8' => Some(nth(8)),
347 b'9' => Some(nth(9)),
348 _ => None,
349 }
350}
351
352impl<I> FromRadix10 for I
353where
354 I: Zero + One + AddAssign + MulAssign,
355{
356 fn from_radix_10(text: &[u8]) -> (Self, usize) {
357 let mut index = 0;
358 let mut number = I::zero();
359 while index != text.len() {
360 if let Some(digit) = ascii_to_digit(text[index]) {
361 number *= nth(10);
362 number += digit;
363 index += 1;
364 } else {
365 break;
366 }
367 }
368 (number, index)
369 }
370}
371
372impl<I> FromRadix10Signed for I
373where
374 I: Zero + One + AddAssign + SubAssign + MulAssign,
375{
376 fn from_radix_10_signed(text: &[u8]) -> (Self, usize) {
377 let mut index;
378 let mut number = I::zero();
379
380 let (sign, offset) = text
381 .first()
382 .and_then(|&byte| Sign::try_from(byte))
383 .map(|sign| (sign, 1))
384 .unwrap_or((Sign::Plus, 0));
385
386 index = offset;
387
388 // Having two dedicated loops for both the negative and the nonnegative case is rather
389 // verbose, yet performed up to 40% better then a more terse single loop with
390 // `number += digit * signum`.
391
392 match sign {
393 Sign::Plus => {
394 while index != text.len() {
395 if let Some(digit) = ascii_to_digit::<I>(text[index]) {
396 number *= nth(10);
397 number += digit;
398 index += 1;
399 } else {
400 break;
401 }
402 }
403 }
404 Sign::Minus => {
405 while index != text.len() {
406 if let Some(digit) = ascii_to_digit::<I>(text[index]) {
407 number *= nth(10);
408 number -= digit;
409 index += 1;
410 } else {
411 break;
412 }
413 }
414 }
415 }
416
417 (number, index)
418 }
419}
420
421impl<I> FromRadix10SignedChecked for I
422where
423 I: Zero
424 + One
425 + AddAssign
426 + MulAssign
427 + SubAssign
428 + CheckedAdd
429 + CheckedSub
430 + CheckedMul
431 + MaxNumDigits,
432{
433 fn from_radix_10_signed_checked(text: &[u8]) -> (Option<Self>, usize) {
434 let mut index;
435 let mut number = I::zero();
436
437 let (sign, offset) = text
438 .first()
439 .and_then(|&byte| Sign::try_from(byte))
440 .map(|sign| (sign, 1))
441 .unwrap_or((Sign::Plus, 0));
442
443 index = offset;
444
445 // Having two dedicated loops for both the negative and the nonnegative case is rather
446 // verbose, yet performed up to 40% better then a more terse single loop with
447 // `number += digit * signum`.
448
449 match sign {
450 Sign::Plus => {
451 let max_safe_digits = max(1, I::max_num_digits(nth(10))) - 1;
452 let max_safe_index = min(text.len(), max_safe_digits + offset);
453 while index != max_safe_index {
454 if let Some(digit) = ascii_to_digit::<I>(text[index]) {
455 number *= nth(10);
456 number += digit;
457 index += 1;
458 } else {
459 break;
460 }
461 }
462 // We parsed the digits, which do not need checking now lets see the next one:
463 let mut number = Some(number);
464 while index != text.len() {
465 if let Some(digit) = ascii_to_digit(text[index]) {
466 number = number.and_then(|n| n.checked_mul(&nth(10)));
467 number = number.and_then(|n| n.checked_add(&digit));
468 index += 1;
469 } else {
470 break;
471 }
472 }
473 (number, index)
474 }
475 Sign::Minus => {
476 let max_safe_digits = max(1, I::max_num_digits_negative(nth(10))) - 1;
477 let max_safe_index = min(text.len(), max_safe_digits + offset);
478 while index != max_safe_index {
479 if let Some(digit) = ascii_to_digit::<I>(text[index]) {
480 number *= nth(10);
481 number -= digit;
482 index += 1;
483 } else {
484 break;
485 }
486 }
487 // We parsed the digits, which do not need checking now lets see the next one:
488 let mut number = Some(number);
489 while index != text.len() {
490 if let Some(digit) = ascii_to_digit(text[index]) {
491 number = number.and_then(|n| n.checked_mul(&nth(10)));
492 number = number.and_then(|n| n.checked_sub(&digit));
493 index += 1;
494 } else {
495 break;
496 }
497 }
498 (number, index)
499 }
500 }
501 }
502}
503
504impl<I> FromRadix10Checked for I
505where
506 I: Zero + One + FromRadix10 + CheckedMul + CheckedAdd + MaxNumDigits,
507{
508 fn from_radix_10_checked(text: &[u8]) -> (Option<I>, usize) {
509 let max_safe_digits = max(1, I::max_num_digits_negative(nth(10))) - 1;
510 let (number, mut index) = I::from_radix_10(&text[..min(text.len(), max_safe_digits)]);
511 let mut number = Some(number);
512 // We parsed the digits, which do not need checking now lets see the next one:
513 while index != text.len() {
514 if let Some(digit) = ascii_to_digit(text[index]) {
515 number = number.and_then(|n| n.checked_mul(&nth(10)));
516 number = number.and_then(|n| n.checked_add(&digit));
517 index += 1;
518 } else {
519 break;
520 }
521 }
522 (number, index)
523 }
524}
525
526/// Converts an ascii character to digit
527fn ascii_to_hexdigit<I>(character: u8) -> Option<I>
528where
529 I: Zero + One,
530{
531 match character {
532 b'0' => Some(nth(0)),
533 b'1' => Some(nth(1)),
534 b'2' => Some(nth(2)),
535 b'3' => Some(nth(3)),
536 b'4' => Some(nth(4)),
537 b'5' => Some(nth(5)),
538 b'6' => Some(nth(6)),
539 b'7' => Some(nth(7)),
540 b'8' => Some(nth(8)),
541 b'9' => Some(nth(9)),
542 b'a' | b'A' => Some(nth(10)),
543 b'b' | b'B' => Some(nth(11)),
544 b'c' | b'C' => Some(nth(12)),
545 b'd' | b'D' => Some(nth(13)),
546 b'e' | b'E' => Some(nth(14)),
547 b'f' | b'F' => Some(nth(15)),
548 _ => None,
549 }
550}
551
552impl<I> FromRadix16 for I
553where
554 I: Zero + One + AddAssign + MulAssign,
555{
556 fn from_radix_16(text: &[u8]) -> (Self, usize) {
557 let mut index = 0;
558 let mut number = I::zero();
559 while index != text.len() {
560 if let Some(digit) = ascii_to_hexdigit(text[index]) {
561 number *= nth(16);
562 number += digit;
563 index += 1;
564 } else {
565 break;
566 }
567 }
568 (number, index)
569 }
570}
571
572impl<I> FromRadix16Checked for I
573where
574 I: Zero + One + FromRadix16 + CheckedMul + CheckedAdd + MaxNumDigits,
575{
576 fn from_radix_16_checked(text: &[u8]) -> (Option<I>, usize) {
577 let max_safe_digits = max(1, I::max_num_digits_negative(nth(10))) - 1;
578 let (number, mut index) = I::from_radix_16(&text[..min(text.len(), max_safe_digits)]);
579 let mut number = Some(number);
580 // We parsed the digits, which do not need checking now lets see the next one:
581 while index != text.len() {
582 if let Some(digit) = ascii_to_hexdigit(text[index]) {
583 number = number.and_then(|n| n.checked_mul(&nth(16)));
584 number = number.and_then(|n| n.checked_add(&digit));
585 index += 1;
586 } else {
587 break;
588 }
589 }
590 (number, index)
591 }
592}
593
594/// Representation of a numerical sign
595#[derive(Clone, Copy, Debug, PartialEq, Eq)]
596pub enum Sign {
597 Plus,
598 Minus,
599}
600
601impl Sign {
602 /// Trys to convert an ascii character into a `Sign`
603 ///
604 /// # Example
605 ///
606 /// ```
607 /// use atoi::Sign;
608 /// assert_eq!(Some(Sign::Plus), Sign::try_from(b'+'));
609 /// assert_eq!(Some(Sign::Minus), Sign::try_from(b'-'));
610 /// assert_eq!(None, Sign::try_from(b'1'));
611 /// ```
612 pub fn try_from(byte: u8) -> Option<Sign> {
613 match byte {
614 b'+' => Some(Sign::Plus),
615 b'-' => Some(Sign::Minus),
616 _ => None,
617 }
618 }
619
620 /// Returns either `+1` or `-1`
621 pub fn signum<I>(self) -> I
622 where
623 I: Signed,
624 {
625 match self {
626 Sign::Plus => I::one(),
627 Sign::Minus => -I::one(),
628 }
629 }
630}
631
632// At least for primitive types this function does not incur runtime costs, since it is only called
633// with constants
634fn nth<I>(n: u8) -> I
635where
636 I: Zero + One,
637{
638 let mut i = I::zero();
639 for _ in 0..n {
640 i = i + I::one();
641 }
642 i
643}
644
645#[cfg(test)]
646mod test {
647
648 use super::*;
649
650 #[test]
651 fn max_digits() {
652 assert_eq!(10, i32::max_num_digits(10));
653 assert_eq!(10, u32::max_num_digits(10));
654 assert_eq!(19, i64::max_num_digits(10));
655 assert_eq!(20, u64::max_num_digits(10));
656 assert_eq!(3, u8::max_num_digits(10));
657 assert_eq!(3, i8::max_num_digits(10));
658 }
659
660 #[test]
661 fn max_digits_negative() {
662 assert_eq!(10, i32::max_num_digits_negative(10));
663 assert_eq!(0, u32::max_num_digits_negative(10));
664 assert_eq!(19, i64::max_num_digits_negative(10));
665 assert_eq!(0, u64::max_num_digits_negative(10));
666 assert_eq!(0, u8::max_num_digits_negative(10));
667 assert_eq!(3, i8::max_num_digits_negative(10));
668 }
669
670 #[test]
671 fn checked_parsing() {
672 assert_eq!((Some(255), 3), u8::from_radix_10_checked(b"255"));
673 assert_eq!((None, 3), u8::from_radix_10_checked(b"256"));
674 assert_eq!((None, 4), u8::from_radix_10_checked(b"1000"));
675 assert_eq!((Some(25), 2), u8::from_radix_10_checked(b"25"));
676 assert_eq!((Some(25), 2), u8::from_radix_10_checked(b"25Blub"));
677 }
678
679 #[test]
680 fn checked_parsing_radix_16() {
681 assert_eq!((Some(255), 2), u8::from_radix_16_checked(b"FF"));
682 assert_eq!((None, 3), u8::from_radix_16_checked(b"100"));
683 assert_eq!((None, 4), u8::from_radix_16_checked(b"1000"));
684 assert_eq!((Some(25), 2), u8::from_radix_16_checked(b"19"));
685 assert_eq!((Some(25), 2), u8::from_radix_16_checked(b"19!Blub"));
686 }
687}