diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 6032dc9a2d371..148b94a344bc1 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -687,6 +687,71 @@ impl u8 { pub fn escape_ascii(&self) -> ascii::EscapeDefault { ascii::escape_default(*self) } + + /// Converts a value to a digit in the given radix. + /// + /// A 'radix' here is sometimes also called a 'base'. A radix of two + /// indicates a binary number, a radix of ten, decimal, and a radix of + /// sixteen, hexadecimal, to give some common values. Arbitrary + /// radices are supported. + /// + /// 'Digit' is defined to be only the following ASCII characters: + /// + /// * `0-9` + /// * `a-z` + /// * `A-Z` + /// + /// # Errors + /// + /// Returns `None` if the value does not refer to a digit in the given radix. + /// + /// # Panics + /// + /// Panics if given a radix larger than 36. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert_eq!(b'1'.to_digit(10), Some(1)); + /// assert_eq!(b'f'.to_digit(16), Some(15)); + /// ``` + /// + /// Passing a non-digit results in failure: + /// + /// ``` + /// assert_eq!(b'f'.to_digit(10), None); + /// assert_eq!(b'z'.to_digit(16), None); + /// ``` + /// + /// Passing a large radix, causing a panic: + /// + /// ```should_panic + /// // this panics + /// b'1'.to_digit(37); + /// ``` + #[unstable(feature = "ascii_to_digit", issue = "none")] + #[inline] + pub fn to_digit(&self, radix: u32) -> Option { + assert!(radix <= 36, "to_digit: radix is too high (maximum 36)"); + // the code is split up here to improve execution speed for cases where + // the `radix` is constant and 10 or smaller + let val = if intrinsics::likely(radix <= 10) { + // If not a digit, a number greater than radix will be created. + self.wrapping_sub(b'0') + } else { + match self { + b'0'..=b'9' => self - b'0', + b'a'..=b'z' => self - b'a' + 10, + b'A'..=b'Z' => self - b'A' + 10, + _ => return None, + } + }; + let val: u32 = val.into(); + + if val < radix { Some(val) } else { None } + } } #[lang = "u16"] diff --git a/library/core/tests/ascii.rs b/library/core/tests/ascii.rs index 66c25e449df2b..85ac8d28476b3 100644 --- a/library/core/tests/ascii.rs +++ b/library/core/tests/ascii.rs @@ -344,6 +344,23 @@ fn test_is_ascii_control() { ); } +#[cfg(not(bootstrap))] +#[test] +fn test_to_digit() { + assert_eq!(b'0'.to_digit(10), Some(0)); + assert_eq!(b'1'.to_digit(2), Some(1)); + assert_eq!(b'2'.to_digit(3), Some(2)); + assert_eq!(b'9'.to_digit(10), Some(9)); + assert_eq!(b'a'.to_digit(16), Some(10)); + assert_eq!(b'A'.to_digit(16), Some(10)); + assert_eq!(b'b'.to_digit(16), Some(11)); + assert_eq!(b'B'.to_digit(16), Some(11)); + assert_eq!(b'z'.to_digit(36), Some(35)); + assert_eq!(b'Z'.to_digit(36), Some(35)); + assert_eq!(b' '.to_digit(10), None); + assert_eq!(b'$'.to_digit(36), None); +} + // `is_ascii` does a good amount of pointer manipulation and has // alignment-dependent computation. This is all sanity-checked via // `debug_assert!`s, so we test various sizes/alignments thoroughly versus an diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index f6bfe67e1b12c..fc3f38009752c 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -4,6 +4,7 @@ #![feature(array_methods)] #![feature(array_map)] #![feature(array_windows)] +#![cfg_attr(not(bootstrap), feature(ascii_to_digit))] #![feature(bool_to_option)] #![feature(bound_cloned)] #![feature(box_syntax)]