Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/libraries/System.Memory/tests/Base64/Base64DecoderUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -875,5 +875,22 @@ public void DecodingWithEmbeddedWhiteSpaceIntoSmallDestination_ActualDestination
Assert.Equal(4, written4);
Assert.Equal(new byte[] { 1, 2, 3, 4 }, destination4);
}

[Fact]
public void DecodingWithEmbeddedWhiteSpaceIntoSmallDestination_TrailingWhiteSpacesAreConsumed()
{
byte[] input = " 8J+N i f C f jYk="u8.ToArray();

// The actual decoded data is 8 bytes long.
// If we provide a destination buffer with 6 bytes, we can decode two blocks (6 bytes) and leave 2 bytes undecoded.
// But even though there are 2 bytes left undecoded, we should still consume as much input as possible,
// such that all trailing whitespace are also consumed.

byte[] destination = new byte[6];
Assert.Equal(OperationStatus.DestinationTooSmall, Base64.DecodeFromUtf8(input, destination, out int consumed, out int written));
Assert.Equal((byte)'j', input[consumed]); // byte right after the spaces
Assert.Equal(destination.Length, written);
Assert.Equal(new byte[] { 240, 159, 141, 137, 240, 159 }, destination);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -304,11 +304,16 @@ static OperationStatus InvalidDataFallback(TBase64Decoder decoder, ReadOnlySpan<
status = DecodeFrom(decoder, source, bytes, out localConsumed, out int localWritten, isFinalBlock, ignoreWhiteSpace: false);
bytesConsumed += localConsumed;
bytesWritten += localWritten;
if (status is not OperationStatus.InvalidData)

if (status is OperationStatus.Done or OperationStatus.NeedMoreData)
{
break;
}

// The DecodeFrom helper will return DestinationTooSmall if the destination is too small,
// regardless of whether it's actually too small once you skip whitespace characters.
// In that case we loop again and fall back to block-wise decoding if we can't make progress.

source = source.Slice(localConsumed);
bytes = bytes.Slice(localWritten);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
using System.Collections.Generic;

using Test.Cryptography;
using System.Buffers.Text;
using System.Buffers;

namespace System.Tests
{
Expand Down Expand Up @@ -297,6 +299,8 @@ public static void TryFromBase64String(string encoded, byte[] expected)
bool success = Convert.TryFromBase64String(encoded, actual, out int bytesWritten);
Assert.False(success);
Assert.Equal(0, bytesWritten);

Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(Encoding.UTF8.GetBytes(encoded), actual, out _, out _));
}
else
{
Expand All @@ -307,6 +311,10 @@ public static void TryFromBase64String(string encoded, byte[] expected)
Assert.True(success);
Assert.Equal<byte>(expected, actual);
Assert.Equal(expected.Length, bytesWritten);

Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(Encoding.UTF8.GetBytes(encoded), actual, out int bytesConsumed, out bytesWritten));
Assert.Equal(encoded.Length, bytesConsumed);
Assert.Equal(expected.Length, bytesWritten);
}

// Buffer too short
Expand All @@ -316,6 +324,11 @@ public static void TryFromBase64String(string encoded, byte[] expected)
bool success = Convert.TryFromBase64String(encoded, actual, out int bytesWritten);
Assert.False(success);
Assert.Equal(0, bytesWritten);

Assert.Equal(OperationStatus.DestinationTooSmall, Base64.DecodeFromUtf8(Encoding.UTF8.GetBytes(encoded), actual, out int bytesConsumed, out bytesWritten));
Assert.Equal(actual.Length / 3 * 3, bytesWritten);
Assert.InRange(bytesConsumed, Base64.GetMaxEncodedToUtf8Length(bytesWritten), encoded.Length - 1);
Assert.NotEqual(' ', encoded[bytesConsumed]);
}

// Buffer larger than needed
Expand All @@ -327,6 +340,10 @@ public static void TryFromBase64String(string encoded, byte[] expected)
Assert.Equal(99, actual[expected.Length]);
Assert.Equal<byte>(expected, actual.Take(expected.Length));
Assert.Equal(expected.Length, bytesWritten);

Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(Encoding.UTF8.GetBytes(encoded), actual, out int bytesConsumed, out bytesWritten));
Assert.Equal(encoded.Length, bytesConsumed);
Assert.Equal(expected.Length, bytesWritten);
}
}
}
Expand Down