Skip to content

Commit 713f552

Browse files
author
ladeak
committed
Using Rune to encode/decode non ascii characters
1 parent f322930 commit 713f552

File tree

1 file changed

+22
-39
lines changed

1 file changed

+22
-39
lines changed

src/Http/Headers/src/ContentDispositionHeaderValue.cs

Lines changed: 22 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public class ContentDispositionHeaderValue
2929
private const string ReadDateString = "read-date";
3030
private const string SizeString = "size";
3131
private const int MaxStackAllocSizeBytes = 256;
32-
private const int MaxStackAllocSizeChars = MaxStackAllocSizeBytes / 2;
3332
private static readonly char[] QuestionMark = new char[] { '?' };
3433
private static readonly char[] SingleQuote = new char[] { '\'' };
3534
private static readonly char[] EscapeChars = new char[] { '\\', '"' };
@@ -38,8 +37,8 @@ public class ContentDispositionHeaderValue
3837

3938
// attr-char definition from RFC5987
4039
// Same as token except ( "*" / "'" / "%" )
41-
private static readonly SearchValues<byte> Rfc5987AttrChar =
42-
SearchValues.Create("!#$&+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~"u8);
40+
private static readonly SearchValues<char> Rfc5987AttrChar =
41+
SearchValues.Create("!#$&+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~");
4342

4443
private static readonly HttpHeaderParser<ContentDispositionHeaderValue> Parser
4544
= new GenericHeaderParser<ContentDispositionHeaderValue>(false, GetDispositionTypeLength);
@@ -619,58 +618,42 @@ private static bool TryDecodeMime(StringSegment input, [NotNullWhen(true)] out s
619618
private static string Encode5987(StringSegment input)
620619
{
621620
var builder = new StringBuilder("UTF-8\'\'");
622-
623-
var maxInputBytes = Encoding.UTF8.GetMaxByteCount(input.Length);
624-
byte[]? bufferFromPool = null;
625-
Span<byte> inputBytes = maxInputBytes <= MaxStackAllocSizeBytes
626-
? stackalloc byte[MaxStackAllocSizeBytes]
627-
: bufferFromPool = ArrayPool<byte>.Shared.Rent(maxInputBytes);
628-
629-
char[]? charBufferFromPool = null;
630-
Span<char> tempCharBuffer = input.Length <= MaxStackAllocSizeChars
631-
? stackalloc char[MaxStackAllocSizeChars]
632-
: charBufferFromPool = ArrayPool<char>.Shared.Rent(input.Length);
633-
634-
var bytesWritten = Encoding.UTF8.GetBytes(input, inputBytes);
635-
inputBytes = inputBytes[..bytesWritten];
636-
637-
while (inputBytes.Length > 0)
621+
Span<byte> utf8CharBuffer = stackalloc byte[4];
622+
var remaining = input.AsSpan();
623+
while (remaining.Length > 0)
638624
{
639-
var length = inputBytes.IndexOfAnyExcept(Rfc5987AttrChar);
625+
var length = remaining.IndexOfAnyExcept(Rfc5987AttrChar);
640626
if (length < 0)
641627
{
642-
length = inputBytes.Length;
628+
length = remaining.Length;
643629
}
644-
int asciiCharCount = Encoding.ASCII.GetChars(inputBytes.Slice(0, length), tempCharBuffer);
645-
builder.Append(tempCharBuffer[..asciiCharCount]);
630+
builder.Append(remaining[..length]);
646631

647-
inputBytes = inputBytes.Slice(length);
648-
if (inputBytes.Length == 0)
632+
remaining = remaining.Slice(length);
633+
if (remaining.Length == 0)
649634
{
650635
break;
651636
}
652637

653-
length = inputBytes.IndexOfAny(Rfc5987AttrChar);
638+
length = remaining.IndexOfAny(Rfc5987AttrChar);
654639
if (length < 0)
655640
{
656-
length = inputBytes.Length;
641+
length = remaining.Length;
657642
}
658643

659-
for (int i = 0; i < length; i++)
644+
for (int i = 0; i < length;)
660645
{
661-
HexEscape(builder, inputBytes[i]);
662-
}
663-
664-
inputBytes = inputBytes.Slice(length);
665-
}
666-
667-
if (bufferFromPool is not null)
646+
Rune.DecodeFromUtf16(remaining.Slice(i), out Rune rune, out var runeLength);
647+
int utf8Length = rune.EncodeToUtf8(utf8CharBuffer);
648+
for (int j = 0; j < utf8Length; j++)
668649
{
669-
ArrayPool<byte>.Shared.Return(bufferFromPool);
650+
var utf8Part = utf8CharBuffer[j];
651+
builder.Append(CultureInfo.InvariantCulture, $"%{HexUpperChars[(utf8Part & 0xf0) >> 4]}{HexUpperChars[utf8Part & 0xf]}");
670652
}
671-
if (charBufferFromPool is not null)
672-
{
673-
ArrayPool<char>.Shared.Return(charBufferFromPool);
653+
i += runeLength;
654+
}
655+
656+
remaining = remaining.Slice(length);
674657
}
675658

676659
return builder.ToString();

0 commit comments

Comments
 (0)