using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Avalonia.Media.TextFormatting.Unicode
{
public readonly record struct Codepoint
{
private readonly uint _value;
///
/// The replacement codepoint that is used for non supported values.
///
public static readonly Codepoint ReplacementCodepoint = new Codepoint('\uFFFD');
public Codepoint(uint value)
{
_value = value;
}
///
/// Get the codepoint's value.
///
public uint Value => _value;
///
/// Gets the .
///
public GeneralCategory GeneralCategory => UnicodeData.GetGeneralCategory(_value);
///
/// Gets the .
///
public Script Script => UnicodeData.GetScript(_value);
///
/// Gets the .
///
public BidiClass BiDiClass => UnicodeData.GetBiDiClass(_value);
///
/// Gets the .
///
public BidiPairedBracketType PairedBracketType => UnicodeData.GetBiDiPairedBracketType(_value);
///
/// Gets the .
///
public LineBreakClass LineBreakClass => UnicodeData.GetLineBreakClass(_value);
///
/// Gets the .
///
public GraphemeBreakClass GraphemeBreakClass => UnicodeData.GetGraphemeClusterBreak(_value);
///
/// Determines whether this is a break char.
///
///
/// true if [is break character]; otherwise, false.
///
public bool IsBreakChar
{
get
{
switch (_value)
{
case '\u000A':
case '\u000B':
case '\u000C':
case '\u000D':
case '\u0085':
case '\u2028':
case '\u2029':
return true;
default:
return false;
}
}
}
///
/// Determines whether this is white space.
///
///
/// true if [is whitespace]; otherwise, false.
///
public bool IsWhiteSpace
{
get
{
switch (GeneralCategory)
{
case GeneralCategory.Control:
case GeneralCategory.NonspacingMark:
case GeneralCategory.Format:
case GeneralCategory.SpaceSeparator:
case GeneralCategory.SpacingMark:
return true;
}
return false;
}
}
///
/// 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(Codepoint codePoint)
{
if (codePoint._value == 0x3008)
{
return new Codepoint(0x2329);
}
if (codePoint._value == 0x3009)
{
return new Codepoint(0x232A);
}
return codePoint;
}
///
/// Gets the codepoint representing the bracket pairing for this instance.
///
///
/// When this method returns, contains the codepoint representing the bracket pairing for this instance;
/// otherwise, the default value for the type of the parameter.
/// This parameter is passed uninitialized.
/// .
/// if this instance has a bracket pairing; otherwise,
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetPairedBracket(out Codepoint codepoint)
{
if (PairedBracketType == BidiPairedBracketType.None)
{
codepoint = default;
return false;
}
codepoint = UnicodeData.GetBiDiPairedBracket(_value);
return true;
}
public static implicit operator int(Codepoint codepoint)
{
return (int)codepoint._value;
}
public static implicit operator uint(Codepoint codepoint)
{
return codepoint._value;
}
///
/// Reads the at specified position.
///
/// The buffer to read from.
/// The index to read at.
/// The count of character that were read.
///
public static Codepoint ReadAt(ReadOnlySpan text, int index, out int count)
{
count = 1;
if (index >= text.Length)
{
return ReplacementCodepoint;
}
var code = text[index];
ushort hi, low;
//# High surrogate
if (0xD800 <= code && code <= 0xDBFF)
{
hi = code;
if (index + 1 == text.Length)
{
return ReplacementCodepoint;
}
low = text[index + 1];
if (0xDC00 <= low && low <= 0xDFFF)
{
count = 2;
return new Codepoint((uint)((hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000));
}
return ReplacementCodepoint;
}
//# Low surrogate
if (0xDC00 <= code && code <= 0xDFFF)
{
if (index == 0)
{
return ReplacementCodepoint;
}
hi = text[index - 1];
low = code;
if (0xD800 <= hi && hi <= 0xDBFF)
{
count = 2;
return new Codepoint((uint)((hi - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000));
}
return ReplacementCodepoint;
}
return new Codepoint(code);
}
///
/// Returns if is between
/// and , inclusive.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsInRangeInclusive(Codepoint cp, uint lowerBound, uint upperBound)
=> (cp._value - lowerBound) <= (upperBound - lowerBound);
}
}