From 004d698066dda1ef8048c01b9ee0e377e76725e6 Mon Sep 17 00:00:00 2001 From: Ahson Khan Date: Tue, 16 Jul 2019 11:56:21 -0700 Subject: [PATCH] Do not over-escape writing Base64 strings within JSON using the writer. (dotnet/corefx#39522) Commit migrated from https://github.com/dotnet/corefx/commit/063e38c7bc66e076fdcc7521e4c69650baae9427 --- .../Writer/Utf8JsonWriter.WriteProperties.Bytes.cs | 24 +++++++++++----------- .../Writer/Utf8JsonWriter.WriteValues.Bytes.cs | 12 +++++------ .../Writer/Utf8JsonWriter.WriteValues.Helpers.cs | 16 +++------------ .../System.Text.Json/tests/Utf8JsonWriterTests.cs | 6 +++--- 4 files changed, 24 insertions(+), 34 deletions(-) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs index 369d2b4..8304baa 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteProperties.Bytes.cs @@ -212,11 +212,11 @@ namespace System.Text.Json { int encodedLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length); - Debug.Assert(escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding < int.MaxValue - (encodedLength * JsonConstants.MaxExpansionFactorWhileEscaping) - 6); + Debug.Assert(escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding < int.MaxValue - encodedLength - 6); // All ASCII, 2 quotes for property name, 2 quotes to surround the base-64 encoded string value, and 1 colon => escapedPropertyName.Length + encodedLength + 5 - // Optionally, 1 list separator, and up to 3x growth when transcoding, with escaping which can by up to 6x. - int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + (encodedLength * JsonConstants.MaxExpansionFactorWhileEscaping) + 6; + // Optionally, 1 list separator, and up to 3x growth when transcoding. + int maxRequired = (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + encodedLength + 6; if (_memory.Length - BytesPending < maxRequired) { @@ -247,11 +247,11 @@ namespace System.Text.Json { int encodedLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length); - Debug.Assert(escapedPropertyName.Length < int.MaxValue - (encodedLength * JsonConstants.MaxExpansionFactorWhileEscaping) - 6); + Debug.Assert(escapedPropertyName.Length < int.MaxValue - encodedLength - 6); // 2 quotes for property name, 2 quotes to surround the base-64 encoded string value, and 1 colon => escapedPropertyName.Length + encodedLength + 5 - // Optionally, 1 list separator, with escaping which can by up to 6x. - int maxRequired = escapedPropertyName.Length + (encodedLength * JsonConstants.MaxExpansionFactorWhileEscaping) + 6; + // Optionally, 1 list separator. + int maxRequired = escapedPropertyName.Length + encodedLength + 6; if (_memory.Length - BytesPending < maxRequired) { @@ -286,11 +286,11 @@ namespace System.Text.Json int encodedLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length); - Debug.Assert(escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding < int.MaxValue - indent - (encodedLength * JsonConstants.MaxExpansionFactorWhileEscaping) - 7 - s_newLineLength); + Debug.Assert(escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding < int.MaxValue - indent - encodedLength - 7 - s_newLineLength); // All ASCII, 2 quotes for property name, 2 quotes to surround the base-64 encoded string value, 1 colon, and 1 space => indent + escapedPropertyName.Length + encodedLength + 6 - // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding, with escaping which can by up to 6x. - int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + (encodedLength * JsonConstants.MaxExpansionFactorWhileEscaping) + 7 + s_newLineLength; + // Optionally, 1 list separator, 1-2 bytes for new line, and up to 3x growth when transcoding. + int maxRequired = indent + (escapedPropertyName.Length * JsonConstants.MaxExpansionFactorWhileTranscoding) + encodedLength + 7 + s_newLineLength; if (_memory.Length - BytesPending < maxRequired) { @@ -336,11 +336,11 @@ namespace System.Text.Json int encodedLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length); - Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - (encodedLength * JsonConstants.MaxExpansionFactorWhileEscaping) - 7 - s_newLineLength); + Debug.Assert(escapedPropertyName.Length < int.MaxValue - indent - encodedLength - 7 - s_newLineLength); // 2 quotes for property name, 2 quotes to surround the base-64 encoded string value, 1 colon, and 1 space => indent + escapedPropertyName.Length + encodedLength + 6 - // Optionally, 1 list separator, and 1-2 bytes for new line, with escaping which can by up to 6x. - int maxRequired = indent + escapedPropertyName.Length + (encodedLength * JsonConstants.MaxExpansionFactorWhileEscaping) + 7 + s_newLineLength; + // Optionally, 1 list separator, and 1-2 bytes for new line. + int maxRequired = indent + escapedPropertyName.Length + encodedLength + 7 + s_newLineLength; if (_memory.Length - BytesPending < maxRequired) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs index e83e3f2..06b09a3 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Bytes.cs @@ -51,11 +51,11 @@ namespace System.Text.Json { int encodingLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length); - Debug.Assert(encodingLength < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping) - 3); + Debug.Assert(encodingLength < int.MaxValue - 3); - // 2 quotes to surround the base-64 encoded string value, with escaping which can by up to 6x. + // 2 quotes to surround the base-64 encoded string value. // Optionally, 1 list separator - int maxRequired = (encodingLength * JsonConstants.MaxExpansionFactorWhileEscaping) + 3; + int maxRequired = encodingLength + 3; if (_memory.Length - BytesPending < maxRequired) { @@ -83,11 +83,11 @@ namespace System.Text.Json int encodingLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length); - Debug.Assert(encodingLength < (int.MaxValue / JsonConstants.MaxExpansionFactorWhileEscaping) - indent - 3 - s_newLineLength); + Debug.Assert(encodingLength < int.MaxValue - indent - 3 - s_newLineLength); - // indentation + 2 quotes to surround the base-64 encoded string value, with escaping which can by up to 6x. + // indentation + 2 quotes to surround the base-64 encoded string value. // Optionally, 1 list separator, and 1-2 bytes for new line - int maxRequired = indent + (encodingLength * JsonConstants.MaxExpansionFactorWhileEscaping) + 3 + s_newLineLength; + int maxRequired = indent + encodingLength + 3 + s_newLineLength; if (_memory.Length - BytesPending < maxRequired) { diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs index 283a7c4..7afbce4 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.WriteValues.Helpers.cs @@ -50,19 +50,9 @@ namespace System.Text.Json encodedBytes = encodedBytes.Slice(0, written); Span destination = output.Slice(BytesPending); - int firstEscapeIndexVal = encodedBytes.IndexOfAny(JsonConstants.Plus, JsonConstants.Slash); - if (firstEscapeIndexVal == -1) - { - Debug.Assert(destination.Length >= written); - encodedBytes.Slice(0, written).CopyTo(destination); - BytesPending += written; - } - else - { - Debug.Assert(destination.Length >= written * JsonConstants.MaxExpansionFactorWhileEscaping); - JsonWriterHelper.EscapeString(encodedBytes, destination, firstEscapeIndexVal, _options.Encoder, out written); - BytesPending += written; - } + Debug.Assert(destination.Length >= written); + encodedBytes.Slice(0, written).CopyTo(destination); + BytesPending += written; if (outputText != null) { diff --git a/src/libraries/System.Text.Json/tests/Utf8JsonWriterTests.cs b/src/libraries/System.Text.Json/tests/Utf8JsonWriterTests.cs index 13734aa..c6ab641 100644 --- a/src/libraries/System.Text.Json/tests/Utf8JsonWriterTests.cs +++ b/src/libraries/System.Text.Json/tests/Utf8JsonWriterTests.cs @@ -2463,7 +2463,7 @@ namespace System.Text.Json.Tests } [Fact] - public void WriteBase64Escapes() + public void WriteBase64DoesNotEscape() { var output = new ArrayBufferWriter(10); using var jsonUtf8 = new Utf8JsonWriter(output); @@ -2473,11 +2473,11 @@ namespace System.Text.Json.Tests jsonUtf8.Flush(); - AssertContents("\"\\u002b\\u002b\\u002b\\u002b\"", output); + AssertContents("\"++++\"", output); } [Fact] - public void WriteBase64EscapesLarge() + public void WriteBase64DoesNotEscapeLarge() { var output = new ArrayBufferWriter(10); using var jsonUtf8 = new Utf8JsonWriter(output); -- 2.7.4