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}