Files
2026-08-27 10:56:38 -06:00

299 lines
6.5 KiB
C#

using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Text.Encodings.Web;
namespace System.Text;
internal readonly struct Rune : IEquatable<Rune>
{
private const int MaxUtf16CharsPerRune = 2;
private const char HighSurrogateStart = '\ud800';
private const char LowSurrogateStart = '\udc00';
private const int HighSurrogateRange = 1023;
private readonly uint _value;
public bool IsAscii => System.Text.UnicodeUtility.IsAsciiCodePoint(_value);
public bool IsBmp => System.Text.UnicodeUtility.IsBmpCodePoint(_value);
public static Rune ReplacementChar => UnsafeCreate(65533u);
public int Utf16SequenceLength => System.Text.UnicodeUtility.GetUtf16SequenceLength(_value);
public int Value => (int)_value;
public Rune(uint value)
{
if (!System.Text.UnicodeUtility.IsValidUnicodeScalar(value))
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.value);
}
_value = value;
}
public Rune(int value)
: this((uint)value)
{
}
private Rune(uint scalarValue, bool _)
{
_value = scalarValue;
}
public static bool operator ==(Rune left, Rune right)
{
return left._value == right._value;
}
public static bool operator !=(Rune left, Rune right)
{
return left._value != right._value;
}
public static bool IsControl(Rune value)
{
return ((value._value + 1) & 0xFFFFFF7Fu) <= 32;
}
public static OperationStatus DecodeFromUtf16(ReadOnlySpan<char> source, out Rune result, out int charsConsumed)
{
if (!source.IsEmpty)
{
char c = source[0];
if (TryCreate(c, out result))
{
charsConsumed = 1;
return OperationStatus.Done;
}
if (1u < (uint)source.Length)
{
char lowSurrogate = source[1];
if (TryCreate(c, lowSurrogate, out result))
{
charsConsumed = 2;
return OperationStatus.Done;
}
}
else if (char.IsHighSurrogate(c))
{
goto IL_004c;
}
charsConsumed = 1;
result = ReplacementChar;
return OperationStatus.InvalidData;
}
goto IL_004c;
IL_004c:
charsConsumed = source.Length;
result = ReplacementChar;
return OperationStatus.NeedMoreData;
}
public static OperationStatus DecodeFromUtf8(ReadOnlySpan<byte> source, out Rune result, out int bytesConsumed)
{
int num = 0;
uint num2;
if ((uint)num < (uint)source.Length)
{
num2 = source[num];
if (System.Text.UnicodeUtility.IsAsciiCodePoint(num2))
{
goto IL_0021;
}
if (System.Text.UnicodeUtility.IsInRangeInclusive(num2, 194u, 244u))
{
num2 = num2 - 194 << 6;
num++;
if ((uint)num >= (uint)source.Length)
{
goto IL_0163;
}
int num3 = (sbyte)source[num];
if (num3 < -64)
{
num2 += (uint)num3;
num2 += 128;
num2 += 128;
if (num2 < 2048)
{
goto IL_0021;
}
if (System.Text.UnicodeUtility.IsInRangeInclusive(num2, 2080u, 3343u) && !System.Text.UnicodeUtility.IsInRangeInclusive(num2, 2912u, 2943u) && !System.Text.UnicodeUtility.IsInRangeInclusive(num2, 3072u, 3087u))
{
num++;
if ((uint)num >= (uint)source.Length)
{
goto IL_0163;
}
num3 = (sbyte)source[num];
if (num3 < -64)
{
num2 <<= 6;
num2 += (uint)num3;
num2 += 128;
num2 -= 131072;
if (num2 > 65535)
{
num++;
if ((uint)num >= (uint)source.Length)
{
goto IL_0163;
}
num3 = (sbyte)source[num];
if (num3 >= -64)
{
goto IL_0153;
}
num2 <<= 6;
num2 += (uint)num3;
num2 += 128;
num2 -= 4194304;
}
goto IL_0021;
}
}
}
}
else
{
num = 1;
}
goto IL_0153;
}
goto IL_0163;
IL_0021:
bytesConsumed = num + 1;
result = UnsafeCreate(num2);
return OperationStatus.Done;
IL_0163:
bytesConsumed = num;
result = ReplacementChar;
return OperationStatus.NeedMoreData;
IL_0153:
bytesConsumed = num;
result = ReplacementChar;
return OperationStatus.InvalidData;
}
public override bool Equals([NotNullWhen(true)] object obj)
{
if (obj is Rune other)
{
return Equals(other);
}
return false;
}
public bool Equals(Rune other)
{
return this == other;
}
public override int GetHashCode()
{
return Value;
}
public static bool TryCreate(char ch, out Rune result)
{
if (!System.Text.UnicodeUtility.IsSurrogateCodePoint(ch))
{
result = UnsafeCreate(ch);
return true;
}
result = default(Rune);
return false;
}
public static bool TryCreate(char highSurrogate, char lowSurrogate, out Rune result)
{
uint num = (uint)(highSurrogate - 55296);
uint num2 = (uint)(lowSurrogate - 56320);
if ((num | num2) <= 1023)
{
result = UnsafeCreate((uint)((int)(num << 10) + (lowSurrogate - 56320) + 65536));
return true;
}
result = default(Rune);
return false;
}
public bool TryEncodeToUtf16(Span<char> destination, out int charsWritten)
{
if (destination.Length >= 1)
{
if (IsBmp)
{
destination[0] = (char)_value;
charsWritten = 1;
return true;
}
if (destination.Length >= 2)
{
System.Text.UnicodeUtility.GetUtf16SurrogatesFromSupplementaryPlaneScalar(_value, out destination[0], out destination[1]);
charsWritten = 2;
return true;
}
}
charsWritten = 0;
return false;
}
public bool TryEncodeToUtf8(Span<byte> destination, out int bytesWritten)
{
if (destination.Length >= 1)
{
if (IsAscii)
{
destination[0] = (byte)_value;
bytesWritten = 1;
return true;
}
if (destination.Length >= 2)
{
if (_value <= 2047)
{
destination[0] = (byte)(_value + 12288 >> 6);
destination[1] = (byte)((_value & 0x3F) + 128);
bytesWritten = 2;
return true;
}
if (destination.Length >= 3)
{
if (_value <= 65535)
{
destination[0] = (byte)(_value + 917504 >> 12);
destination[1] = (byte)(((_value & 0xFC0) >> 6) + 128);
destination[2] = (byte)((_value & 0x3F) + 128);
bytesWritten = 3;
return true;
}
if (destination.Length >= 4)
{
destination[0] = (byte)(_value + 62914560 >> 18);
destination[1] = (byte)(((_value & 0x3F000) >> 12) + 128);
destination[2] = (byte)(((_value & 0xFC0) >> 6) + 128);
destination[3] = (byte)((_value & 0x3F) + 128);
bytesWritten = 4;
return true;
}
}
}
}
bytesWritten = 0;
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Rune UnsafeCreate(uint scalarValue)
{
return new Rune(scalarValue, _: false);
}
}