// 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}");
}
}