//! This uses the name `AsciiChar`, even though it's not exposed that way right now,
//! because it avoids a whole bunch of "are you sure you didn't mean `char`?"
//! suggestions from rustc if you get anything slightly wrong in here, and overall
//! helps with clarity as we're also referring to `char` intentionally in here.

use crate::mem::transmute;
use crate::{assert_unsafe_precondition, fmt};

/// One of the 128 Unicode characters from U+0000 through U+007F,
/// often known as the [ASCII] subset.
///
/// Officially, this is the first [block] in Unicode, _Basic Latin_.
/// For details, see the [*C0 Controls and Basic Latin*][chart] code chart.
///
/// This block was based on older 7-bit character code standards such as
/// ANSI X3.4-1977, ISO 646-1973, and [NIST FIPS 1-2].
///
/// # When to use this
///
/// The main advantage of this subset is that it's always valid UTF-8.  As such,
/// the `&[ascii::Char]` -> `&str` conversion function (as well as other related
/// ones) are O(1): *no* runtime checks are needed.
///
/// If you're consuming strings, you should usually handle Unicode and thus
/// accept `str`s, not limit yourself to `ascii::Char`s.
///
/// However, certain formats are intentionally designed to produce ASCII-only
/// output in order to be 8-bit-clean.  In those cases, it can be simpler and
/// faster to generate `ascii::Char`s instead of dealing with the variable width
/// properties of general UTF-8 encoded strings, while still allowing the result
/// to be used freely with other Rust things that deal in general `str`s.
///
/// For example, a UUID library might offer a way to produce the string
/// representation of a UUID as an `[ascii::Char; 36]` to avoid memory
/// allocation yet still allow it to be used as UTF-8 via `as_str` without
/// paying for validation (or needing `unsafe` code) the way it would if it
/// were provided as a `[u8; 36]`.
///
/// # Layout
///
/// This type is guaranteed to have a size and alignment of 1 byte.
///
/// # Names
///
/// The variants on this type are [Unicode names][NamesList] of the characters
/// in upper camel case, with a few tweaks:
/// - For `<control>` characters, the primary alias name is used.
/// - `LATIN` is dropped, as this block has no non-latin letters.
/// - `LETTER` is dropped, as `CAPITAL`/`SMALL` suffices in this block.
/// - `DIGIT`s use a single digit rather than writing out `ZERO`, `ONE`, etc.
///
/// [ASCII]: https://www.unicode.org/glossary/index.html#ASCII
/// [block]: https://www.unicode.org/glossary/index.html#block
/// [chart]: https://www.unicode.org/charts/PDF/U0000.pdf
/// [NIST FIPS 1-2]: https://nvlpubs.nist.gov/nistpubs/Legacy/FIPS/fipspub1-2-1977.pdf
/// [NamesList]: https://www.unicode.org/Public/15.0.0/ucd/NamesList.txt
#[derive(Copy, Hash)]
#[derive_const(Clone, Eq, PartialEq, Ord, PartialOrd)]
#[unstable(feature = "ascii_char", issue = "110998")]
#[repr(u8)]
pub enum AsciiChar {
    /// U+0000 (The default variant)
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Null = 0,
    /// U+0001
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    StartOfHeading = 1,
    /// U+0002
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    StartOfText = 2,
    /// U+0003
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    EndOfText = 3,
    /// U+0004
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    EndOfTransmission = 4,
    /// U+0005
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Enquiry = 5,
    /// U+0006
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Acknowledge = 6,
    /// U+0007
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Bell = 7,
    /// U+0008
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Backspace = 8,
    /// U+0009
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CharacterTabulation = 9,
    /// U+000A
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    LineFeed = 10,
    /// U+000B
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    LineTabulation = 11,
    /// U+000C
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    FormFeed = 12,
    /// U+000D
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CarriageReturn = 13,
    /// U+000E
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    ShiftOut = 14,
    /// U+000F
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    ShiftIn = 15,
    /// U+0010
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    DataLinkEscape = 16,
    /// U+0011
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    DeviceControlOne = 17,
    /// U+0012
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    DeviceControlTwo = 18,
    /// U+0013
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    DeviceControlThree = 19,
    /// U+0014
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    DeviceControlFour = 20,
    /// U+0015
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    NegativeAcknowledge = 21,
    /// U+0016
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SynchronousIdle = 22,
    /// U+0017
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    EndOfTransmissionBlock = 23,
    /// U+0018
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Cancel = 24,
    /// U+0019
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    EndOfMedium = 25,
    /// U+001A
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Substitute = 26,
    /// U+001B
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Escape = 27,
    /// U+001C
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    InformationSeparatorFour = 28,
    /// U+001D
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    InformationSeparatorThree = 29,
    /// U+001E
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    InformationSeparatorTwo = 30,
    /// U+001F
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    InformationSeparatorOne = 31,
    /// U+0020
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Space = 32,
    /// U+0021
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    ExclamationMark = 33,
    /// U+0022
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    QuotationMark = 34,
    /// U+0023
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    NumberSign = 35,
    /// U+0024
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    DollarSign = 36,
    /// U+0025
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    PercentSign = 37,
    /// U+0026
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Ampersand = 38,
    /// U+0027
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Apostrophe = 39,
    /// U+0028
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    LeftParenthesis = 40,
    /// U+0029
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    RightParenthesis = 41,
    /// U+002A
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Asterisk = 42,
    /// U+002B
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    PlusSign = 43,
    /// U+002C
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Comma = 44,
    /// U+002D
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    HyphenMinus = 45,
    /// U+002E
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    FullStop = 46,
    /// U+002F
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Solidus = 47,
    /// U+0030
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Digit0 = 48,
    /// U+0031
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Digit1 = 49,
    /// U+0032
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Digit2 = 50,
    /// U+0033
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Digit3 = 51,
    /// U+0034
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Digit4 = 52,
    /// U+0035
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Digit5 = 53,
    /// U+0036
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Digit6 = 54,
    /// U+0037
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Digit7 = 55,
    /// U+0038
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Digit8 = 56,
    /// U+0039
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Digit9 = 57,
    /// U+003A
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Colon = 58,
    /// U+003B
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Semicolon = 59,
    /// U+003C
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    LessThanSign = 60,
    /// U+003D
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    EqualsSign = 61,
    /// U+003E
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    GreaterThanSign = 62,
    /// U+003F
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    QuestionMark = 63,
    /// U+0040
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CommercialAt = 64,
    /// U+0041
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalA = 65,
    /// U+0042
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalB = 66,
    /// U+0043
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalC = 67,
    /// U+0044
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalD = 68,
    /// U+0045
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalE = 69,
    /// U+0046
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalF = 70,
    /// U+0047
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalG = 71,
    /// U+0048
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalH = 72,
    /// U+0049
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalI = 73,
    /// U+004A
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalJ = 74,
    /// U+004B
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalK = 75,
    /// U+004C
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalL = 76,
    /// U+004D
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalM = 77,
    /// U+004E
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalN = 78,
    /// U+004F
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalO = 79,
    /// U+0050
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalP = 80,
    /// U+0051
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalQ = 81,
    /// U+0052
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalR = 82,
    /// U+0053
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalS = 83,
    /// U+0054
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalT = 84,
    /// U+0055
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalU = 85,
    /// U+0056
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalV = 86,
    /// U+0057
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalW = 87,
    /// U+0058
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalX = 88,
    /// U+0059
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalY = 89,
    /// U+005A
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CapitalZ = 90,
    /// U+005B
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    LeftSquareBracket = 91,
    /// U+005C
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    ReverseSolidus = 92,
    /// U+005D
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    RightSquareBracket = 93,
    /// U+005E
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    CircumflexAccent = 94,
    /// U+005F
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    LowLine = 95,
    /// U+0060
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    GraveAccent = 96,
    /// U+0061
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallA = 97,
    /// U+0062
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallB = 98,
    /// U+0063
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallC = 99,
    /// U+0064
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallD = 100,
    /// U+0065
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallE = 101,
    /// U+0066
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallF = 102,
    /// U+0067
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallG = 103,
    /// U+0068
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallH = 104,
    /// U+0069
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallI = 105,
    /// U+006A
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallJ = 106,
    /// U+006B
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallK = 107,
    /// U+006C
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallL = 108,
    /// U+006D
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallM = 109,
    /// U+006E
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallN = 110,
    /// U+006F
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallO = 111,
    /// U+0070
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallP = 112,
    /// U+0071
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallQ = 113,
    /// U+0072
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallR = 114,
    /// U+0073
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallS = 115,
    /// U+0074
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallT = 116,
    /// U+0075
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallU = 117,
    /// U+0076
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallV = 118,
    /// U+0077
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallW = 119,
    /// U+0078
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallX = 120,
    /// U+0079
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallY = 121,
    /// U+007A
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    SmallZ = 122,
    /// U+007B
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    LeftCurlyBracket = 123,
    /// U+007C
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    VerticalLine = 124,
    /// U+007D
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    RightCurlyBracket = 125,
    /// U+007E
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Tilde = 126,
    /// U+007F
    #[unstable(feature = "ascii_char_variants", issue = "110998")]
    Delete = 127,
}

impl AsciiChar {
    /// The character with the lowest ASCII code.
    #[unstable(feature = "ascii_char", issue = "110998")]
    pub const MIN: Self = Self::Null;

    /// The character with the highest ASCII code.
    #[unstable(feature = "ascii_char", issue = "110998")]
    pub const MAX: Self = Self::Delete;

    /// Creates an ASCII character from the byte `b`,
    /// or returns `None` if it's too large.
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn from_u8(b: u8) -> Option<Self> {
        if b <= 127 {
            // SAFETY: Just checked that `b` is in-range
            Some(unsafe { Self::from_u8_unchecked(b) })
        } else {
            None
        }
    }

    /// Creates an ASCII character from the byte `b`,
    /// without checking whether it's valid.
    ///
    /// # Safety
    ///
    /// `b` must be in `0..=127`, or else this is UB.
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const unsafe fn from_u8_unchecked(b: u8) -> Self {
        assert_unsafe_precondition!(
            check_library_ub,
            "`ascii::Char::from_u8_unchecked` input cannot exceed 127.",
            (b: u8 = b) => b <= 127,
        );
        // SAFETY: Our safety precondition is that `b` is in-range.
        unsafe { transmute(b) }
    }

    /// When passed the *number* `0`, `1`, …, `9`, returns the *character*
    /// `'0'`, `'1'`, …, `'9'` respectively.
    ///
    /// If `d >= 10`, returns `None`.
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn digit(d: u8) -> Option<Self> {
        if d < 10 {
            // SAFETY: Just checked it's in-range.
            Some(unsafe { Self::digit_unchecked(d) })
        } else {
            None
        }
    }

    /// When passed the *number* `0`, `1`, …, `9`, returns the *character*
    /// `'0'`, `'1'`, …, `'9'` respectively, without checking that it's in-range.
    ///
    /// # Safety
    ///
    /// This is immediate UB if called with `d > 64`.
    ///
    /// If `d >= 10` and `d <= 64`, this is allowed to return any value or panic.
    /// Notably, it should not be expected to return hex digits, or any other
    /// reasonable extension of the decimal digits.
    ///
    /// (This loose safety condition is intended to simplify soundness proofs
    /// when writing code using this method, since the implementation doesn't
    /// need something really specific, not to make those other arguments do
    /// something useful. It might be tightened before stabilization.)
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    #[track_caller]
    pub const unsafe fn digit_unchecked(d: u8) -> Self {
        assert_unsafe_precondition!(
            check_library_ub,
            "`ascii::Char::digit_unchecked` input cannot exceed 9.",
            (d: u8 = d) => d < 10
        );

        // SAFETY: `'0'` through `'9'` are U+00030 through U+0039,
        // so because `d` must be 64 or less the addition can return at most
        // 112 (0x70), which doesn't overflow and is within the ASCII range.
        unsafe {
            let byte = b'0'.unchecked_add(d);
            Self::from_u8_unchecked(byte)
        }
    }

    /// Gets this ASCII character as a byte.
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn to_u8(self) -> u8 {
        self as u8
    }

    /// Gets this ASCII character as a `char` Unicode Scalar Value.
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn to_char(self) -> char {
        self as u8 as char
    }

    /// Views this ASCII character as a one-code-unit UTF-8 `str`.
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn as_str(&self) -> &str {
        crate::slice::from_ref(self).as_str()
    }

    /// Makes a copy of the value in its upper case equivalent.
    ///
    /// Letters 'a' to 'z' are mapped to 'A' to 'Z'.
    ///
    /// To uppercase the value in-place, use [`make_uppercase`].
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let lowercase_a = ascii::Char::SmallA;
    ///
    /// assert_eq!(
    ///     ascii::Char::CapitalA,
    ///     lowercase_a.to_uppercase(),
    /// );
    /// ```
    ///
    /// [`make_uppercase`]: Self::make_uppercase
    #[must_use = "to uppercase the value in-place, use `make_uppercase()`"]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn to_uppercase(self) -> Self {
        let uppercase_byte = self.to_u8().to_ascii_uppercase();
        // SAFETY: Toggling the 6th bit won't convert ASCII to non-ASCII.
        unsafe { Self::from_u8_unchecked(uppercase_byte) }
    }

    /// Makes a copy of the value in its lower case equivalent.
    ///
    /// Letters 'A' to 'Z' are mapped to 'a' to 'z'.
    ///
    /// To lowercase the value in-place, use [`make_lowercase`].
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    ///
    /// assert_eq!(
    ///     ascii::Char::SmallA,
    ///     uppercase_a.to_lowercase(),
    /// );
    /// ```
    ///
    /// [`make_lowercase`]: Self::make_lowercase
    #[must_use = "to lowercase the value in-place, use `make_lowercase()`"]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn to_lowercase(self) -> Self {
        let lowercase_byte = self.to_u8().to_ascii_lowercase();
        // SAFETY: Setting the 6th bit won't convert ASCII to non-ASCII.
        unsafe { Self::from_u8_unchecked(lowercase_byte) }
    }

    /// Checks that two values are a case-insensitive match.
    ///
    /// This is equivalent to `to_lowercase(a) == to_lowercase(b)`.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let lowercase_a = ascii::Char::SmallA;
    /// let uppercase_a = ascii::Char::CapitalA;
    ///
    /// assert!(lowercase_a.eq_ignore_case(uppercase_a));
    /// ```
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn eq_ignore_case(self, other: Self) -> bool {
        // FIXME(const-hack) `arg.to_u8().to_ascii_lowercase()` -> `arg.to_lowercase()`
        // once `PartialEq` is const for `Self`.
        self.to_u8().to_ascii_lowercase() == other.to_u8().to_ascii_lowercase()
    }

    /// Converts this value to its upper case equivalent in-place.
    ///
    /// Letters 'a' to 'z' are mapped to 'A' to 'Z'.
    ///
    /// To return a new uppercased value without modifying the existing one, use
    /// [`to_uppercase`].
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let mut letter_a = ascii::Char::SmallA;
    ///
    /// letter_a.make_uppercase();
    ///
    /// assert_eq!(ascii::Char::CapitalA, letter_a);
    /// ```
    ///
    /// [`to_uppercase`]: Self::to_uppercase
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn make_uppercase(&mut self) {
        *self = self.to_uppercase();
    }

    /// Converts this value to its lower case equivalent in-place.
    ///
    /// Letters 'A' to 'Z' are mapped to 'a' to 'z'.
    ///
    /// To return a new lowercased value without modifying the existing one, use
    /// [`to_lowercase`].
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let mut letter_a = ascii::Char::CapitalA;
    ///
    /// letter_a.make_lowercase();
    ///
    /// assert_eq!(ascii::Char::SmallA, letter_a);
    /// ```
    ///
    /// [`to_lowercase`]: Self::to_lowercase
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn make_lowercase(&mut self) {
        *self = self.to_lowercase();
    }

    /// Checks if the value is an alphabetic character:
    ///
    /// - 0x41 'A' ..= 0x5A 'Z', or
    /// - 0x61 'a' ..= 0x7A 'z'.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    /// let uppercase_g = ascii::Char::CapitalG;
    /// let a = ascii::Char::SmallA;
    /// let g = ascii::Char::SmallG;
    /// let zero = ascii::Char::Digit0;
    /// let percent = ascii::Char::PercentSign;
    /// let space = ascii::Char::Space;
    /// let lf = ascii::Char::LineFeed;
    /// let esc = ascii::Char::Escape;
    ///
    /// assert!(uppercase_a.is_alphabetic());
    /// assert!(uppercase_g.is_alphabetic());
    /// assert!(a.is_alphabetic());
    /// assert!(g.is_alphabetic());
    /// assert!(!zero.is_alphabetic());
    /// assert!(!percent.is_alphabetic());
    /// assert!(!space.is_alphabetic());
    /// assert!(!lf.is_alphabetic());
    /// assert!(!esc.is_alphabetic());
    /// ```
    #[must_use]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn is_alphabetic(self) -> bool {
        self.to_u8().is_ascii_alphabetic()
    }

    /// Checks if the value is an uppercase character:
    /// 0x41 'A' ..= 0x5A 'Z'.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    /// let uppercase_g = ascii::Char::CapitalG;
    /// let a = ascii::Char::SmallA;
    /// let g = ascii::Char::SmallG;
    /// let zero = ascii::Char::Digit0;
    /// let percent = ascii::Char::PercentSign;
    /// let space = ascii::Char::Space;
    /// let lf = ascii::Char::LineFeed;
    /// let esc = ascii::Char::Escape;
    ///
    /// assert!(uppercase_a.is_uppercase());
    /// assert!(uppercase_g.is_uppercase());
    /// assert!(!a.is_uppercase());
    /// assert!(!g.is_uppercase());
    /// assert!(!zero.is_uppercase());
    /// assert!(!percent.is_uppercase());
    /// assert!(!space.is_uppercase());
    /// assert!(!lf.is_uppercase());
    /// assert!(!esc.is_uppercase());
    /// ```
    #[must_use]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn is_uppercase(self) -> bool {
        self.to_u8().is_ascii_uppercase()
    }

    /// Checks if the value is a lowercase character:
    /// 0x61 'a' ..= 0x7A 'z'.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    /// let uppercase_g = ascii::Char::CapitalG;
    /// let a = ascii::Char::SmallA;
    /// let g = ascii::Char::SmallG;
    /// let zero = ascii::Char::Digit0;
    /// let percent = ascii::Char::PercentSign;
    /// let space = ascii::Char::Space;
    /// let lf = ascii::Char::LineFeed;
    /// let esc = ascii::Char::Escape;
    ///
    /// assert!(!uppercase_a.is_lowercase());
    /// assert!(!uppercase_g.is_lowercase());
    /// assert!(a.is_lowercase());
    /// assert!(g.is_lowercase());
    /// assert!(!zero.is_lowercase());
    /// assert!(!percent.is_lowercase());
    /// assert!(!space.is_lowercase());
    /// assert!(!lf.is_lowercase());
    /// assert!(!esc.is_lowercase());
    /// ```
    #[must_use]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn is_lowercase(self) -> bool {
        self.to_u8().is_ascii_lowercase()
    }

    /// Checks if the value is an alphanumeric character:
    ///
    /// - 0x41 'A' ..= 0x5A 'Z', or
    /// - 0x61 'a' ..= 0x7A 'z', or
    /// - 0x30 '0' ..= 0x39 '9'.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    /// let uppercase_g = ascii::Char::CapitalG;
    /// let a = ascii::Char::SmallA;
    /// let g = ascii::Char::SmallG;
    /// let zero = ascii::Char::Digit0;
    /// let percent = ascii::Char::PercentSign;
    /// let space = ascii::Char::Space;
    /// let lf = ascii::Char::LineFeed;
    /// let esc = ascii::Char::Escape;
    ///
    /// assert!(uppercase_a.is_alphanumeric());
    /// assert!(uppercase_g.is_alphanumeric());
    /// assert!(a.is_alphanumeric());
    /// assert!(g.is_alphanumeric());
    /// assert!(zero.is_alphanumeric());
    /// assert!(!percent.is_alphanumeric());
    /// assert!(!space.is_alphanumeric());
    /// assert!(!lf.is_alphanumeric());
    /// assert!(!esc.is_alphanumeric());
    /// ```
    #[must_use]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn is_alphanumeric(self) -> bool {
        self.to_u8().is_ascii_alphanumeric()
    }

    /// Checks if the value is a decimal digit:
    /// 0x30 '0' ..= 0x39 '9'.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    /// let uppercase_g = ascii::Char::CapitalG;
    /// let a = ascii::Char::SmallA;
    /// let g = ascii::Char::SmallG;
    /// let zero = ascii::Char::Digit0;
    /// let percent = ascii::Char::PercentSign;
    /// let space = ascii::Char::Space;
    /// let lf = ascii::Char::LineFeed;
    /// let esc = ascii::Char::Escape;
    ///
    /// assert!(!uppercase_a.is_digit());
    /// assert!(!uppercase_g.is_digit());
    /// assert!(!a.is_digit());
    /// assert!(!g.is_digit());
    /// assert!(zero.is_digit());
    /// assert!(!percent.is_digit());
    /// assert!(!space.is_digit());
    /// assert!(!lf.is_digit());
    /// assert!(!esc.is_digit());
    /// ```
    #[must_use]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn is_digit(self) -> bool {
        self.to_u8().is_ascii_digit()
    }

    /// Checks if the value is an octal digit:
    /// 0x30 '0' ..= 0x37 '7'.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    ///
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    /// let a = ascii::Char::SmallA;
    /// let zero = ascii::Char::Digit0;
    /// let seven = ascii::Char::Digit7;
    /// let eight = ascii::Char::Digit8;
    /// let percent = ascii::Char::PercentSign;
    /// let lf = ascii::Char::LineFeed;
    /// let esc = ascii::Char::Escape;
    ///
    /// assert!(!uppercase_a.is_octdigit());
    /// assert!(!a.is_octdigit());
    /// assert!(zero.is_octdigit());
    /// assert!(seven.is_octdigit());
    /// assert!(!eight.is_octdigit());
    /// assert!(!percent.is_octdigit());
    /// assert!(!lf.is_octdigit());
    /// assert!(!esc.is_octdigit());
    /// ```
    #[must_use]
    // This is blocked on two unstable features. Please ensure both are
    // stabilized before marking this method as stable.
    #[unstable(feature = "ascii_char", issue = "110998")]
    // #[unstable(feature = "is_ascii_octdigit", issue = "101288")]
    #[inline]
    pub const fn is_octdigit(self) -> bool {
        self.to_u8().is_ascii_octdigit()
    }

    /// Checks if the value is a hexadecimal digit:
    ///
    /// - 0x30 '0' ..= 0x39 '9', or
    /// - 0x41 'A' ..= 0x46 'F', or
    /// - 0x61 'a' ..= 0x66 'f'.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    /// let uppercase_g = ascii::Char::CapitalG;
    /// let a = ascii::Char::SmallA;
    /// let g = ascii::Char::SmallG;
    /// let zero = ascii::Char::Digit0;
    /// let percent = ascii::Char::PercentSign;
    /// let space = ascii::Char::Space;
    /// let lf = ascii::Char::LineFeed;
    /// let esc = ascii::Char::Escape;
    ///
    /// assert!(uppercase_a.is_hexdigit());
    /// assert!(!uppercase_g.is_hexdigit());
    /// assert!(a.is_hexdigit());
    /// assert!(!g.is_hexdigit());
    /// assert!(zero.is_hexdigit());
    /// assert!(!percent.is_hexdigit());
    /// assert!(!space.is_hexdigit());
    /// assert!(!lf.is_hexdigit());
    /// assert!(!esc.is_hexdigit());
    /// ```
    #[must_use]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn is_hexdigit(self) -> bool {
        self.to_u8().is_ascii_hexdigit()
    }

    /// Checks if the value is a punctuation or symbol character
    /// (i.e. not alphanumeric, whitespace, or control):
    ///
    /// - 0x21 ..= 0x2F `! " # $ % & ' ( ) * + , - . /`, or
    /// - 0x3A ..= 0x40 `: ; < = > ? @`, or
    /// - 0x5B ..= 0x60 `` [ \ ] ^ _ ` ``, or
    /// - 0x7B ..= 0x7E `{ | } ~`
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    /// let uppercase_g = ascii::Char::CapitalG;
    /// let a = ascii::Char::SmallA;
    /// let g = ascii::Char::SmallG;
    /// let zero = ascii::Char::Digit0;
    /// let percent = ascii::Char::PercentSign;
    /// let space = ascii::Char::Space;
    /// let lf = ascii::Char::LineFeed;
    /// let esc = ascii::Char::Escape;
    ///
    /// assert!(!uppercase_a.is_punctuation());
    /// assert!(!uppercase_g.is_punctuation());
    /// assert!(!a.is_punctuation());
    /// assert!(!g.is_punctuation());
    /// assert!(!zero.is_punctuation());
    /// assert!(percent.is_punctuation());
    /// assert!(!space.is_punctuation());
    /// assert!(!lf.is_punctuation());
    /// assert!(!esc.is_punctuation());
    /// ```
    #[must_use]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn is_punctuation(self) -> bool {
        self.to_u8().is_ascii_punctuation()
    }

    /// Checks if the value is a graphic character
    /// (i.e. not whitespace or control):
    /// 0x21 '!' ..= 0x7E '~'.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    /// let uppercase_g = ascii::Char::CapitalG;
    /// let a = ascii::Char::SmallA;
    /// let g = ascii::Char::SmallG;
    /// let zero = ascii::Char::Digit0;
    /// let percent = ascii::Char::PercentSign;
    /// let space = ascii::Char::Space;
    /// let lf = ascii::Char::LineFeed;
    /// let esc = ascii::Char::Escape;
    ///
    /// assert!(uppercase_a.is_graphic());
    /// assert!(uppercase_g.is_graphic());
    /// assert!(a.is_graphic());
    /// assert!(g.is_graphic());
    /// assert!(zero.is_graphic());
    /// assert!(percent.is_graphic());
    /// assert!(!space.is_graphic());
    /// assert!(!lf.is_graphic());
    /// assert!(!esc.is_graphic());
    /// ```
    #[must_use]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn is_graphic(self) -> bool {
        self.to_u8().is_ascii_graphic()
    }

    /// Checks if the value is a whitespace character:
    /// 0x20 SPACE, 0x09 HORIZONTAL TAB, 0x0A LINE FEED,
    /// 0x0C FORM FEED, or 0x0D CARRIAGE RETURN.
    ///
    /// Rust uses the WhatWG Infra Standard's [definition of ASCII
    /// whitespace][infra-aw]. There are several other definitions in
    /// wide use. For instance, [the POSIX locale][pct] includes
    /// 0x0B VERTICAL TAB as well as all the above characters,
    /// but—from the very same specification—[the default rule for
    /// "field splitting" in the Bourne shell][bfs] considers *only*
    /// SPACE, HORIZONTAL TAB, and LINE FEED as whitespace.
    ///
    /// If you are writing a program that will process an existing
    /// file format, check what that format's definition of whitespace is
    /// before using this function.
    ///
    /// [infra-aw]: https://infra.spec.whatwg.org/#ascii-whitespace
    /// [pct]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_03_01
    /// [bfs]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_05
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    /// let uppercase_g = ascii::Char::CapitalG;
    /// let a = ascii::Char::SmallA;
    /// let g = ascii::Char::SmallG;
    /// let zero = ascii::Char::Digit0;
    /// let percent = ascii::Char::PercentSign;
    /// let space = ascii::Char::Space;
    /// let lf = ascii::Char::LineFeed;
    /// let esc = ascii::Char::Escape;
    ///
    /// assert!(!uppercase_a.is_whitespace());
    /// assert!(!uppercase_g.is_whitespace());
    /// assert!(!a.is_whitespace());
    /// assert!(!g.is_whitespace());
    /// assert!(!zero.is_whitespace());
    /// assert!(!percent.is_whitespace());
    /// assert!(space.is_whitespace());
    /// assert!(lf.is_whitespace());
    /// assert!(!esc.is_whitespace());
    /// ```
    #[must_use]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn is_whitespace(self) -> bool {
        self.to_u8().is_ascii_whitespace()
    }

    /// Checks if the value is a control character:
    /// 0x00 NUL ..= 0x1F UNIT SEPARATOR, or 0x7F DELETE.
    /// Note that most whitespace characters are control
    /// characters, but SPACE is not.
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let uppercase_a = ascii::Char::CapitalA;
    /// let uppercase_g = ascii::Char::CapitalG;
    /// let a = ascii::Char::SmallA;
    /// let g = ascii::Char::SmallG;
    /// let zero = ascii::Char::Digit0;
    /// let percent = ascii::Char::PercentSign;
    /// let space = ascii::Char::Space;
    /// let lf = ascii::Char::LineFeed;
    /// let esc = ascii::Char::Escape;
    ///
    /// assert!(!uppercase_a.is_control());
    /// assert!(!uppercase_g.is_control());
    /// assert!(!a.is_control());
    /// assert!(!g.is_control());
    /// assert!(!zero.is_control());
    /// assert!(!percent.is_control());
    /// assert!(!space.is_control());
    /// assert!(lf.is_control());
    /// assert!(esc.is_control());
    /// ```
    #[must_use]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn is_control(self) -> bool {
        self.to_u8().is_ascii_control()
    }

    /// Returns an iterator that produces an escaped version of a
    /// character.
    ///
    /// The behavior is identical to
    /// [`ascii::escape_default`](crate::ascii::escape_default).
    ///
    /// # Examples
    ///
    /// ```
    /// #![feature(ascii_char, ascii_char_variants)]
    /// use std::ascii;
    ///
    /// let zero = ascii::Char::Digit0;
    /// let tab = ascii::Char::CharacterTabulation;
    /// let cr = ascii::Char::CarriageReturn;
    /// let lf = ascii::Char::LineFeed;
    /// let apostrophe = ascii::Char::Apostrophe;
    /// let double_quote = ascii::Char::QuotationMark;
    /// let backslash = ascii::Char::ReverseSolidus;
    ///
    /// assert_eq!("0", zero.escape_ascii().to_string());
    /// assert_eq!("\\t", tab.escape_ascii().to_string());
    /// assert_eq!("\\r", cr.escape_ascii().to_string());
    /// assert_eq!("\\n", lf.escape_ascii().to_string());
    /// assert_eq!("\\'", apostrophe.escape_ascii().to_string());
    /// assert_eq!("\\\"", double_quote.escape_ascii().to_string());
    /// assert_eq!("\\\\", backslash.escape_ascii().to_string());
    /// ```
    #[must_use = "this returns the escaped character as an iterator, \
                  without modifying the original"]
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub fn escape_ascii(self) -> super::EscapeDefault {
        super::escape_default(self.to_u8())
    }
}

macro_rules! into_int_impl {
    ($($ty:ty)*) => {
        $(
            #[unstable(feature = "ascii_char", issue = "110998")]
            #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
            const impl From<AsciiChar> for $ty {
                #[inline]
                fn from(chr: AsciiChar) -> $ty {
                    chr as u8 as $ty
                }
            }
        )*
    }
}

into_int_impl!(u8 u16 u32 u64 u128 char);

impl [AsciiChar] {
    /// Views this slice of ASCII characters as a UTF-8 `str`.
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn as_str(&self) -> &str {
        let ascii_ptr: *const Self = self;
        let str_ptr = ascii_ptr as *const str;
        // SAFETY: Each ASCII codepoint in UTF-8 is encoded as one single-byte
        // code unit having the same value as the ASCII byte.
        unsafe { &*str_ptr }
    }

    /// Views this slice of ASCII characters as a slice of `u8` bytes.
    #[unstable(feature = "ascii_char", issue = "110998")]
    #[inline]
    pub const fn as_bytes(&self) -> &[u8] {
        self.as_str().as_bytes()
    }
}

#[unstable(feature = "ascii_char", issue = "110998")]
impl fmt::Display for AsciiChar {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        <str as fmt::Display>::fmt(self.as_str(), f)
    }
}

#[unstable(feature = "ascii_char", issue = "110998")]
impl fmt::Debug for AsciiChar {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use AsciiChar::{Apostrophe, Null, ReverseSolidus as Backslash};

        fn backslash(a: AsciiChar) -> ([AsciiChar; 6], usize) {
            ([Apostrophe, Backslash, a, Apostrophe, Null, Null], 4)
        }

        let (buf, len) = match self {
            AsciiChar::Null => backslash(AsciiChar::Digit0),
            AsciiChar::CharacterTabulation => backslash(AsciiChar::SmallT),
            AsciiChar::CarriageReturn => backslash(AsciiChar::SmallR),
            AsciiChar::LineFeed => backslash(AsciiChar::SmallN),
            AsciiChar::ReverseSolidus => backslash(AsciiChar::ReverseSolidus),
            AsciiChar::Apostrophe => backslash(AsciiChar::Apostrophe),
            _ if self.to_u8().is_ascii_control() => {
                const HEX_DIGITS: [AsciiChar; 16] = *b"0123456789abcdef".as_ascii().unwrap();

                let byte = self.to_u8();
                let hi = HEX_DIGITS[usize::from(byte >> 4)];
                let lo = HEX_DIGITS[usize::from(byte & 0xf)];
                ([Apostrophe, Backslash, AsciiChar::SmallX, hi, lo, Apostrophe], 6)
            }
            _ => ([Apostrophe, *self, Apostrophe, Null, Null, Null], 3),
        };

        f.write_str(buf[..len].as_str())
    }
}
