private static void EscapeString(ReadOnlySpan <byte> value, Span <byte> destination, JavaScriptEncoder encoder, ref int written) { Debug.Assert(encoder != null); OperationStatus result = encoder.EncodeUtf8(value, destination, out int encoderBytesConsumed, out int encoderBytesWritten); Debug.Assert(result != OperationStatus.DestinationTooSmall); Debug.Assert(result != OperationStatus.NeedMoreData); Debug.Assert(encoderBytesConsumed == value.Length); if (result != OperationStatus.Done) { ThrowHelper.ThrowArgumentException_InvalidUTF8(value.Slice(encoderBytesWritten)); } written += encoderBytesWritten; }
private static int EscapeNextBytes(ReadOnlySpan <byte> value, Span <byte> destination, ref int written) { SequenceValidity status = PeekFirstSequence(value, out int numBytesConsumed, out int scalar); if (status != SequenceValidity.WellFormed) { ThrowHelper.ThrowArgumentException_InvalidUTF8(value); } destination[written++] = (byte)'\\'; switch (scalar) { case JsonConstants.LineFeed: destination[written++] = (byte)'n'; break; case JsonConstants.CarriageReturn: destination[written++] = (byte)'r'; break; case JsonConstants.Tab: destination[written++] = (byte)'t'; break; case JsonConstants.BackSlash: destination[written++] = (byte)'\\'; break; case JsonConstants.BackSpace: destination[written++] = (byte)'b'; break; case JsonConstants.FormFeed: destination[written++] = (byte)'f'; break; default: destination[written++] = (byte)'u'; if (scalar < JsonConstants.UnicodePlane01StartValue) { bool result = Utf8Formatter.TryFormat(scalar, destination.Slice(written), out int bytesWritten, format: s_hexStandardFormat); Debug.Assert(result); Debug.Assert(bytesWritten == 4); written += bytesWritten; } else { // Divide by 0x400 to shift right by 10 in order to find the surrogate pairs from the scalar // High surrogate = ((scalar - 0x10000) / 0x400) + D800 // Low surrogate = ((scalar - 0x10000) % 0x400) + DC00 int quotient = Math.DivRem(scalar - JsonConstants.UnicodePlane01StartValue, JsonConstants.ShiftRightBy10, out int remainder); int firstChar = quotient + JsonConstants.HighSurrogateStartValue; int nextChar = remainder + JsonConstants.LowSurrogateStartValue; bool result = Utf8Formatter.TryFormat(firstChar, destination.Slice(written), out int bytesWritten, format: s_hexStandardFormat); Debug.Assert(result); Debug.Assert(bytesWritten == 4); written += bytesWritten; destination[written++] = (byte)'\\'; destination[written++] = (byte)'u'; result = Utf8Formatter.TryFormat(nextChar, destination.Slice(written), out bytesWritten, format: s_hexStandardFormat); Debug.Assert(result); Debug.Assert(bytesWritten == 4); written += bytesWritten; } break; } return(numBytesConsumed); }
private static void EscapeString(ReadOnlySpan <char> value, Span <char> destination, JavaScriptEncoder encoder, ref int written) { // todo: issue #39523: add an Encode(ReadOnlySpan<char>) decode API to System.Text.Encodings.Web.TextEncoding to avoid utf16->utf8->utf16 conversion. Debug.Assert(encoder != null); // Convert char to byte. byte[] utf8DestinationArray = null; Span <byte> utf8Destination; int length = checked ((value.Length) * JsonConstants.MaxExpansionFactorWhileTranscoding); if (length > JsonConstants.StackallocThreshold) { utf8DestinationArray = ArrayPool <byte> .Shared.Rent(length); utf8Destination = utf8DestinationArray; } else { unsafe { byte *ptr = stackalloc byte[JsonConstants.StackallocThreshold]; utf8Destination = new Span <byte>(ptr, JsonConstants.StackallocThreshold); } } ReadOnlySpan <byte> utf16Value = MemoryMarshal.AsBytes(value); OperationStatus toUtf8Status = ToUtf8(utf16Value, utf8Destination, out int bytesConsumed, out int bytesWritten); Debug.Assert(toUtf8Status != OperationStatus.DestinationTooSmall); Debug.Assert(toUtf8Status != OperationStatus.NeedMoreData); if (toUtf8Status != OperationStatus.Done) { if (utf8DestinationArray != null) { utf8Destination.Slice(0, bytesWritten).Clear(); ArrayPool <byte> .Shared.Return(utf8DestinationArray); } ThrowHelper.ThrowArgumentException_InvalidUTF8(utf16Value.Slice(bytesWritten)); } Debug.Assert(toUtf8Status == OperationStatus.Done); Debug.Assert(bytesConsumed == utf16Value.Length); // Escape the bytes. byte[] utf8ConvertedDestinationArray = null; Span <byte> utf8ConvertedDestination; length = checked (bytesWritten * JsonConstants.MaxExpansionFactorWhileEscaping); if (length > JsonConstants.StackallocThreshold) { utf8ConvertedDestinationArray = ArrayPool <byte> .Shared.Rent(length); utf8ConvertedDestination = utf8ConvertedDestinationArray; } else { unsafe { byte *ptr = stackalloc byte[JsonConstants.StackallocThreshold]; utf8ConvertedDestination = new Span <byte>(ptr, JsonConstants.StackallocThreshold); } } EscapeString(utf8Destination.Slice(0, bytesWritten), utf8ConvertedDestination, indexOfFirstByteToEscape: 0, encoder, out int convertedBytesWritten); if (utf8DestinationArray != null) { utf8Destination.Slice(0, bytesWritten).Clear(); ArrayPool <byte> .Shared.Return(utf8DestinationArray); } // Convert byte to char. #if BUILDING_INBOX_LIBRARY OperationStatus toUtf16Status = Utf8.ToUtf16(utf8ConvertedDestination.Slice(0, convertedBytesWritten), destination, out int bytesRead, out int charsWritten); Debug.Assert(toUtf16Status == OperationStatus.Done); Debug.Assert(bytesRead == convertedBytesWritten); #else string utf16 = JsonReaderHelper.GetTextFromUtf8(utf8ConvertedDestination.Slice(0, convertedBytesWritten)); utf16.AsSpan().CopyTo(destination); int charsWritten = utf16.Length; #endif written += charsWritten; if (utf8ConvertedDestinationArray != null) { utf8ConvertedDestination.Slice(0, written).Clear(); ArrayPool <byte> .Shared.Return(utf8ConvertedDestinationArray); } }
public static void EscapeString(ReadOnlySpan <byte> value, Span <byte> destination, int indexOfFirstByteToEscape, JavaScriptEncoder encoder, out int written) { Debug.Assert(indexOfFirstByteToEscape >= 0 && indexOfFirstByteToEscape < value.Length); value.Slice(0, indexOfFirstByteToEscape).CopyTo(destination); written = indexOfFirstByteToEscape; int consumed = indexOfFirstByteToEscape; if (encoder != null) { OperationStatus result = encoder.EncodeUtf8( value.Slice(consumed), destination.Slice(written), out int encoderBytesConsumed, out int encoderBytesWritten); Debug.Assert(result != OperationStatus.DestinationTooSmall); Debug.Assert(result != OperationStatus.NeedMoreData); Debug.Assert(encoderBytesConsumed == value.Length - consumed); if (result != OperationStatus.Done) { ThrowHelper.ThrowArgumentException_InvalidUTF8(value.Slice(encoderBytesWritten)); } written += encoderBytesWritten; } else { // For performance when no encoder is specified, perform escaping here for Ascii and on the // first occurrence of a non-Ascii character, then call into the default encoder. while (consumed < value.Length) { byte val = value[consumed]; if (IsAsciiValue(val)) { if (NeedsEscapingNoBoundsCheck(val)) { EscapeNextBytes(val, destination, ref written); consumed++; } else { destination[written] = val; written++; consumed++; } } else { // Fall back to default encoder OperationStatus result = JavaScriptEncoder.Default.EncodeUtf8( value.Slice(consumed), destination.Slice(written), out int encoderBytesConsumed, out int encoderBytesWritten); Debug.Assert(result != OperationStatus.DestinationTooSmall); Debug.Assert(result != OperationStatus.NeedMoreData); Debug.Assert(encoderBytesConsumed == value.Length - consumed); if (result != OperationStatus.Done) { ThrowHelper.ThrowArgumentException_InvalidUTF8(value.Slice(encoderBytesConsumed)); } consumed += encoderBytesConsumed; written += encoderBytesWritten; } } } }