internal static void EscapeAsciiChar(byte b, ref ValueStringBuilder to) { to.Append('%'); HexConverter.ToCharsBuffer(b, to.AppendSpan(2), 0, HexConverter.Casing.Upper); }
internal static bool IsHexDigit(char character) => HexConverter.IsHexChar(character);
private static void EscapeStringToBuilder( ReadOnlySpan <char> stringToEscape, ref ValueStringBuilder vsb, ReadOnlySpan <bool> noEscape, bool checkExistingEscaped) { // Allocate enough stack space to hold any Rune's UTF8 encoding. Span <byte> utf8Bytes = stackalloc byte[4]; // Then enumerate every rune in the input. SpanRuneEnumerator e = stringToEscape.EnumerateRunes(); while (e.MoveNext()) { Rune r = e.Current; if (!r.IsAscii) { // The rune is non-ASCII, so encode it as UTF8, and escape each UTF8 byte. r.TryEncodeToUtf8(utf8Bytes, out int bytesWritten); foreach (byte b in utf8Bytes.Slice(0, bytesWritten)) { vsb.Append('%'); HexConverter.ToCharsBuffer(b, vsb.AppendSpan(2), 0, HexConverter.Casing.Upper); } continue; } // If the value doesn't need to be escaped, append it and continue. byte value = (byte)r.Value; if (noEscape[value]) { vsb.Append((char)value); continue; } // If we're checking for existing escape sequences, then if this is the beginning of // one, check the next two characters in the sequence. This is a little tricky to do // as we're using an enumerator, but luckily it's a ref struct-based enumerator: we can // make a copy and iterate through the copy without impacting the original, and then only // push the original ahead if we find what we're looking for in the copy. if (checkExistingEscaped && value == '%') { // If the next two characters are valid escaped ASCII, then just output them as-is. SpanRuneEnumerator tmpEnumerator = e; if (tmpEnumerator.MoveNext()) { Rune r1 = tmpEnumerator.Current; if (r1.IsAscii && IsHexDigit((char)r1.Value) && tmpEnumerator.MoveNext()) { Rune r2 = tmpEnumerator.Current; if (r2.IsAscii && IsHexDigit((char)r2.Value)) { vsb.Append('%'); vsb.Append((char)r1.Value); vsb.Append((char)r2.Value); e = tmpEnumerator; continue; } } } } // Otherwise, append the escaped character. vsb.Append('%'); HexConverter.ToCharsBuffer(value, vsb.AppendSpan(2), 0, HexConverter.Casing.Upper); } }