internal override int EncodeUtf16(Rune value, Span <char> destination) { uint utf8lsb = (uint)UnicodeHelpers.GetUtf8RepresentationForScalarValue((uint)value.Value); if (!SpanUtility.IsValidIndex(destination, 2)) { goto OutOfSpace; } destination[0] = '%'; HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 1); if ((utf8lsb >>= 8) == 0) { return(3); } // "%XX" if (!SpanUtility.IsValidIndex(destination, 5)) { goto OutOfSpace; } destination[3] = '%'; HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 4); if ((utf8lsb >>= 8) == 0) { return(6); } // "%XX%YY" if (!SpanUtility.IsValidIndex(destination, 8)) { goto OutOfSpace; } destination[6] = '%'; HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 7); if ((utf8lsb >>= 8) == 0) { return(9); } // "%XX%YY%ZZ" if (!SpanUtility.IsValidIndex(destination, 11)) { goto OutOfSpace; } destination[9] = '%'; HexConverter.ToCharsBuffer((byte)utf8lsb, destination, startingIndex: 10); return(12); // "%XX%YY%ZZ%WW" OutOfSpace: return(-1); }
// Writes a scalar value as a JavaScript-escaped character (or sequence of characters). // See ECMA-262, Sec. 7.8.4, and ECMA-404, Sec. 9 // https://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4 // https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf // // ECMA-262 allows encoding U+000B as "\v", but ECMA-404 does not. // Both ECMA-262 and ECMA-404 allow encoding U+002F SOLIDUS as "\/" // (in ECMA-262 this character is a NonEscape character); however, we // don't encode SOLIDUS by default unless the caller has provided an // explicit bitmap which does not contain it. In this case we'll assume // that the caller didn't want a SOLIDUS written to the output at all, // so it should be written using "\u002F" encoding. // HTML-specific characters (including apostrophe and quotes) will // be written out as numeric entities for defense-in-depth. internal override int EncodeUtf8(Rune value, Span <byte> destination) { if (_preescapedMap.TryLookup(value, out byte preescapedForm)) { if (!SpanUtility.IsValidIndex(destination, 1)) { goto OutOfSpace; } destination[0] = (byte)'\\'; destination[1] = preescapedForm; return(2); OutOfSpace: return(-1); } return(TryEncodeScalarAsHex(this, value, destination));
#pragma warning disable IDE0060 // 'this' taken explicitly to avoid argument shuffling by caller static int TryEncodeScalarAsHex(object @this, Rune value, Span <byte> destination) #pragma warning restore IDE0060 { if (value.IsBmp) { // Write 6 bytes: "\uXXXX" if (!SpanUtility.IsValidIndex(destination, 5)) { goto OutOfSpaceInner; } destination[0] = (byte)'\\'; destination[1] = (byte)'u'; HexConverter.ToBytesBuffer((byte)value.Value, destination, 4); HexConverter.ToBytesBuffer((byte)((uint)value.Value >> 8), destination, 2); return(6); } else { // Write 12 bytes: "\uXXXX\uYYYY" UnicodeHelpers.GetUtf16SurrogatePairFromAstralScalarValue((uint)value.Value, out char highSurrogate, out char lowSurrogate); if (!SpanUtility.IsValidIndex(destination, 11)) { goto OutOfSpaceInner; } destination[0] = (byte)'\\'; destination[1] = (byte)'u'; HexConverter.ToBytesBuffer((byte)highSurrogate, destination, 4); HexConverter.ToBytesBuffer((byte)((uint)highSurrogate >> 8), destination, 2); destination[6] = (byte)'\\'; destination[7] = (byte)'u'; HexConverter.ToBytesBuffer((byte)lowSurrogate, destination, 10); HexConverter.ToBytesBuffer((byte)((uint)lowSurrogate >> 8), destination, 8); return(12); } OutOfSpaceInner: return(-1); }
#pragma warning disable IDE0060 // 'this' taken explicitly to avoid argument shuffling by caller static int TryEncodeScalarAsHex(object @this, uint scalarValue, Span <byte> destination) #pragma warning restore IDE0060 { UnicodeDebug.AssertIsValidScalar(scalarValue); // See comments in the UTF-16 equivalent method later in this file. int idxOfSemicolon = (int)((uint)BitOperations.Log2(scalarValue) / 4) + 4; Debug.Assert(4 <= idxOfSemicolon && idxOfSemicolon <= 9, "Expected '�'..''."); if (!SpanUtility.IsValidIndex(destination, idxOfSemicolon)) { goto OutOfSpaceInner; } destination[idxOfSemicolon] = (byte)';'; if (!SpanUtility.TryWriteBytes(destination, (byte)'&', (byte)'#', (byte)'x', (byte)'0')) { Debug.Fail("We should've had enough room to write 4 bytes."); } destination = destination.Slice(3, idxOfSemicolon - 3); for (int i = destination.Length - 1; SpanUtility.IsValidIndex(destination, i); i--) { char asUpperHex = HexConverter.ToCharUpper((int)scalarValue); destination[i] = (byte)asUpperHex; scalarValue >>= 4; // write a nibble - not a byte - at a time } return(destination.Length + 4); OutOfSpaceInner: return(-1); }
public void IsValidIndex_Span(string inputData, int index, bool expectedValue) { Span <char> span = inputData.ToCharArray(); Assert.Equal(expectedValue, SpanUtility.IsValidIndex(span, index)); }
public void IsValidIndex_ReadOnlySpan(string inputData, int index, bool expectedValue) { ReadOnlySpan <char> span = inputData.AsSpan(); Assert.Equal(expectedValue, SpanUtility.IsValidIndex(span, index)); }