/// </remarks>
public void WriteBase64StringValue(ReadOnlySpan<byte> bytes)
{
- JsonWriterHelper.ValidateBytes(bytes);
-
WriteBase64ByOptions(bytes);
SetFlagToAddListSeparatorBeforeNextItem();
// TODO: https://github.com/dotnet/runtime/issues/29293
private void WriteBase64Minimized(ReadOnlySpan<byte> bytes)
{
- int encodingLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length);
+ // Base64.GetMaxEncodedToUtf8Length checks to make sure the length is <= int.MaxValue / 4 * 3,
+ // as a length longer than that would overflow int.MaxValue when Base64 encoded. To ensure we
+ // throw an appropriate exception, we check the same condition here first.
+ const int MaxLengthAllowed = int.MaxValue / 4 * 3;
+ if (bytes.Length > MaxLengthAllowed)
+ {
+ ThrowHelper.ThrowArgumentException_ValueTooLarge(bytes.Length);
+ }
- Debug.Assert(encodingLength < int.MaxValue - 3);
+ int encodingLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length);
+ Debug.Assert(encodingLength <= int.MaxValue - 3);
// 2 quotes to surround the base-64 encoded string value.
// Optionally, 1 list separator
int maxRequired = encodingLength + 3;
+ Debug.Assert((uint)maxRequired <= int.MaxValue);
if (_memory.Length - BytesPending < maxRequired)
{
int indent = Indentation;
Debug.Assert(indent <= 2 * _options.MaxDepth);
- int encodingLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length);
+ // Base64.GetMaxEncodedToUtf8Length checks to make sure the length is <= int.MaxValue / 4 * 3,
+ // as a length longer than that would overflow int.MaxValue when Base64 encoded. However, we
+ // also need the indentation + 2 quotes, and optionally a list separate and 1-2 bytes for a new line.
+ // Validate the encoded bytes length won't overflow with all of the length.
+ int extraSpaceRequired = indent + 3 + s_newLineLength;
+ int maxLengthAllowed = int.MaxValue / 4 * 3 - extraSpaceRequired;
+ if (bytes.Length > maxLengthAllowed)
+ {
+ ThrowHelper.ThrowArgumentException_ValueTooLarge(bytes.Length);
+ }
- Debug.Assert(encodingLength < int.MaxValue - indent - 3 - s_newLineLength);
+ int encodingLength = Base64.GetMaxEncodedToUtf8Length(bytes.Length);
- // 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 + 3 + s_newLineLength;
+ int maxRequired = encodingLength + extraSpaceRequired;
+ Debug.Assert((uint)maxRequired <= int.MaxValue - 3);
if (_memory.Length - BytesPending < maxRequired)
{
using System.Text.Unicode;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.DotNet.XUnitExtensions;
using Newtonsoft.Json;
using Xunit;
[InlineData(false, false)]
public void WritingTooLargeBase64Bytes(bool formatted, bool skipValidation)
{
- byte[] value;
-
try
{
- value = new byte[200_000_000];
- }
- catch (OutOfMemoryException)
- {
- return;
- }
+ byte[] value = new byte[200_000_000];
+ value.AsSpan().Fill((byte)'a');
- value.AsSpan().Fill(255);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(value.Length);
- var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- var output = new ArrayBufferWriter<byte>(1024);
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ jsonUtf8.WriteBase64StringValue(value.AsSpan(0, 125_000_001));
+ }
+ Assert.InRange(output.WrittenCount, 125_000_001, int.MaxValue);
+ output.Clear();
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
- {
- Assert.Throws<ArgumentException>(() => jsonUtf8.WriteBase64StringValue(value.AsSpan(0, 125_000_001)));
- }
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteBase64String(value.AsSpan(0, 166_666_667), value.AsSpan(0, 1)));
+ }
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
- {
- Assert.Throws<ArgumentException>(() => jsonUtf8.WriteBase64String(value.AsSpan(0, 166_666_667), value.AsSpan(0, 1)));
- }
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ Assert.Throws<ArgumentException>(() => jsonUtf8.WriteBase64String(Encoding.UTF8.GetString(value).ToCharArray().AsSpan(0, 166_666_667), value.AsSpan(0, 1)));
+ }
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
- {
- Assert.Throws<ArgumentException>(() => jsonUtf8.WriteBase64String(Encoding.UTF8.GetString(value).ToCharArray().AsSpan(0, 166_666_667), value.AsSpan(0, 1)));
- }
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ jsonUtf8.WriteBase64StringValue(value);
+ }
+ Assert.InRange(output.WrittenCount, Base64.GetMaxEncodedToUtf8Length(value.Length), int.MaxValue);
+ output.Clear();
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
- {
- Assert.Throws<ArgumentException>(() => jsonUtf8.WriteBase64StringValue(value));
- }
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteBase64String("foo", value);
+ }
+ Assert.InRange(output.WrittenCount, Base64.GetMaxEncodedToUtf8Length(value.Length), int.MaxValue);
+ output.Clear();
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
- {
- jsonUtf8.WriteStartObject();
- Assert.Throws<ArgumentException>(() => jsonUtf8.WriteBase64String("foo", value));
- }
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteBase64String("foo"u8, value);
+ }
+ Assert.InRange(output.WrittenCount, Base64.GetMaxEncodedToUtf8Length(value.Length), int.MaxValue);
+ output.Clear();
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
- {
- jsonUtf8.WriteStartObject();
- Assert.Throws<ArgumentException>(() => jsonUtf8.WriteBase64String("foo"u8, value));
- }
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteBase64String("foo".AsSpan(), value);
+ }
+ Assert.InRange(output.WrittenCount, Base64.GetMaxEncodedToUtf8Length(value.Length), int.MaxValue);
+ output.Clear();
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
- {
- jsonUtf8.WriteStartObject();
- Assert.Throws<ArgumentException>(() => jsonUtf8.WriteBase64String("foo".AsSpan(), value));
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteBase64String(JsonEncodedText.Encode("foo"), value);
+ }
+ Assert.InRange(output.WrittenCount, Base64.GetMaxEncodedToUtf8Length(value.Length), int.MaxValue);
+ output.Clear();
}
-
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ catch (OutOfMemoryException)
{
- jsonUtf8.WriteStartObject();
- Assert.Throws<ArgumentException>(() => jsonUtf8.WriteBase64String(JsonEncodedText.Encode("foo"), value));
+ throw new SkipTestException("Out of memory allocating large objects");
}
}
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(false, false)]
- public void WritingLargestPossibleBase64Bytes(bool formatted, bool skipValidation)
+ public void WritingHugeBase64Bytes(bool formatted, bool skipValidation)
{
- byte[] value;
-
try
{
- value = new byte[125_000_000];
- }
- catch (OutOfMemoryException)
- {
- return;
- }
+ byte[] value = new byte[1_000_000_000];
- value.AsSpan().Fill(168);
+ value.AsSpan().Fill(168);
- var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
- var output = new ArrayBufferWriter<byte>(1024);
+ var options = new JsonWriterOptions { Indented = formatted, SkipValidation = skipValidation };
+ var output = new ArrayBufferWriter<byte>(1024);
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
- {
- jsonUtf8.WriteBase64StringValue(value);
- }
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ jsonUtf8.WriteBase64StringValue(value);
+ }
- output.Clear();
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
- {
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteBase64String("foo", value);
- jsonUtf8.WriteEndObject();
- }
+ output.Clear();
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteBase64String("foo", value);
+ jsonUtf8.WriteEndObject();
+ }
- output.Clear();
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
- {
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteBase64String("foo"u8, value);
- jsonUtf8.WriteEndObject();
- }
+ output.Clear();
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteBase64String("foo"u8, value);
+ jsonUtf8.WriteEndObject();
+ }
- output.Clear();
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
- {
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteBase64String("foo".AsSpan(), value);
- jsonUtf8.WriteEndObject();
- }
+ output.Clear();
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteBase64String("foo".AsSpan(), value);
+ jsonUtf8.WriteEndObject();
+ }
- output.Clear();
- using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ output.Clear();
+ using (var jsonUtf8 = new Utf8JsonWriter(output, options))
+ {
+ jsonUtf8.WriteStartObject();
+ jsonUtf8.WriteBase64String(JsonEncodedText.Encode("foo"), value);
+ jsonUtf8.WriteEndObject();
+ }
+ }
+ catch (OutOfMemoryException)
{
- jsonUtf8.WriteStartObject();
- jsonUtf8.WriteBase64String(JsonEncodedText.Encode("foo"), value);
- jsonUtf8.WriteEndObject();
+ throw new SkipTestException("Out of memory allocating large objects");
}
}