// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System; using System.Diagnostics; using System.Globalization; using System.Runtime.CompilerServices; namespace SixLabors.Fonts.Unicode { /// /// Represents a Unicode value ([ U+0000..U+10FFFF ], inclusive). /// /// /// This type's constructors and conversion operators validate the input, so consumers can call the APIs /// assuming that the underlying instance is well-formed. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] public readonly struct CodePoint : IComparable, IComparable, IEquatable { // Supplementary plane code points are encoded as 2 UTF-16 code units private const int MaxUtf16CharsPerCodePoint = 2; // Supplementary plane code points are encoded as 4 UTF-8 code units internal const int MaxUtf8BytesPerCodePoint = 4; private const byte IsWhiteSpaceFlag = 0x80; private const byte IsLetterOrDigitFlag = 0x40; private const byte UnicodeCategoryMask = 0x1F; private readonly uint value; /// /// Initializes a new instance of the struct. /// /// The char representing the UTF-16 code unit /// /// If represents a UTF-16 surrogate code point /// U+D800..U+DFFF, inclusive. /// public CodePoint(char value) { uint expanded = value; if (UnicodeUtility.IsSurrogateCodePoint(expanded)) { ThrowArgumentOutOfRange(expanded, nameof(value), "Must not be in [ U+D800..U+DFFF ], inclusive."); } this.value = expanded; } /// /// Initializes a new instance of the struct. /// /// A char representing a UTF-16 high surrogate code unit. /// A char representing a UTF-16 low surrogate code unit. /// /// If does not represent a UTF-16 high surrogate code unit /// or does not represent a UTF-16 low surrogate code unit. /// public CodePoint(char highSurrogate, char lowSurrogate) : this((uint)char.ConvertToUtf32(highSurrogate, lowSurrogate), false) { } /// /// Initializes a new instance of the struct. /// /// The value to create the codepoint. /// /// If does not represent a value Unicode scalar value. /// public CodePoint(int value) : this((uint)value) { } /// /// Initializes a new instance of the struct. /// /// The value to create the codepoint. /// /// If does not represent a value Unicode scalar value. /// public CodePoint(uint value) { if (!IsValid(value)) { ThrowArgumentOutOfRange(value, nameof(value), "Must be in [ U+0000..U+10FFFF ], inclusive."); } this.value = value; } // Non-validating ctor #pragma warning disable IDE0060 // Remove unused parameter private CodePoint(uint scalarValue, bool unused) { UnicodeUtility.DebugAssertIsValidCodePoint(scalarValue); this.value = scalarValue; } #pragma warning restore IDE0060 // Remove unused parameter // Contains information about the ASCII character range [ U+0000..U+007F ], with: // - 0x80 bit if set means 'is whitespace' // - 0x40 bit if set means 'is letter or digit' // - 0x20 bit is reserved for future use // - bottom 5 bits are the UnicodeCategory of the character private static ReadOnlySpan AsciiCharInfo => [ 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x0E, 0x0E, // U+0000..U+000F 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, // U+0010..U+001F 0x8B, 0x18, 0x18, 0x18, 0x1A, 0x18, 0x18, 0x18, 0x14, 0x15, 0x18, 0x19, 0x18, 0x13, 0x18, 0x18, // U+0020..U+002F 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x18, 0x18, 0x19, 0x19, 0x19, 0x18, // U+0030..U+003F 0x18, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // U+0040..U+004F 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x14, 0x18, 0x15, 0x1B, 0x12, // U+0050..U+005F 0x1B, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, // U+0060..U+006F 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x14, 0x19, 0x15, 0x19, 0x0E, // U+0070..U+007F ]; /// /// Gets a value indicating whether this value is ASCII ([ U+0000..U+007F ]) /// and therefore representable by a single UTF-8 code unit. /// public bool IsAscii => UnicodeUtility.IsAsciiCodePoint(this.value); /// /// Gets a value indicating whether this value is within the BMP ([ U+0000..U+FFFF ]) /// and therefore representable by a single UTF-16 code unit. /// public bool IsBmp => UnicodeUtility.IsBmpCodePoint(this.value); /// /// Gets the Unicode plane (0 to 16, inclusive) which contains this scalar. /// public int Plane => UnicodeUtility.GetPlane(this.value); // Displayed as "'' (U+XXXX)"; e.g., "'e' (U+0065)" private string DebuggerDisplay => FormattableString.Invariant($"U+{this.value:X4} '{(IsValid(this.value) ? this.ToString() : "\uFFFD")}'"); /// /// Gets the Unicode value as an integer. /// public int Value => (int)this.value; /// /// Gets the length in code units () of the /// UTF-16 sequence required to represent this scalar value. /// /// /// The return value will be 1 or 2. /// public int Utf16SequenceLength { get { int codeUnitCount = UnicodeUtility.GetUtf16SequenceLength(this.value); Debug.Assert(codeUnitCount is > 0 and <= MaxUtf16CharsPerCodePoint, $"Invalid Utf16SequenceLength {codeUnitCount}."); return codeUnitCount; } } /// /// Gets the length in code units of the /// UTF-8 sequence required to represent this scalar value. /// /// /// The return value will be 1 through 4, inclusive. /// public int Utf8SequenceLength { get { int codeUnitCount = UnicodeUtility.GetUtf8SequenceLength(this.value); Debug.Assert(codeUnitCount is > 0 and <= MaxUtf8BytesPerCodePoint, $"Invalid Utf8SequenceLength {codeUnitCount}."); return codeUnitCount; } } /// /// Gets a instance that represents the Unicode replacement character U+FFFD. /// public static CodePoint ReplacementChar { get; } = new CodePoint(0xFFFD); /// /// Gets a instance that represents the Unicode object replacement character U+FFFC. /// public static CodePoint ObjectReplacementChar { get; } = new CodePoint(0xFFFC); #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member // Operators below are explicit because they may throw. public static explicit operator CodePoint(char ch) => new(ch); public static explicit operator CodePoint(uint value) => new(value); public static explicit operator CodePoint(int value) => new(value); public static bool operator ==(CodePoint left, CodePoint right) => left.value == right.value; public static bool operator !=(CodePoint left, CodePoint right) => left.value != right.value; public static bool operator <(CodePoint left, CodePoint right) => left.value < right.value; public static bool operator <=(CodePoint left, CodePoint right) => left.value <= right.value; public static bool operator >(CodePoint left, CodePoint right) => left.value > right.value; public static bool operator >=(CodePoint left, CodePoint right) => left.value >= right.value; #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member /// /// Returns if is a valid Unicode code /// point, i.e., is in [ U+0000..U+10FFFF ], inclusive. /// /// The value to evaluate. /// if represents a valid codepoint; otherwise, public static bool IsValid(int value) => IsValid((uint)value); /// /// Returns if is a valid Unicode code /// point, i.e., is in [ U+0000..U+10FFFF ], inclusive. /// /// The value to evaluate. /// if represents a valid codepoint; otherwise, public static bool IsValid(uint value) => UnicodeUtility.IsValidCodePoint(value); /// /// Gets a value indicating whether the given codepoint is white space. /// /// The codepoint to evaluate. /// if is a whitespace character; otherwise, public static bool IsWhiteSpace(in CodePoint codePoint) { if (codePoint.IsAscii) { return (AsciiCharInfo[codePoint.Value] & IsWhiteSpaceFlag) != 0; } // Only BMP code points can be white space, so only call into char // if the incoming value is within the BMP. return codePoint.IsBmp && char.IsWhiteSpace((char)codePoint.Value); } /// /// Gets a value indicating whether the given codepoint is a non-breaking space. /// /// The codepoint to evaluate. /// if is a non-breaking space character; otherwise, public static bool IsNonBreakingSpace(in CodePoint codePoint) => codePoint.Value == 0x00A0; /// /// Gets a value indicating whether the given codepoint is a zero-width-non-joiner. /// /// The codepoint to evaluate. /// if is a zero-width-non-joiner character; otherwise, public static bool IsZeroWidthNonJoiner(in CodePoint codePoint) => codePoint.Value == 0x200C; /// /// Gets a value indicating whether the given codepoint is a zero-width-joiner. /// /// The codepoint to evaluate. /// if is a zero-width-joiner character; otherwise, public static bool IsZeroWidthJoiner(in CodePoint codePoint) => codePoint.Value == 0x200D; /// /// Gets a value indicating whether the given codepoint is a variation selector. /// /// /// The codepoint to evaluate. /// if is a variation selector character; otherwise, public static bool IsVariationSelector(in CodePoint codePoint) => (codePoint.Value & 0xFFF0) == 0xFE00; /// /// Gets a value indicating whether the given codepoint is a control character. /// /// The codepoint to evaluate. /// if is a control character; otherwise, public static bool IsControl(in CodePoint codePoint) => // Per the Unicode stability policy, the set of control characters // is forever fixed at [ U+0000..U+001F ], [ U+007F..U+009F ]. No // characters will ever be added to or removed from the "control characters" // group. See https://www.unicode.org/policies/stability_policy.html. // // Logic below depends on CodePoint.Value never being -1 (since CodePoint is a validating type) // 00..1F (+1) => 01..20 (&~80) => 01..20 // 7F..9F (+1) => 80..A0 (&~80) => 00..20 ((codePoint.value + 1) & ~0x80u) <= 0x20u; /// /// Returns a value that indicates whether the specified codepoint is categorized as a decimal digit. /// /// The codepoint to evaluate. /// if is a decimal digit; otherwise, public static bool IsDigit(in CodePoint codePoint) { if (codePoint.IsAscii) { return UnicodeUtility.IsInRangeInclusive(codePoint.value, '0', '9'); } else { return GetGeneralCategory(codePoint) == UnicodeCategory.DecimalDigitNumber; } } /// /// Returns a value that indicates whether the specified codepoint is categorized as a letter. /// /// The codepoint to evaluate. /// if is a letter; otherwise, public static bool IsLetter(in CodePoint codePoint) { if (codePoint.IsAscii) { return ((codePoint.value - 'A') & ~0x20u) <= 'Z' - 'A'; // [A-Za-z] } else { return IsCategoryLetter(GetGeneralCategory(codePoint)); } } /// /// Returns a value that indicates whether the specified codepoint is categorized as a letter or decimal digit. /// /// The codepoint to evaluate. /// if is a letter or decimal digit; otherwise, public static bool IsLetterOrDigit(in CodePoint codePoint) { if (codePoint.IsAscii) { return (AsciiCharInfo[codePoint.Value] & IsLetterOrDigitFlag) != 0; } else { return IsCategoryLetterOrDecimalDigit(GetGeneralCategory(codePoint)); } } /// /// Returns a value that indicates whether the specified codepoint is categorized as a lowercase letter. /// /// The codepoint to evaluate. /// if is a lowercase letter; otherwise, public static bool IsLower(in CodePoint codePoint) { if (codePoint.IsAscii) { return UnicodeUtility.IsInRangeInclusive(codePoint.value, 'a', 'z'); } else { return GetGeneralCategory(codePoint) == UnicodeCategory.LowercaseLetter; } } /// /// Returns a value that indicates whether the specified codepoint is categorized as a number. /// /// The codepoint to evaluate. /// if is a number; otherwise, public static bool IsNumber(in CodePoint codePoint) { if (codePoint.IsAscii) { return UnicodeUtility.IsInRangeInclusive(codePoint.value, '0', '9'); } else { return IsCategoryNumber(GetGeneralCategory(codePoint)); } } /// /// Returns a value that indicates whether the specified codepoint is categorized as punctuation. /// /// The codepoint to evaluate. /// if is punctuation; otherwise, public static bool IsPunctuation(in CodePoint codePoint) => IsCategoryPunctuation(GetGeneralCategory(codePoint)); /// /// Returns a value that indicates whether the specified codepoint is categorized as a separator. /// /// The codepoint to evaluate. /// if is a separator; otherwise, public static bool IsSeparator(in CodePoint codePoint) => IsCategorySeparator(GetGeneralCategory(codePoint)); /// /// Returns a value that indicates whether the specified codepoint is categorized as a symbol. /// /// The codepoint to evaluate. /// if is a symbol; otherwise, public static bool IsSymbol(in CodePoint codePoint) => IsCategorySymbol(GetGeneralCategory(codePoint)); /// /// Returns a value that indicates whether the specified codepoint is categorized as a mark. /// /// The codepoint to evaluate. /// if is a symbol; otherwise, public static bool IsMark(in CodePoint codePoint) => IsCategoryMark(GetGeneralCategory(codePoint)); /// /// Returns a value that indicates whether the specified codepoint is categorized as an uppercase letter. /// /// The codepoint to evaluate. /// if is a uppercase letter; otherwise, public static bool IsUpper(in CodePoint codePoint) { if (codePoint.IsAscii) { return UnicodeUtility.IsInRangeInclusive(codePoint.value, 'A', 'Z'); } else { return GetGeneralCategory(codePoint) == UnicodeCategory.UppercaseLetter; } } /// /// Gets a value indicating whether the given codepoint is a tabulation indicator. /// /// The codepoint to evaluate. /// if is a tabulation indicator; otherwise, public static bool IsTabulation(in CodePoint codePoint) => codePoint.value == 0x0009; /// /// Gets a value indicating whether the given codepoint is a new line indicator. /// /// The codepoint to evaluate. /// if is a new line indicator; otherwise, public static bool IsNewLine(in CodePoint codePoint) => codePoint.Value switch { // See https://www.unicode.org/standard/reports/tr13/tr13-5.html 0x000A // LINE FEED (LF) or 0x000B // LINE TABULATION (VT) or 0x000C // FORM FEED (FF) or 0x000D // CARRIAGE RETURN (CR) or 0x0085 // NEXT LINE (NEL) or 0x2028 // LINE SEPARATOR (LS) or 0x2029 => true, // PARAGRAPH SEPARATOR (PS) _ => false, }; /// /// Returns the number of codepoints in a given string buffer. /// /// The source buffer to parse. /// The count. public static int GetCodePointCount(ReadOnlySpan source) { if (source.IsEmpty) { return 0; } int count = 0; SpanCodePointEnumerator enumerator = new(source); while (enumerator.MoveNext()) { count++; } return count; } /// /// Gets the canonical representation of a given codepoint. /// /// /// The code point to be mapped. /// The mapped canonical code point, or the passed . [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static CodePoint GetCanonicalType(in CodePoint codePoint) { if (codePoint.Value == 0x3008) { return new CodePoint(0x2329); } if (codePoint.Value == 0x3009) { return new CodePoint(0x232A); } return codePoint; } /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . public static BidiClass GetBidiClass(in CodePoint codePoint) => new(codePoint); /// /// Gets the codepoint representing the bidi mirror for this instance. /// /// /// The code point to be mapped. /// /// When this method returns, contains the codepoint representing the bidi mirror for this instance; /// otherwise, the default value for the type of the parameter. /// This parameter is passed uninitialized. /// . /// if this instance has a mirror; otherwise, [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool TryGetBidiMirror(in CodePoint codePoint, out CodePoint mirror) { uint value = UnicodeData.GetBidiMirror(codePoint.value); if (value == 0u) { mirror = default; return false; } mirror = new CodePoint(value); return true; } /// /// Gets the codepoint representing the vertical mirror for this instance. /// /// /// The code point to be mapped. /// /// When this method returns, contains the codepoint representing the vertical mirror for this instance; /// otherwise, the default value for the type of the parameter. /// This parameter is passed uninitialized. /// . /// if this instance has a mirror; otherwise, public static bool TryGetVerticalMirror(in CodePoint codePoint, out CodePoint mirror) { uint value = UnicodeUtility.GetVerticalMirror((uint)codePoint.Value); if (value == 0u) { mirror = default; return false; } mirror = new CodePoint(value); return true; } /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . public static LineBreakClass GetLineBreakClass(in CodePoint codePoint) => UnicodeData.GetLineBreakClass(codePoint.value); /// /// Gets the for the given codepoint. /// /// The codepoint. /// The . public static WordBreakClass GetWordBreakClass(in CodePoint codePoint) => UnicodeData.GetWordBreakClass(codePoint.value); /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . public static GraphemeClusterClass GetGraphemeClusterClass(in CodePoint codePoint) => UnicodeData.GetGraphemeClusterClass(codePoint.value); /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . public static VerticalOrientationType GetVerticalOrientationType(in CodePoint codePoint) => UnicodeData.GetVerticalOrientation(codePoint.value); /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . /// /// This returns the Unicode East_Asian_Width property value from UAX #11. It does /// not resolve context-sensitive display-cell width; for example, /// may resolve as narrow or wide /// depending on language, script, source encoding, font, or explicit markup. /// public static EastAsianWidthClass GetEastAsianWidthClass(in CodePoint codePoint) => UnicodeData.GetEastAsianWidthClass(codePoint.value); /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . public static EmojiProperties GetEmojiProperties(in CodePoint codePoint) => UnicodeData.GetEmojiProperties(codePoint.value); /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . public static ArabicJoiningClass GetArabicJoiningClass(in CodePoint codePoint) => new(codePoint); /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . public static ScriptClass GetScriptClass(in CodePoint codePoint) => UnicodeData.GetScriptClass(codePoint.value); /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . public static IndicConjunctBreakClass GetIndicConjunctBreakClass(in CodePoint codePoint) => UnicodeData.GetIndicConjunctBreakClass(codePoint.value); /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . public static IndicSyllabicCategory GetIndicSyllabicCategory(in CodePoint codePoint) => UnicodeData.GetIndicSyllabicCategory(codePoint.value); /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . public static IndicPositionalCategory GetIndicPositionalCategory(in CodePoint codePoint) => UnicodeData.GetIndicPositionalCategory(codePoint.value); /// /// Gets the for the given codepoint. /// /// The codepoint to evaluate. /// The . public static UnicodeCategory GetGeneralCategory(in CodePoint codePoint) { if (codePoint.IsAscii) { return (UnicodeCategory)(AsciiCharInfo[codePoint.Value] & UnicodeCategoryMask); } return UnicodeData.GetUnicodeCategory(codePoint.value); } /// /// Reads the at specified position. /// /// The text to read from. /// The index to read at. /// The count of chars consumed reading the buffer. /// The . internal static CodePoint ReadAt(string text, int index, out int charsConsumed) => DecodeFromUtf16At(text.AsMemory().Span, index, out charsConsumed); /// /// Decodes the from the provided UTF-16 source buffer at the specified position. /// /// The buffer to read from. /// The index to read at. /// The . internal static CodePoint DecodeFromUtf16At(ReadOnlySpan source, int index) => DecodeFromUtf16At(source, index, out int _); /// /// Decodes the from the provided UTF-16 source buffer at the specified position. /// /// The buffer to read from. /// The index to read at. /// The count of chars consumed reading the buffer. /// The . internal static CodePoint DecodeFromUtf16At(ReadOnlySpan source, int index, out int charsConsumed) { if (index >= source.Length) { charsConsumed = 0; return default; } // Optimistically assume input is within BMP. charsConsumed = 1; uint code = source[index]; // High surrogate if (UnicodeUtility.IsHighSurrogateCodePoint(code)) { uint hi, low; hi = code; index++; if (index == source.Length) { return ReplacementChar; } low = source[index]; if (UnicodeUtility.IsLowSurrogateCodePoint(low)) { charsConsumed = 2; return new CodePoint(UnicodeUtility.GetScalarFromUtf16SurrogatePair(hi, low)); } return ReplacementChar; } if (UnicodeUtility.IsLowSurrogateCodePoint(code)) { return ReplacementChar; } return new CodePoint(code); } /// int IComparable.CompareTo(object? obj) { if (obj is null) { return 1; // non-null ("this") always sorts after null } if (obj is CodePoint other) { return this.CompareTo(other); } throw new ArgumentException("Object must be of type CodePoint."); } /// public int CompareTo(CodePoint other) // Values don't span entire 32-bit domain so won't integer overflow. => this.Value - other.Value; /// public override bool Equals(object? obj) => obj is CodePoint point && this.Equals(point); /// public bool Equals(CodePoint other) => this.value == other.value; /// public override int GetHashCode() => HashCode.Combine(this.value); /// public override string ToString() { if (this.IsBmp) { return ((char)this.value).ToString(); } else { Span buffer = stackalloc char[MaxUtf16CharsPerCodePoint]; UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneCodePoint(this.value, out buffer[0], out buffer[1]); return buffer.ToString(); } } /// /// Returns this instance displayed as "'<char>' (U+XXXX)"; e.g., "'e' (U+0065)" /// /// The . internal string ToDebuggerDisplay() => this.DebuggerDisplay; // Returns true if this Unicode category represents a letter private static bool IsCategoryLetter(UnicodeCategory category) => UnicodeUtility.IsInRangeInclusive((uint)category, (uint)UnicodeCategory.UppercaseLetter, (uint)UnicodeCategory.OtherLetter); // Returns true if this Unicode category represents a letter or a decimal digit private static bool IsCategoryLetterOrDecimalDigit(UnicodeCategory category) => UnicodeUtility.IsInRangeInclusive((uint)category, (uint)UnicodeCategory.UppercaseLetter, (uint)UnicodeCategory.OtherLetter) || (category == UnicodeCategory.DecimalDigitNumber); // Returns true if this Unicode category represents a number private static bool IsCategoryNumber(UnicodeCategory category) => UnicodeUtility.IsInRangeInclusive((uint)category, (uint)UnicodeCategory.DecimalDigitNumber, (uint)UnicodeCategory.OtherNumber); // Returns true if this Unicode category represents a punctuation mark private static bool IsCategoryPunctuation(UnicodeCategory category) => UnicodeUtility.IsInRangeInclusive((uint)category, (uint)UnicodeCategory.ConnectorPunctuation, (uint)UnicodeCategory.OtherPunctuation); // Returns true if this Unicode category represents a separator private static bool IsCategorySeparator(UnicodeCategory category) => UnicodeUtility.IsInRangeInclusive((uint)category, (uint)UnicodeCategory.SpaceSeparator, (uint)UnicodeCategory.ParagraphSeparator); // Returns true if this Unicode category represents a symbol private static bool IsCategorySymbol(UnicodeCategory category) => UnicodeUtility.IsInRangeInclusive((uint)category, (uint)UnicodeCategory.MathSymbol, (uint)UnicodeCategory.OtherSymbol); // Returns true if this Unicode category represents a mark private static bool IsCategoryMark(UnicodeCategory category) => UnicodeUtility.IsInRangeInclusive((uint)category, (uint)UnicodeCategory.NonSpacingMark, (uint)UnicodeCategory.EnclosingMark); [MethodImpl(MethodImplOptions.NoInlining)] private static void ThrowArgumentOutOfRange(uint value, string paramName, string message) => throw new ArgumentOutOfRangeException(paramName, $"The value {UnicodeUtility.ToHexString(value)} is not a valid Unicode code point value. {message}"); } }