public bool MoveNext() { while (true) { if (_scalars.MoveNext()) { Rune scalar = _scalars.Current; byte lbProperty = GetLineBreakProperty(scalar.Value); byte newState; if (_posInScalars == 0) { _state = newState = lbProperty; } else { int i = _state * LinebreakCategoryCount + lbProperty; newState = LinebreakStateMachine[i]; } int pos = _posInScalars++; _lenInScalars++; if ((sbyte)newState < 0) { _state = (byte)(newState & 0x3F); SetCurrent(pos, newState); return(true); } _state = newState; } else { if (_posInScalars == _lenInScalars) { _posInScalars++; int i = _state * LinebreakCategoryCount; byte state = LinebreakStateMachine[i]; SetCurrent(_lenInScalars, state); return(true); } return(false); } } }
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); } }