diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 6032dc9a2d371..cf53b29758ec0 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -687,6 +687,74 @@ 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: + /// + /// ``` + /// #![feature(byte_parse_ascii_digit)] + /// assert_eq!(b'1'.parse_ascii_digit(10), Some(1)); + /// assert_eq!(b'f'.parse_ascii_digit(16), Some(15)); + /// ``` + /// + /// Passing a non-digit results in failure: + /// + /// ``` + /// #![feature(byte_parse_ascii_digit)] + /// assert_eq!(b'f'.parse_ascii_digit(10), None); + /// assert_eq!(b'z'.parse_ascii_digit(16), None); + /// ``` + /// + /// Passing a large radix, causing a panic: + /// + /// ```should_panic + /// #![feature(byte_parse_ascii_digit)] + /// // this panics + /// b'1'.parse_ascii_digit(37); + /// ``` + #[unstable(feature = "byte_parse_ascii_digit", issue = "83447")] + #[inline] + pub fn parse_ascii_digit(&self, radix: u32) -> Option { + assert!(radix <= 36, "parse_ascii_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..8c06848e0c2d2 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'.parse_ascii_digit(10), Some(0)); + assert_eq!(b'1'.parse_ascii_digit(2), Some(1)); + assert_eq!(b'2'.parse_ascii_digit(3), Some(2)); + assert_eq!(b'9'.parse_ascii_digit(10), Some(9)); + assert_eq!(b'a'.parse_ascii_digit(16), Some(10)); + assert_eq!(b'A'.parse_ascii_digit(16), Some(10)); + assert_eq!(b'b'.parse_ascii_digit(16), Some(11)); + assert_eq!(b'B'.parse_ascii_digit(16), Some(11)); + assert_eq!(b'z'.parse_ascii_digit(36), Some(35)); + assert_eq!(b'Z'.parse_ascii_digit(36), Some(35)); + assert_eq!(b' '.parse_ascii_digit(10), None); + assert_eq!(b'$'.parse_ascii_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..4f1034f5586dd 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -44,6 +44,7 @@ #![feature(step_trait_ext)] #![feature(str_internals)] #![feature(test)] +#![cfg_attr(not(bootstrap), feature(byte_parse_ascii_digit))] #![feature(trusted_len)] #![feature(try_trait)] #![feature(slice_internals)]