content: // Copyright 2016 Google Inc. Use of this source code is governed by an
  // MIT-style license that can be found in the LICENSE file or at
  // https://opensource.org/licenses/MIT.
  
  import 'package:charcode/charcode.dart';
  
  /// The difference between upper- and lowercase ASCII letters.
  ///
  /// `0b100000` can be bitwise-ORed with uppercase ASCII letters to get their
  /// lowercase equivalents.
  const _asciiCaseBit = 0x20;
  
  /// The highest character allowed in CSS.
  ///
  /// See https://drafts.csswg.org/css-syntax-3/#maximum-allowed-code-point
  const maxAllowedCharacter = 0x10FFFF;
  
  // Define these checks as extension getters so they can be used in pattern
  // matches.
  extension CharacterExtension on int {
    /// Returns whether [character] is a letter or number.
    bool get isAlphanumeric => isAlphabetic || isDigit;
  
    /// Returns whether [character] is a letter.
    bool get isAlphabetic =>
        (this >= $a && this <= $z) || (this >= $A && this <= $Z);
  
    /// Returns whether [character] is a number.
    bool get isDigit => this >= $0 && this <= $9;
  
    /// Returns whether [character] is legal as the start of a Sass identifier.
    bool get isNameStart => this == $_ || isAlphabetic || this >= 0x0080;
  
    /// Returns whether [character] is legal in the body of a Sass identifier.
    bool get isName => isNameStart || isDigit || this == $minus;
  
    /// Returns whether [character] is the beginning of a UTF-16 surrogate pair.
    bool get isHighSurrogate =>
        // A character is a high surrogate exactly if it matches 0b110110XXXXXXXXXX.
        // 0x36 == 0b110110.
        this >> 10 == 0x36;
  
    /// Returns whether [character] is the end of a UTF-16 surrogate pair.
    bool get isLowSurrogate =>
        // A character is a high surrogate exactly if it matches 0b110111XXXXXXXXXX.
        // 0x36 == 0b110111.
        this >> 10 == 0x37;
  
    /// Returns whether [character] is a Unicode private-use code point in the Basic
    /// Multilingual Plane.
    ///
    /// See https://en.wikipedia.org/wiki/Private_Use_Areas for details.
    bool get isPrivateUseBMP => this >= 0xE000 && this <= 0xF8FF;
  
    /// Returns whether [character] is the high surrogate for a code point in a
    /// Unicode private-use supplementary plane.
    ///
    /// See https://en.wikipedia.org/wiki/Private_Use_Areas for details.
    bool get isPrivateUseHighSurrogate =>
        // Supplementary Private Use Area-A's and B's high surrogates range from
        // 0xDB80 to 0xDBFF, which covers exactly the range 0b110110111XXXXXXX.
        // 0b110110111 == 0x1B7.
        this >> 7 == 0x1B7;
  
    /// Returns whether [character] is a hexadecimal digit.
    bool get isHex =>
        isDigit || (this >= $a && this <= $f) || (this >= $A && this <= $F);
  }
  
  // Like [CharacterExtension], but these are defined on nullable ints because
  // they only use equality comparisons.
  //
  // This also extends a few [CharacterExtension] getters to return `false` for
  // null values.
  extension NullableCharacterExtension on int? {
    /// Returns whether [character] is an ASCII whitespace character.
    bool get isWhitespace => isSpaceOrTab || isNewline;
  
    /// Returns whether [character] is an ASCII newline.
    bool get isNewline => this == $lf || this == $cr || this == $ff;
  
    /// Returns whether [character] is a space or a tab character.
    bool get isSpaceOrTab => this == $space || this == $tab;
  
    /// Returns whether [character] is a number.
    bool get isDigit {
      var self = this;
      return self != null && self.isDigit;
    }
  
    /// Returns whether [character] is a hexadecimal digit.
    bool get isHex {
      var self = this;
      return self != null && self.isHex;
    }
  }
  
  /// Combines a UTF-16 high and low surrogate pair into a single code unit.
  ///
  /// See https://en.wikipedia.org/wiki/UTF-16 for details.
  int combineSurrogates(int highSurrogate, int lowSurrogate) =>
      // 0x3FF == 0b0000001111111111, which masks out the six bits that indicate
      // high/low surrogates.
      0x10000 + ((highSurrogate & 0x3FF) << 10) + (lowSurrogate & 0x3FF);
  
  /// Returns whether [identifier] is module-private.
  ///
  /// Assumes [identifier] is a valid Sass identifier.
  bool isPrivate(String identifier) {
    var first = identifier.codeUnitAt(0);
    return first == $dash || first == $underscore;
  }
  
  /// Returns the value of [character] as a hex digit.
  ///
  /// Assumes that [character] is a hex digit.
  int asHex(int character) {
    assert(character.isHex);
    return switch (character) {
      // dart-lang/sdk#52740
      // ignore: non_constant_relational_pattern_expression
      <= $9 => character - $0,
      // ignore: non_constant_relational_pattern_expression
      <= $F => 10 + character - $A,
      _ => 10 + character - $a,
    };
  }
  
  /// Returns the hexadecimal digit for [number].
  ///
  /// Assumes that [number] is less than 16.
  int hexCharFor(int number) {
    assert(number < 0x10);
    return number < 0xA ? $0 + number : $a - 0xA + number;
  }
  
  /// Returns the value of [character] as a decimal digit.
  ///
  /// Assumes that [character] is a decimal digit.
  int asDecimal(int character) {
    assert(character >= $0 && character <= $9);
    return character - $0;
  }
  
  /// Returns the decimal digit for [number].
  ///
  /// Assumes that [number] is less than 10.
  int decimalCharFor(int number) {
    assert(number < 10, "Expected $number to be a digit from 0 to 9.");
    return $0 + number;
  }
  
  /// Assumes that [character] is a left-hand brace-like character, and returns
  /// the right-hand version.
  int opposite(int character) => switch (character) {
        $lparen => $rparen,
        $lbrace => $rbrace,
        $lbracket => $rbracket,
        _ => throw ArgumentError(
            '"${String.fromCharCode(character)}" isn\'t a brace-like character.',
          ),
      };
  
  /// Returns [character], converted to upper case if it's an ASCII lowercase
  /// letter.
  int toUpperCase(int character) => (character >= $a && character <= $z)
      ? character & ~_asciiCaseBit
      : character;
  
  /// Returns [character], converted to lower case if it's an ASCII uppercase
  /// letter.
  int toLowerCase(int character) => (character >= $A && character <= $Z)
      ? character | _asciiCaseBit
      : character;
  
  /// Returns whether [character1] and [character2] are the same, modulo ASCII case.
  bool characterEqualsIgnoreCase(int character1, int character2) {
    if (character1 == character2) return true;
  
    // If this check fails, the characters are definitely different. If it
    // succeeds *and* either character is an ASCII letter, they're equivalent.
    if (character1 ^ character2 != _asciiCaseBit) return false;
  
    // Now we just need to verify that one of the characters is an ASCII letter.
    var upperCase1 = character1 & ~_asciiCaseBit;
    return upperCase1 >= $A && upperCase1 <= $Z;
  }
  
  /// Like [characterEqualsIgnoreCase], but optimized for the fact that [letter]
  /// is known to be a lowercase ASCII letter.
  bool equalsLetterIgnoreCase(int letter, int actual) {
    assert(letter >= $a && letter <= $z);
    return (actual | _asciiCaseBit) == letter;
  }
  