var rnd = new Random(42);
for (int i = 0; i < 10; i++)
{
- int numBytes = rnd.Next(100, 1000 * 1000);
- while (numBytes % 4 != 0)
+ int numBytes;
+ do
{
numBytes = rnd.Next(100, 1000 * 1000);
- }
+ } while (numBytes % 4 != 0); // ensure we have a valid length
+
Span<byte> source = new byte[numBytes];
Base64TestHelper.InitalizeDecodableBytes(source, numBytes);
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- Assert.Equal(OperationStatus.Done,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
+ Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
Assert.Equal(source.Length, consumed);
Assert.Equal(decodedBytes.Length, decodedByteCount);
Assert.True(Base64TestHelper.VerifyDecodingCorrectness(source.Length, decodedBytes.Length, source, decodedBytes));
}
[Fact]
- public void DecodeEmptySpan()
+ public void BasicDecodingInvalidInputLength()
+ {
+ var rnd = new Random(42);
+ for (int i = 0; i < 10; i++)
+ {
+ int numBytes;
+ do
+ {
+ numBytes = rnd.Next(100, 1000 * 1000);
+ } while (numBytes % 4 == 0); // ensure we have a invalid length
+
+ Span<byte> source = new byte[numBytes];
+ Base64TestHelper.InitalizeDecodableBytes(source, numBytes);
+
+ Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
+ int expectedConsumed = numBytes / 4 * 4; // decode input up to the closest multiple of 4
+ int expectedDecoded = expectedConsumed / 4 * 3;
+
+ Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
+ Assert.Equal(expectedConsumed, consumed);
+ Assert.Equal(expectedDecoded, decodedByteCount);
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, expectedDecoded, source, decodedBytes));
+ }
+ }
+
+ [Fact]
+ public void BasicDecodingWithFinalBlockFalse()
+ {
+ var rnd = new Random(42);
+ for (int i = 0; i < 10; i++)
+ {
+ int numBytes;
+ do
+ {
+ numBytes = rnd.Next(100, 1000 * 1000);
+ } while (numBytes % 4 != 0); // ensure we have a valid length
+
+ Span<byte> source = new byte[numBytes];
+ Base64TestHelper.InitalizeDecodableBytes(source, numBytes);
+
+ Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
+ int expectedConsumed = source.Length / 4 * 4; // only consume closest multiple of four since isFinalBlock is false
+
+ Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock: false));
+ Assert.Equal(expectedConsumed, consumed);
+ Assert.Equal(decodedBytes.Length, decodedByteCount);
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
+ }
+ }
+
+ [Fact]
+ public void BasicDecodingWithFinalBlockFalseInvalidInputLength()
+ {
+ var rnd = new Random(42);
+ for (int i = 0; i < 10; i++)
+ {
+ int numBytes;
+ do
+ {
+ numBytes = rnd.Next(100, 1000 * 1000);
+ } while (numBytes % 4 == 0); // ensure we have a invalid length
+
+ Span<byte> source = new byte[numBytes];
+ Base64TestHelper.InitalizeDecodableBytes(source, numBytes);
+
+ Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
+ int expectedConsumed = source.Length / 4 * 4; // only consume closest multiple of four since isFinalBlock is false
+ int expectedDecoded = expectedConsumed / 4 * 3;
+
+ Assert.Equal(OperationStatus.NeedMoreData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock: false));
+ Assert.Equal(expectedConsumed, consumed);
+ Assert.Equal(expectedDecoded, decodedByteCount);
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
+ }
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void DecodeEmptySpan(bool isFinalBlock)
{
Span<byte> source = Span<byte>.Empty;
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- Assert.Equal(OperationStatus.Done,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
- Assert.Equal(source.Length, consumed);
- Assert.Equal(decodedBytes.Length, decodedByteCount);
- Assert.True(Base64TestHelper.VerifyDecodingCorrectness(source.Length, decodedBytes.Length, source, decodedBytes));
+ Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock));
+ Assert.Equal(0, consumed);
+ Assert.Equal(0, decodedByteCount);
}
[Fact]
Span<byte> decodedBytes = Guid.NewGuid().ToByteArray();
Base64.EncodeToUtf8(decodedBytes, source, out int _, out int _);
- Assert.Equal(OperationStatus.Done,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
+ Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
Assert.Equal(24, consumed);
Assert.Equal(16, decodedByteCount);
Assert.True(Base64TestHelper.VerifyDecodingCorrectness(source.Length, decodedBytes.Length, source, decodedBytes));
}
[Fact]
- public void BasicDecodingWithFinalBlockFalse()
+ public void DecodingOutputTooSmall()
{
- var rnd = new Random(42);
- for (int i = 0; i < 10; i++)
+ for (int numBytes = 5; numBytes < 20; numBytes++)
{
- int numBytes = rnd.Next(100, 1000 * 1000);
- while (numBytes % 4 != 0)
- {
- numBytes = rnd.Next(100, 1000 * 1000);
- }
Span<byte> source = new byte[numBytes];
Base64TestHelper.InitalizeDecodableBytes(source, numBytes);
- Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- int expectedConsumed = source.Length / 4 * 4; // only consume closest multiple of four since isFinalBlock is false
+ Span<byte> decodedBytes = new byte[3];
+ int consumed, written;
+ if (numBytes % 4 == 0)
+ {
+ Assert.True(OperationStatus.DestinationTooSmall ==
+ Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out written), "Number of Input Bytes: " + numBytes);
+ }
+ else
+ {
+ Assert.True(OperationStatus.InvalidData ==
+ Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out written), "Number of Input Bytes: " + numBytes);
+ }
+ int expectedConsumed = 4;
+ Assert.Equal(expectedConsumed, consumed);
+ Assert.Equal(decodedBytes.Length, written);
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
+ }
+
+ // Output too small even with padding characters in the input
+ {
+ Span<byte> source = new byte[12];
+ Base64TestHelper.InitalizeDecodableBytes(source);
+ source[10] = Base64TestHelper.EncodingPad;
+ source[11] = Base64TestHelper.EncodingPad;
- Assert.Equal(OperationStatus.NeedMoreData,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock: false));
+ Span<byte> decodedBytes = new byte[6];
+ Assert.Equal(OperationStatus.DestinationTooSmall, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int written));
+ int expectedConsumed = 8;
Assert.Equal(expectedConsumed, consumed);
- Assert.Equal(decodedBytes.Length, decodedByteCount);
+ Assert.Equal(decodedBytes.Length, written);
Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
}
+
+ {
+ Span<byte> source = new byte[12];
+ Base64TestHelper.InitalizeDecodableBytes(source);
+ source[11] = Base64TestHelper.EncodingPad;
+
+ Span<byte> decodedBytes = new byte[7];
+ Assert.Equal(OperationStatus.DestinationTooSmall, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int written));
+ int expectedConsumed = 8;
+ Assert.Equal(expectedConsumed, consumed);
+ Assert.Equal(6, written);
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, 6, source, decodedBytes));
+ }
+ }
+
+ [Fact]
+ public void DecodingOutputTooSmallWithFinalBlockFalse()
+ {
+ for (int numBytes = 8; numBytes < 20; numBytes++)
+ {
+ Span<byte> source = new byte[numBytes];
+ Base64TestHelper.InitalizeDecodableBytes(source, numBytes);
+
+ Span<byte> decodedBytes = new byte[4];
+ int consumed, written;
+ Assert.True(OperationStatus.DestinationTooSmall ==
+ Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out written, isFinalBlock: false), "Number of Input Bytes: " + numBytes);
+ int expectedConsumed = 4;
+ int expectedWritten = 3;
+ Assert.Equal(expectedConsumed, consumed);
+ Assert.Equal(expectedWritten, written);
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, expectedWritten, source, decodedBytes));
+ }
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void DecodingOutputTooSmallRetry(bool isFinalBlock)
+ {
+ Span<byte> source = new byte[1000];
+ Base64TestHelper.InitalizeDecodableBytes(source);
+
+ int outputSize = 240;
+ int requiredSize = Base64.GetMaxDecodedFromUtf8Length(source.Length);
+
+ Span<byte> decodedBytes = new byte[outputSize];
+ Assert.Equal(OperationStatus.DestinationTooSmall, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock));
+ int expectedConsumed = decodedBytes.Length / 3 * 4;
+ Assert.Equal(expectedConsumed, consumed);
+ Assert.Equal(decodedBytes.Length, decodedByteCount);
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
+
+ decodedBytes = new byte[requiredSize - outputSize];
+ source = source.Slice(consumed);
+ Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out decodedByteCount, isFinalBlock));
+ expectedConsumed = decodedBytes.Length / 3 * 4;
+ Assert.Equal(expectedConsumed, consumed);
+ Assert.Equal(decodedBytes.Length, decodedByteCount);
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
+ }
+
+ [Theory]
+ [InlineData("AQ==", 1)]
+ [InlineData("AQI=", 2)]
+ [InlineData("AQID", 3)]
+ [InlineData("AQIDBA==", 4)]
+ [InlineData("AQIDBAU=", 5)]
+ [InlineData("AQIDBAUG", 6)]
+ public void BasicDecodingWithFinalBlockTrueKnownInputDone(string inputString, int expectedWritten)
+ {
+ Span<byte> source = Encoding.ASCII.GetBytes(inputString);
+ Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
+
+ int expectedConsumed = inputString.Length;
+ Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
+ Assert.Equal(expectedConsumed, consumed);
+ Assert.Equal(expectedWritten, decodedByteCount);
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, expectedWritten, source, decodedBytes));
}
[Theory]
[InlineData("AQI", 0, 0)]
[InlineData("AQIDBA", 4, 3)]
[InlineData("AQIDBAU", 4, 3)]
- [InlineData("AQID", 4, 3)]
- [InlineData("AQIDBAUG", 8, 6)]
- public void BasicDecodingWithFinalBlockFalseKnownInputNeedMoreData(string inputString, int expectedConsumed, int expectedWritten)
+ public void BasicDecodingWithFinalBlockTrueKnownInputInvalid(string inputString, int expectedConsumed, int expectedWritten)
{
Span<byte> source = Encoding.ASCII.GetBytes(inputString);
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- Assert.Equal(OperationStatus.NeedMoreData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock: false));
+ Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
Assert.Equal(expectedConsumed, consumed);
Assert.Equal(expectedWritten, decodedByteCount); // expectedWritten == decodedBytes.Length
Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
}
[Theory]
- [InlineData("AQ==", 0, 0)]
- [InlineData("AQI=", 0, 0)]
- [InlineData("AQIDBA==", 4, 3)]
- [InlineData("AQIDBAU=", 4, 3)]
- public void BasicDecodingWithFinalBlockFalseKnownInputInvalid(string inputString, int expectedConsumed, int expectedWritten)
+ [InlineData("AQID", 3)]
+ [InlineData("AQIDBAUG", 6)]
+ public void BasicDecodingWithFinalBlockFalseKnownInputDone(string inputString, int expectedWritten)
{
Span<byte> source = Encoding.ASCII.GetBytes(inputString);
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock: false));
+ int expectedConsumed = inputString.Length;
+ Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock: false));
Assert.Equal(expectedConsumed, consumed);
- Assert.Equal(expectedWritten, decodedByteCount);
- Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, expectedWritten, source, decodedBytes));
+ Assert.Equal(expectedWritten, decodedByteCount); // expectedWritten == decodedBytes.Length
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
}
[Theory]
[InlineData("A", 0, 0)]
[InlineData("AQ", 0, 0)]
[InlineData("AQI", 0, 0)]
+ [InlineData("AQIDB", 4, 3)]
[InlineData("AQIDBA", 4, 3)]
[InlineData("AQIDBAU", 4, 3)]
- public void BasicDecodingWithFinalBlockTrueKnownInputInvalid(string inputString, int expectedConsumed, int expectedWritten)
+ public void BasicDecodingWithFinalBlockFalseKnownInputNeedMoreData(string inputString, int expectedConsumed, int expectedWritten)
{
Span<byte> source = Encoding.ASCII.GetBytes(inputString);
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
+ Assert.Equal(OperationStatus.NeedMoreData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock: false));
Assert.Equal(expectedConsumed, consumed);
Assert.Equal(expectedWritten, decodedByteCount); // expectedWritten == decodedBytes.Length
Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
}
[Theory]
- [InlineData("AQ==", 4, 1)]
- [InlineData("AQI=", 4, 2)]
- [InlineData("AQID", 4, 3)]
- [InlineData("AQIDBA==", 8, 4)]
- [InlineData("AQIDBAU=", 8, 5)]
- [InlineData("AQIDBAUG", 8, 6)]
- public void BasicDecodingWithFinalBlockTrueKnownInputDone(string inputString, int expectedConsumed, int expectedWritten)
+ [InlineData("AQ==", 0, 0)]
+ [InlineData("AQI=", 0, 0)]
+ [InlineData("AQIDBA==", 4, 3)]
+ [InlineData("AQIDBAU=", 4, 3)]
+ public void BasicDecodingWithFinalBlockFalseKnownInputInvalid(string inputString, int expectedConsumed, int expectedWritten)
{
Span<byte> source = Encoding.ASCII.GetBytes(inputString);
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
+ Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock: false));
Assert.Equal(expectedConsumed, consumed);
Assert.Equal(expectedWritten, decodedByteCount);
Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, expectedWritten, source, decodedBytes));
}
- [Fact]
- public void DecodingInvalidBytes()
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void DecodingInvalidBytes(bool isFinalBlock)
{
// Invalid Bytes:
// 0-42
for (int i = 0; i < invalidBytes.Length; i++)
{
// Don't test padding (byte 61 i.e. '='), which is tested in DecodingInvalidBytesPadding
- if (invalidBytes[i] == Base64TestHelper.s_encodingPad)
+ if (invalidBytes[i] == Base64TestHelper.EncodingPad)
continue;
// replace one byte with an invalid input
source[j] = invalidBytes[i];
- Assert.Equal(OperationStatus.InvalidData,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
+ Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock));
if (j < 4)
{
}
}
- // Input that is not a multiple of 4 is considered invalid
+ // Input that is not a multiple of 4 is considered invalid, if isFinalBlock = true
+ if (isFinalBlock)
{
Span<byte> source = new byte[7] { 50, 50, 50, 50, 80, 80, 80 }; // incomplete input - "2222PPP"
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- Assert.Equal(OperationStatus.InvalidData,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
+ Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
Assert.Equal(4, consumed);
Assert.Equal(3, decodedByteCount);
Assert.True(Base64TestHelper.VerifyDecodingCorrectness(4, 3, source, decodedBytes));
}
}
- [Fact]
- public void DecodingInvalidBytesPadding()
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void DecodingInvalidBytesPadding(bool isFinalBlock)
{
// Only last 2 bytes can be padding, all other occurrence of padding is invalid
for (int j = 0; j < 7; j++)
{
Span<byte> source = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 }; // valid input - "2222PPPP"
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- source[j] = Base64TestHelper.s_encodingPad;
- Assert.Equal(OperationStatus.InvalidData,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
+ source[j] = Base64TestHelper.EncodingPad;
+ Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock));
if (j < 4)
{
{
Span<byte> source = new byte[] { 50, 50, 50, 50, 80, 42, 42, 42 };
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- source[6] = Base64TestHelper.s_encodingPad;
- source[7] = Base64TestHelper.s_encodingPad; // invalid input - "2222P*=="
- Assert.Equal(OperationStatus.InvalidData,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
+ source[6] = Base64TestHelper.EncodingPad;
+ source[7] = Base64TestHelper.EncodingPad; // invalid input - "2222P*=="
+ Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock));
Assert.Equal(4, consumed);
Assert.Equal(3, decodedByteCount);
source = new byte[] { 50, 50, 50, 50, 80, 42, 42, 42 };
decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- source[7] = Base64TestHelper.s_encodingPad; // invalid input - "2222PP**="
- Assert.Equal(OperationStatus.InvalidData,
- Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out decodedByteCount));
+ source[7] = Base64TestHelper.EncodingPad; // invalid input - "2222PP**="
+ Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out decodedByteCount, isFinalBlock));
Assert.Equal(4, consumed);
Assert.Equal(3, decodedByteCount);
Assert.True(Base64TestHelper.VerifyDecodingCorrectness(4, 3, source, decodedBytes));
}
- // The last byte or the last 2 bytes being the padding character is valid
+ // The last byte or the last 2 bytes being the padding character is valid, if isFinalBlock = true
{
Span<byte> source = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 };
Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- source[6] = Base64TestHelper.s_encodingPad;
- source[7] = Base64TestHelper.s_encodingPad; // valid input - "2222PP=="
- Assert.Equal(OperationStatus.Done,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
-
- Assert.Equal(source.Length, consumed);
- Assert.Equal(4, decodedByteCount);
- Assert.True(Base64TestHelper.VerifyDecodingCorrectness(source.Length, 4, source, decodedBytes));
+ source[6] = Base64TestHelper.EncodingPad;
+ source[7] = Base64TestHelper.EncodingPad; // valid input - "2222PP=="
- source = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 };
- decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
- source[7] = Base64TestHelper.s_encodingPad; // valid input - "2222PPP="
- Assert.Equal(OperationStatus.Done,
- Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out decodedByteCount));
-
- Assert.Equal(source.Length, consumed);
- Assert.Equal(5, decodedByteCount);
- Assert.True(Base64TestHelper.VerifyDecodingCorrectness(source.Length, 5, source, decodedBytes));
- }
- }
+ OperationStatus expectedStatus = isFinalBlock ? OperationStatus.Done : OperationStatus.InvalidData;
+ int expectedConsumed = isFinalBlock ? source.Length : 4;
+ int expectedWritten = isFinalBlock ? 4 : 3;
- [Fact]
- public void DecodingOutputTooSmall()
- {
- for (int numBytes = 5; numBytes < 20; numBytes++)
- {
- Span<byte> source = new byte[numBytes];
- Base64TestHelper.InitalizeDecodableBytes(source, numBytes);
-
- Span<byte> decodedBytes = new byte[3];
- int consumed, written;
- if (numBytes % 4 != 0)
- {
- Assert.True(OperationStatus.InvalidData ==
- Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out written), "Number of Input Bytes: " + numBytes);
- }
- else
- {
- Assert.True(OperationStatus.DestinationTooSmall ==
- Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out written), "Number of Input Bytes: " + numBytes);
- }
- int expectedConsumed = 4;
- Assert.Equal(expectedConsumed, consumed);
- Assert.Equal(decodedBytes.Length, written);
- Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
- }
-
- // Output too small even with padding characters in the input
- {
- Span<byte> source = new byte[12];
- Base64TestHelper.InitalizeDecodableBytes(source);
- source[10] = Base64TestHelper.s_encodingPad;
- source[11] = Base64TestHelper.s_encodingPad;
-
- Span<byte> decodedBytes = new byte[6];
- Assert.Equal(OperationStatus.DestinationTooSmall,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int written));
- int expectedConsumed = 8;
+ Assert.Equal(expectedStatus, Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount, isFinalBlock));
Assert.Equal(expectedConsumed, consumed);
- Assert.Equal(decodedBytes.Length, written);
- Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
- }
+ Assert.Equal(expectedWritten, decodedByteCount);
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, expectedWritten, source, decodedBytes));
- {
- Span<byte> source = new byte[12];
- Base64TestHelper.InitalizeDecodableBytes(source);
- source[11] = Base64TestHelper.s_encodingPad;
+ source = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 };
+ decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(source.Length)];
+ source[7] = Base64TestHelper.EncodingPad; // valid input - "2222PPP="
- Span<byte> decodedBytes = new byte[7];
- Assert.Equal(OperationStatus.DestinationTooSmall,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int written));
- int expectedConsumed = 8;
+ expectedConsumed = isFinalBlock ? source.Length : 4;
+ expectedWritten = isFinalBlock ? 5 : 3;
+ Assert.Equal(expectedStatus, Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out decodedByteCount, isFinalBlock));
Assert.Equal(expectedConsumed, consumed);
- Assert.Equal(6, written);
- Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, 6, source, decodedBytes));
+ Assert.Equal(expectedWritten, decodedByteCount);
+ Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, expectedWritten, source, decodedBytes));
}
}
[Fact]
- public void DecodingOutputTooSmallRetry()
- {
- Span<byte> source = new byte[1000];
- Base64TestHelper.InitalizeDecodableBytes(source);
-
- int outputSize = 240;
- int requiredSize = Base64.GetMaxDecodedFromUtf8Length(source.Length);
-
- Span<byte> decodedBytes = new byte[outputSize];
- Assert.Equal(OperationStatus.DestinationTooSmall,
- Base64.DecodeFromUtf8(source, decodedBytes, out int consumed, out int decodedByteCount));
- int expectedConsumed = decodedBytes.Length / 3 * 4;
- Assert.Equal(expectedConsumed, consumed);
- Assert.Equal(decodedBytes.Length, decodedByteCount);
- Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
-
- decodedBytes = new byte[requiredSize - outputSize];
- source = source.Slice(consumed);
- Assert.Equal(OperationStatus.Done,
- Base64.DecodeFromUtf8(source, decodedBytes, out consumed, out decodedByteCount));
- expectedConsumed = decodedBytes.Length / 3 * 4;
- Assert.Equal(expectedConsumed, consumed);
- Assert.Equal(decodedBytes.Length, decodedByteCount);
- Assert.True(Base64TestHelper.VerifyDecodingCorrectness(expectedConsumed, decodedBytes.Length, source, decodedBytes));
- }
-
- [Fact]
public void GetMaxDecodedLength()
{
Span<byte> sourceEmpty = Span<byte>.Empty;
Span<byte> buffer = new byte[8] { 50, 50, 50, 50, 80, 80, 80, 80 }; // valid input - "2222PPPP"
// Don't test padding (byte 61 i.e. '='), which is tested in DecodeInPlaceInvalidBytesPadding
- if (invalidBytes[i] == Base64TestHelper.s_encodingPad)
+ if (invalidBytes[i] == Base64TestHelper.EncodingPad)
continue;
// replace one byte with an invalid input
for (int j = 0; j < 7; j++)
{
Span<byte> buffer = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 }; // valid input - "2222PPPP"
- buffer[j] = Base64TestHelper.s_encodingPad;
+ buffer[j] = Base64TestHelper.EncodingPad;
string sourceString = Encoding.ASCII.GetString(buffer.Slice(0, 4).ToArray());
Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten));
// Invalid input with valid padding
{
Span<byte> buffer = new byte[] { 50, 50, 50, 50, 80, 42, 42, 42 };
- buffer[6] = Base64TestHelper.s_encodingPad;
- buffer[7] = Base64TestHelper.s_encodingPad; // invalid input - "2222P*=="
+ buffer[6] = Base64TestHelper.EncodingPad;
+ buffer[7] = Base64TestHelper.EncodingPad; // invalid input - "2222P*=="
string sourceString = Encoding.ASCII.GetString(buffer.Slice(0, 4).ToArray());
Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten));
Assert.Equal(3, bytesWritten);
{
Span<byte> buffer = new byte[] { 50, 50, 50, 50, 80, 42, 42, 42 };
- buffer[7] = Base64TestHelper.s_encodingPad; // invalid input - "2222P**="
+ buffer[7] = Base64TestHelper.EncodingPad; // invalid input - "2222P**="
string sourceString = Encoding.ASCII.GetString(buffer.Slice(0, 4).ToArray());
Assert.Equal(OperationStatus.InvalidData, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten));
Assert.Equal(3, bytesWritten);
// The last byte or the last 2 bytes being the padding character is valid
{
Span<byte> buffer = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 };
- buffer[6] = Base64TestHelper.s_encodingPad;
- buffer[7] = Base64TestHelper.s_encodingPad; // valid input - "2222PP=="
+ buffer[6] = Base64TestHelper.EncodingPad;
+ buffer[7] = Base64TestHelper.EncodingPad; // valid input - "2222PP=="
string sourceString = Encoding.ASCII.GetString(buffer.ToArray());
Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten));
Assert.Equal(4, bytesWritten);
{
Span<byte> buffer = new byte[] { 50, 50, 50, 50, 80, 80, 80, 80 };
- buffer[7] = Base64TestHelper.s_encodingPad; // valid input - "2222PPP="
+ buffer[7] = Base64TestHelper.EncodingPad; // valid input - "2222PPP="
string sourceString = Encoding.ASCII.GetString(buffer.ToArray());
Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8InPlace(buffer, out int bytesWritten));
Assert.Equal(5, bytesWritten);
string expectedText = Convert.ToBase64String(bytes, 0, value + 1);
Assert.Equal(expectedText, encodedText);
- if (encodedBytes.Length % 4 == 0)
- {
- Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(encodedBytes.Length)];
- Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(encodedBytes, decodedBytes, out consumed, out int decodedByteCount));
- Assert.Equal(encodedBytes.Length, consumed);
- Assert.Equal(sourceBytes.Length, decodedByteCount);
- Assert.True(sourceBytes.SequenceEqual(decodedBytes.Slice(0, decodedByteCount)));
- }
+ Assert.Equal(0, encodedBytes.Length % 4);
+ Span<byte> decodedBytes = new byte[Base64.GetMaxDecodedFromUtf8Length(encodedBytes.Length)];
+ Assert.Equal(OperationStatus.Done, Base64.DecodeFromUtf8(encodedBytes, decodedBytes, out consumed, out int decodedByteCount));
+ Assert.Equal(encodedBytes.Length, consumed);
+ Assert.Equal(sourceBytes.Length, decodedByteCount);
+ Assert.True(sourceBytes.SequenceEqual(decodedBytes.Slice(0, decodedByteCount)));
}
}
}
[Fact]
- public void EncodeEmptySpan()
- {
- Span<byte> source = Span<byte>.Empty;
- Span<byte> encodedBytes = new byte[Base64.GetMaxEncodedToUtf8Length(source.Length)];
-
- Assert.Equal(OperationStatus.Done, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount));
- Assert.Equal(source.Length, consumed);
- Assert.Equal(encodedBytes.Length, encodedBytesCount);
- Assert.True(Base64TestHelper.VerifyEncodingCorrectness(source.Length, encodedBytes.Length, source, encodedBytes));
- }
-
- [Fact]
- [OuterLoop]
- public void EncodeTooLargeSpan()
- {
-
- if (IntPtr.Size < 8)
- return;
-
- bool allocatedFirst = false;
- bool allocatedSecond = false;
- IntPtr memBlockFirst = IntPtr.Zero;
- IntPtr memBlockSecond = IntPtr.Zero;
-
- // int.MaxValue - (int.MaxValue % 4) => 2147483644, largest multiple of 4 less than int.MaxValue
- // CLR default limit of 2 gigabytes (GB).
- // 1610612734, larger than MaximumEncodeLength, requires output buffer of size 2147483648 (which is > int.MaxValue)
- const int sourceCount = (int.MaxValue >> 2) * 3 + 1;
- const int encodedCount = 2000000000;
-
- try
- {
- allocatedFirst = AllocationHelper.TryAllocNative((IntPtr)sourceCount, out memBlockFirst);
- allocatedSecond = AllocationHelper.TryAllocNative((IntPtr)encodedCount, out memBlockSecond);
- if (allocatedFirst && allocatedSecond)
- {
- unsafe
- {
- var source = new Span<byte>(memBlockFirst.ToPointer(), sourceCount);
- var encodedBytes = new Span<byte>(memBlockSecond.ToPointer(), encodedCount);
-
- Assert.Equal(OperationStatus.DestinationTooSmall, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount));
- Assert.Equal((encodedBytes.Length >> 2) * 3, consumed); // encoding 1500000000 bytes fits into buffer of 2000000000 bytes
- Assert.Equal(encodedBytes.Length, encodedBytesCount);
- }
- }
- }
- finally
- {
- if (allocatedFirst)
- AllocationHelper.ReleaseNative(ref memBlockFirst);
- if (allocatedSecond)
- AllocationHelper.ReleaseNative(ref memBlockSecond);
- }
- }
-
- [Fact]
public void BasicEncodingWithFinalBlockFalse()
{
var rnd = new Random(42);
int numBytes = rnd.Next(100, 1000 * 1000);
Span<byte> source = new byte[numBytes];
Base64TestHelper.InitalizeBytes(source, numBytes);
+
Span<byte> encodedBytes = new byte[Base64.GetMaxEncodedToUtf8Length(source.Length)];
int expectedConsumed = source.Length / 3 * 3; // only consume closest multiple of three since isFinalBlock is false
int expectedWritten = source.Length / 3 * 4;
- Assert.Equal(OperationStatus.NeedMoreData, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount, isFinalBlock: false));
+ // The constant random seed guarantees that both states are tested.
+ OperationStatus expectedStatus = numBytes % 3 == 0 ? OperationStatus.Done : OperationStatus.NeedMoreData;
+ Assert.Equal(expectedStatus, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount, isFinalBlock: false));
Assert.Equal(expectedConsumed, consumed);
Assert.Equal(expectedWritten, encodedBytesCount);
Assert.True(Base64TestHelper.VerifyEncodingCorrectness(expectedConsumed, expectedWritten, source, encodedBytes));
}
[Theory]
- [InlineData(1, "", 0, 0)]
- [InlineData(2, "", 0, 0)]
- [InlineData(3, "AQID", 3, 4)]
- [InlineData(4, "AQID", 3, 4)]
- [InlineData(5, "AQID", 3, 4)]
- [InlineData(6, "AQIDBAUG", 6, 8)]
- [InlineData(7, "AQIDBAUG", 6, 8)]
- public void BasicEncodingWithFinalBlockFalseKnownInput(int numBytes, string expectedText, int expectedConsumed, int expectedWritten)
+ [InlineData(1, "AQ==")]
+ [InlineData(2, "AQI=")]
+ [InlineData(3, "AQID")]
+ [InlineData(4, "AQIDBA==")]
+ [InlineData(5, "AQIDBAU=")]
+ [InlineData(6, "AQIDBAUG")]
+ [InlineData(7, "AQIDBAUGBw==")]
+ public void BasicEncodingWithFinalBlockTrueKnownInput(int numBytes, string expectedText)
{
+ int expectedConsumed = numBytes;
+ int expectedWritten = expectedText.Length;
+
Span<byte> source = new byte[numBytes];
for (int i = 0; i < numBytes; i++)
{
}
Span<byte> encodedBytes = new byte[Base64.GetMaxEncodedToUtf8Length(source.Length)];
- Assert.Equal(OperationStatus.NeedMoreData, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount, isFinalBlock: false));
+ Assert.Equal(OperationStatus.Done, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount, isFinalBlock: true));
Assert.Equal(expectedConsumed, consumed);
Assert.Equal(expectedWritten, encodedBytesCount);
}
[Theory]
- [InlineData(1, "AQ==", 1, 4)]
- [InlineData(2, "AQI=", 2, 4)]
+ [InlineData(1, "", 0, 0)]
+ [InlineData(2, "", 0, 0)]
[InlineData(3, "AQID", 3, 4)]
- [InlineData(4, "AQIDBA==", 4, 8)]
- [InlineData(5, "AQIDBAU=", 5, 8)]
+ [InlineData(4, "AQID", 3, 4)]
+ [InlineData(5, "AQID", 3, 4)]
[InlineData(6, "AQIDBAUG", 6, 8)]
- [InlineData(7, "AQIDBAUGBw==", 7, 12)]
- public void BasicEncodingWithFinalBlockTrueKnownInput(int numBytes, string expectedText, int expectedConsumed, int expectedWritten)
+ [InlineData(7, "AQIDBAUG", 6, 8)]
+ public void BasicEncodingWithFinalBlockFalseKnownInput(int numBytes, string expectedText, int expectedConsumed, int expectedWritten)
{
Span<byte> source = new byte[numBytes];
for (int i = 0; i < numBytes; i++)
}
Span<byte> encodedBytes = new byte[Base64.GetMaxEncodedToUtf8Length(source.Length)];
- Assert.Equal(OperationStatus.Done, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount, isFinalBlock: true));
+ OperationStatus expectedStatus = numBytes % 3 == 0 ? OperationStatus.Done : OperationStatus.NeedMoreData;
+ Assert.Equal(expectedStatus, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount, isFinalBlock: false));
Assert.Equal(expectedConsumed, consumed);
Assert.Equal(expectedWritten, encodedBytesCount);
Assert.Equal(expectedText, encodedText);
}
- [Fact]
- public void EncodingOutputTooSmall()
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void EncodeEmptySpan(bool isFinalBlock)
+ {
+ Span<byte> source = Span<byte>.Empty;
+ Span<byte> encodedBytes = new byte[Base64.GetMaxEncodedToUtf8Length(source.Length)];
+
+ Assert.Equal(OperationStatus.Done, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount, isFinalBlock));
+ Assert.Equal(0, consumed);
+ Assert.Equal(0, encodedBytesCount);
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void EncodingOutputTooSmall(bool isFinalBlock)
{
for (int numBytes = 4; numBytes < 20; numBytes++)
{
Base64TestHelper.InitalizeBytes(source, numBytes);
Span<byte> encodedBytes = new byte[4];
- Assert.Equal(OperationStatus.DestinationTooSmall,
- Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int written));
+ Assert.Equal(OperationStatus.DestinationTooSmall, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int written, isFinalBlock));
int expectedConsumed = 3;
Assert.Equal(expectedConsumed, consumed);
Assert.Equal(encodedBytes.Length, written);
}
}
- [Fact]
- public void EncodingOutputTooSmallRetry()
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void EncodingOutputTooSmallRetry(bool isFinalBlock)
{
Span<byte> source = new byte[750];
Base64TestHelper.InitalizeBytes(source);
int requiredSize = Base64.GetMaxEncodedToUtf8Length(source.Length);
Span<byte> encodedBytes = new byte[outputSize];
- Assert.Equal(OperationStatus.DestinationTooSmall,
- Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int written));
+ Assert.Equal(OperationStatus.DestinationTooSmall, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int written, isFinalBlock));
int expectedConsumed = encodedBytes.Length / 4 * 3;
Assert.Equal(expectedConsumed, consumed);
Assert.Equal(encodedBytes.Length, written);
encodedBytes = new byte[requiredSize - outputSize];
source = source.Slice(consumed);
- Assert.Equal(OperationStatus.Done,
- Base64.EncodeToUtf8(source, encodedBytes, out consumed, out written));
+ Assert.Equal(OperationStatus.Done, Base64.EncodeToUtf8(source, encodedBytes, out consumed, out written, isFinalBlock));
expectedConsumed = encodedBytes.Length / 4 * 3;
Assert.Equal(expectedConsumed, consumed);
Assert.Equal(encodedBytes.Length, written);
Assert.True(Base64TestHelper.VerifyEncodingCorrectness(expectedConsumed, encodedBytes.Length, source, encodedBytes));
}
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ [OuterLoop]
+ public void EncodeTooLargeSpan(bool isFinalBlock)
+ {
+ if (!Environment.Is64BitProcess)
+ return;
+
+ bool allocatedFirst = false;
+ bool allocatedSecond = false;
+ IntPtr memBlockFirst = IntPtr.Zero;
+ IntPtr memBlockSecond = IntPtr.Zero;
+
+ // int.MaxValue - (int.MaxValue % 4) => 2147483644, largest multiple of 4 less than int.MaxValue
+ // CLR default limit of 2 gigabytes (GB).
+ // 1610612734, larger than MaximumEncodeLength, requires output buffer of size 2147483648 (which is > int.MaxValue)
+ const int sourceCount = (int.MaxValue >> 2) * 3 + 1;
+ const int encodedCount = 2000000000;
+
+ try
+ {
+ allocatedFirst = AllocationHelper.TryAllocNative((IntPtr)sourceCount, out memBlockFirst);
+ allocatedSecond = AllocationHelper.TryAllocNative((IntPtr)encodedCount, out memBlockSecond);
+ if (allocatedFirst && allocatedSecond)
+ {
+ unsafe
+ {
+ var source = new Span<byte>(memBlockFirst.ToPointer(), sourceCount);
+ var encodedBytes = new Span<byte>(memBlockSecond.ToPointer(), encodedCount);
+
+ Assert.Equal(OperationStatus.DestinationTooSmall, Base64.EncodeToUtf8(source, encodedBytes, out int consumed, out int encodedBytesCount, isFinalBlock));
+ Assert.Equal((encodedBytes.Length >> 2) * 3, consumed); // encoding 1500000000 bytes fits into buffer of 2000000000 bytes
+ Assert.Equal(encodedBytes.Length, encodedBytesCount);
+ }
+ }
+ }
+ finally
+ {
+ if (allocatedFirst)
+ AllocationHelper.ReleaseNative(ref memBlockFirst);
+ if (allocatedSecond)
+ AllocationHelper.ReleaseNative(ref memBlockSecond);
+ }
+ }
+
[Fact]
public void GetMaxEncodedLength()
{