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

182 lines
4.9 KiB
C#

using System.Buffers;
using System.Numerics;
using System.Text.Unicode;
namespace System.Text.Encodings.Web;
internal sealed class DefaultHtmlEncoder : HtmlEncoder
{
private sealed class EscaperImplementation : ScalarEscaperBase
{
internal static readonly EscaperImplementation Singleton = new EscaperImplementation();
private EscaperImplementation()
{
}
internal override int EncodeUtf8(Rune value, Span<byte> destination)
{
if (value.Value == 60)
{
if (SpanUtility.TryWriteBytes(destination, 38, 108, 116, 59))
{
return 4;
}
}
else if (value.Value == 62)
{
if (SpanUtility.TryWriteBytes(destination, 38, 103, 116, 59))
{
return 4;
}
}
else if (value.Value == 38)
{
if (SpanUtility.TryWriteBytes(destination, 38, 97, 109, 112, 59))
{
return 5;
}
}
else
{
if (value.Value != 34)
{
return TryEncodeScalarAsHex(this, (uint)value.Value, destination);
}
if (SpanUtility.TryWriteBytes(destination, 38, 113, 117, 111, 116, 59))
{
return 6;
}
}
return -1;
static int TryEncodeScalarAsHex(object @this, uint scalarValue, Span<byte> span)
{
int num = (int)((uint)BitOperations.Log2(scalarValue) / 4u + 4);
if (SpanUtility.IsValidIndex(span, num))
{
span[num] = 59;
SpanUtility.TryWriteBytes(span, 38, 35, 120, 48);
span = span.Slice(3, num - 3);
int num2 = span.Length - 1;
while (SpanUtility.IsValidIndex(span, num2))
{
char c = System.HexConverter.ToCharUpper((int)scalarValue);
span[num2] = (byte)c;
scalarValue >>= 4;
num2--;
}
return span.Length + 4;
}
return -1;
}
}
internal override int EncodeUtf16(Rune value, Span<char> destination)
{
if (value.Value == 60)
{
if (SpanUtility.TryWriteChars(destination, '&', 'l', 't', ';'))
{
return 4;
}
}
else if (value.Value == 62)
{
if (SpanUtility.TryWriteChars(destination, '&', 'g', 't', ';'))
{
return 4;
}
}
else if (value.Value == 38)
{
if (SpanUtility.TryWriteChars(destination, '&', 'a', 'm', 'p', ';'))
{
return 5;
}
}
else
{
if (value.Value != 34)
{
return TryEncodeScalarAsHex(this, (uint)value.Value, destination);
}
if (SpanUtility.TryWriteChars(destination, '&', 'q', 'u', 'o', 't', ';'))
{
return 6;
}
}
return -1;
static int TryEncodeScalarAsHex(object @this, uint scalarValue, Span<char> span)
{
int num = (int)((uint)BitOperations.Log2(scalarValue) / 4u + 4);
if (SpanUtility.IsValidIndex(span, num))
{
span[num] = ';';
SpanUtility.TryWriteChars(span, '&', '#', 'x', '0');
span = span.Slice(3, num - 3);
int num2 = span.Length - 1;
while (SpanUtility.IsValidIndex(span, num2))
{
char c = System.HexConverter.ToCharUpper((int)scalarValue);
span[num2] = c;
scalarValue >>= 4;
num2--;
}
return span.Length + 4;
}
return -1;
}
}
}
internal static readonly DefaultHtmlEncoder BasicLatinSingleton = new DefaultHtmlEncoder(new TextEncoderSettings(UnicodeRanges.BasicLatin));
private readonly OptimizedInboxTextEncoder _innerEncoder;
public override int MaxOutputCharactersPerInputCharacter => 8;
internal DefaultHtmlEncoder(TextEncoderSettings settings)
{
if (settings == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.settings);
}
_innerEncoder = new OptimizedInboxTextEncoder(EscaperImplementation.Singleton, in settings.GetAllowedCodePointsBitmap());
}
private protected override OperationStatus EncodeCore(ReadOnlySpan<char> source, Span<char> destination, out int charsConsumed, out int charsWritten, bool isFinalBlock)
{
return _innerEncoder.Encode(source, destination, out charsConsumed, out charsWritten, isFinalBlock);
}
private protected override OperationStatus EncodeUtf8Core(ReadOnlySpan<byte> utf8Source, Span<byte> utf8Destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock)
{
return _innerEncoder.EncodeUtf8(utf8Source, utf8Destination, out bytesConsumed, out bytesWritten, isFinalBlock);
}
private protected override int FindFirstCharacterToEncode(ReadOnlySpan<char> text)
{
return _innerEncoder.GetIndexOfFirstCharToEncode(text);
}
public unsafe override int FindFirstCharacterToEncode(char* text, int textLength)
{
return _innerEncoder.FindFirstCharacterToEncode(text, textLength);
}
public override int FindFirstCharacterToEncodeUtf8(ReadOnlySpan<byte> utf8Text)
{
return _innerEncoder.GetIndexOfFirstByteToEncode(utf8Text);
}
public unsafe override bool TryEncodeUnicodeScalar(int unicodeScalar, char* buffer, int bufferLength, out int numberOfCharactersWritten)
{
return _innerEncoder.TryEncodeUnicodeScalar(unicodeScalar, buffer, bufferLength, out numberOfCharactersWritten);
}
public override bool WillEncode(int unicodeScalar)
{
return !_innerEncoder.IsScalarValueAllowed(new Rune(unicodeScalar));
}
}