Base64Decoder
Represents base64 decoder suitable for decoding large base64-encoded binary
data using streaming approach.
using DotNext.IO;
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace DotNext.Buffers.Text
{
[StructLayout(LayoutKind.Auto)]
[DebuggerDisplay("NeedMoreData = {NeedMoreData}")]
public struct Base64Decoder : IResettable
{
private const int DecodingBufferSize = 258;
private const int GotPaddingFlag = -1;
private ulong reservedBuffer;
private int reservedBufferSize;
private const char PaddingChar = '=';
private const byte PaddingByte = 61;
public bool NeedMoreData {
[IsReadOnly]
get {
return reservedBufferSize > 0;
}
}
public void Reset()
{
reservedBufferSize = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Span<char> AsChars(ref ulong value)
{
return MemoryMarshal.CreateSpan(ref Unsafe.As<ulong, char>(ref value), 4);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ReadOnlySpan<char> AsChars([In] [IsReadOnly] ref ulong value, int length)
{
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ulong, char>(ref Unsafe.AsRef(ref value)), length);
}
private bool DecodeFromUtf16Core<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<char> chars, [LifetimeAnnotation(true, false)] ref TWriter writer) where TWriter : IBufferWriter<byte>
{
int bytesWritten = chars.Length & 3;
if (bytesWritten != 0) {
bytesWritten = chars.Length - bytesWritten;
ReadOnlySpan<char> readOnlySpan = chars.Slice(bytesWritten);
readOnlySpan.CopyTo(AsChars(ref reservedBuffer));
reservedBufferSize = readOnlySpan.Length;
chars = chars.Slice(0, bytesWritten);
} else {
int length = chars.Length;
if (length >= 1 && chars[length - 1] == '=')
reservedBufferSize = -1;
else
reservedBufferSize = 0;
}
bool num = Convert.TryFromBase64Chars(chars, ((IBufferWriter<byte>)writer).GetSpan(chars.Length), out bytesWritten);
if (num)
((IBufferWriter<byte>)writer).Advance(bytesWritten);
return num;
}
[SkipLocalsInit]
private unsafe bool CopyAndDecodeFromUtf16<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<char> chars, [LifetimeAnnotation(true, false)] ref TWriter writer) where TWriter : IBufferWriter<byte>
{
int num = reservedBufferSize + chars.Length;
MemoryRental<char> memoryRental;
if ((uint)num > (uint)MemoryRental<char>.StackallocThreshold)
memoryRental = new MemoryRental<char>(num, true);
else {
int num2 = num;
memoryRental = new Span<char>((void*)stackalloc byte[(int)checked(unchecked((ulong)(uint)num2) * 2)], num2);
}
MemoryRental<char> memoryRental2 = memoryRental;
try {
AsChars(ref reservedBuffer, reservedBufferSize).CopyTo(memoryRental2.Span);
chars.CopyTo(memoryRental2.Span.Slice(reservedBufferSize));
return DecodeFromUtf16Core(memoryRental2.Span, ref writer);
} finally {
memoryRental2.Dispose();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool DecodeFromUtf16<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<char> chars, [LifetimeAnnotation(true, false)] ref TWriter writer) where TWriter : IBufferWriter<byte>
{
switch (reservedBufferSize) {
case -1:
return false;
case 0:
return DecodeFromUtf16Core(chars, ref writer);
default:
return CopyAndDecodeFromUtf16(chars, ref writer);
}
}
public void DecodeFromUtf16([LifetimeAnnotation(false, true)] ReadOnlySpan<char> chars, [System.Runtime.CompilerServices.Nullable(1)] IBufferWriter<byte> output)
{
ArgumentNullException.ThrowIfNull((object)output, "output");
if (!DecodeFromUtf16(chars, ref output))
throw new FormatException(ExceptionMessages.MalformedBase64);
}
public void DecodeFromUtf16([In] [LifetimeAnnotation(true, false)] [IsReadOnly] ref ReadOnlySequence<char> chars, [System.Runtime.CompilerServices.Nullable(1)] IBufferWriter<byte> output)
{
ArgumentNullException.ThrowIfNull((object)output, "output");
ReadOnlySequence<char>.Enumerator enumerator = chars.GetEnumerator();
do {
if (!enumerator.MoveNext())
return;
} while (DecodeFromUtf16(enumerator.Current.Span, ref output));
throw new FormatException(ExceptionMessages.MalformedBase64);
}
public MemoryOwner<byte> DecodeFromUtf16([LifetimeAnnotation(false, true)] ReadOnlySpan<char> chars, [System.Runtime.CompilerServices.Nullable(2)] MemoryAllocator<byte> allocator = null)
{
MemoryOwnerWrapper<byte> writer = new MemoryOwnerWrapper<byte>(allocator);
if (chars.IsEmpty || DecodeFromUtf16(chars, ref writer))
return writer.Buffer;
writer.Buffer.Dispose();
throw new FormatException(ExceptionMessages.MalformedBase64);
}
[SkipLocalsInit]
private unsafe void DecodeFromUtf16Core<TConsumer>([LifetimeAnnotation(false, true)] ReadOnlySpan<char> chars, TConsumer output) where TConsumer : IReadOnlySpanConsumer<byte>
{
Span<byte> output2 = new Span<byte>((void*)stackalloc byte[258], 258);
do {
ReadOnlySpan<char> input = chars.TrimLength(344);
if (!<DecodeFromUtf16Core>g__Decode|16_0<TConsumer>(input, output2, out int consumedChars, out int producedBytes, ref reservedBufferSize)) {
reservedBufferSize = input.Length - consumedChars;
input.Slice(consumedChars).CopyTo(AsChars(ref reservedBuffer));
}
if (consumedChars == 0 || producedBytes == 0)
break;
((IReadOnlySpanConsumer<byte>)output).Invoke((ReadOnlySpan<byte>)output2.Slice(0, producedBytes));
chars = chars.Slice(consumedChars);
} while (reservedBufferSize != -1);
}
[SkipLocalsInit]
private unsafe void CopyAndDecodeFromUtf16<TConsumer>([LifetimeAnnotation(false, true)] ReadOnlySpan<char> chars, TConsumer output) where TConsumer : IReadOnlySpanConsumer<byte>
{
int num = reservedBufferSize + chars.Length;
MemoryRental<char> memoryRental;
if ((uint)num > (uint)MemoryRental<char>.StackallocThreshold)
memoryRental = new MemoryRental<char>(num, true);
else {
int num2 = num;
memoryRental = new Span<char>((void*)stackalloc byte[(int)checked(unchecked((ulong)(uint)num2) * 2)], num2);
}
MemoryRental<char> memoryRental2 = memoryRental;
try {
AsChars(ref reservedBuffer, reservedBufferSize).CopyTo(memoryRental2.Span);
chars.CopyTo(memoryRental2.Span.Slice(reservedBufferSize));
DecodeFromUtf16Core(memoryRental2.Span, output);
} finally {
memoryRental2.Dispose();
}
}
[System.Runtime.CompilerServices.NullableContext(1)]
public void DecodeFromUtf16<TConsumer>([LifetimeAnnotation(false, true)] [System.Runtime.CompilerServices.Nullable(0)] ReadOnlySpan<char> chars, TConsumer output) where TConsumer : IReadOnlySpanConsumer<byte>
{
switch (reservedBufferSize) {
case -1:
throw new FormatException(ExceptionMessages.MalformedBase64);
case 0:
DecodeFromUtf16Core(chars, output);
break;
default:
CopyAndDecodeFromUtf16(chars, output);
break;
}
}
[System.Runtime.CompilerServices.NullableContext(1)]
public void DecodeFromUtf16<[System.Runtime.CompilerServices.Nullable(2)] TArg>([LifetimeAnnotation(false, true)] [System.Runtime.CompilerServices.Nullable(0)] ReadOnlySpan<char> chars, ReadOnlySpanAction<byte, TArg> callback, TArg arg)
{
DecodeFromUtf16(chars, new DelegatingReadOnlySpanConsumer<byte, TArg>(callback, arg));
}
[CLSCompliant(false)]
public void DecodeFromUtf16<[System.Runtime.CompilerServices.Nullable(2)] TArg>([LifetimeAnnotation(false, true)] ReadOnlySpan<char> chars, [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
0,
1
})] IntPtr callback, [System.Runtime.CompilerServices.Nullable(1)] TArg arg)
{
DecodeFromUtf16(chars, new ReadOnlySpanConsumer<byte, TArg>(callback, arg));
}
[System.Runtime.CompilerServices.NullableContext(2)]
[AsyncIteratorStateMachine(typeof(<DecodeFromUtf16Async>d__21))]
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
1,
0
})]
public static IAsyncEnumerable<ReadOnlyMemory<byte>> DecodeFromUtf16Async([System.Runtime.CompilerServices.Nullable(new byte[] {
1,
0
})] IAsyncEnumerable<ReadOnlyMemory<char>> chars, MemoryAllocator<byte> allocator = null, [EnumeratorCancellation] CancellationToken token = default(CancellationToken))
{
<DecodeFromUtf16Async>d__21 <DecodeFromUtf16Async>d__ = new <DecodeFromUtf16Async>d__21(-2);
<DecodeFromUtf16Async>d__.<>3__chars = chars;
<DecodeFromUtf16Async>d__.<>3__allocator = allocator;
<DecodeFromUtf16Async>d__.<>3__token = token;
return <DecodeFromUtf16Async>d__;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ReadOnlySpan<byte> AsReadOnlyBytes([In] [IsReadOnly] ref ulong value, int length)
{
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ulong, byte>(ref Unsafe.AsRef(ref value)), length);
}
private bool DecodeFromUtf8Core<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> utf8Chars, [LifetimeAnnotation(true, false)] ref TWriter writer) where TWriter : IBufferWriter<byte>
{
int bytesWritten = Base64.GetMaxDecodedFromUtf8Length(utf8Chars.Length);
Span<byte> span = ((IBufferWriter<byte>)writer).GetSpan(bytesWritten);
int bytesConsumed;
switch (Base64.DecodeFromUtf8(utf8Chars, span, out bytesConsumed, out bytesWritten, (utf8Chars.Length & 3) == 0)) {
default:
return false;
case OperationStatus.Done: {
int length = utf8Chars.Length;
reservedBufferSize = ((length >= 1 && utf8Chars[length - 1] == 61) ? (-1) : 0);
break;
}
case OperationStatus.DestinationTooSmall:
reservedBufferSize = 0;
break;
case OperationStatus.NeedMoreData:
reservedBufferSize = utf8Chars.Length - bytesConsumed;
utf8Chars.Slice(bytesConsumed).CopyTo(Span.AsBytes(ref reservedBuffer));
break;
}
((IBufferWriter<byte>)writer).Advance(bytesWritten);
return true;
}
[SkipLocalsInit]
private unsafe bool CopyAndDecodeFromUtf8<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> utf8Chars, [LifetimeAnnotation(true, false)] ref TWriter writer) where TWriter : IBufferWriter<byte>
{
int num = reservedBufferSize + utf8Chars.Length;
MemoryRental<byte> memoryRental;
if ((uint)num > (uint)MemoryRental<byte>.StackallocThreshold)
memoryRental = new MemoryRental<byte>(num, true);
else {
int num2 = num;
memoryRental = new Span<byte>((void*)stackalloc byte[(int)(uint)num2], num2);
}
MemoryRental<byte> memoryRental2 = memoryRental;
try {
AsReadOnlyBytes(ref reservedBuffer, reservedBufferSize).CopyTo(memoryRental2.Span);
utf8Chars.CopyTo(memoryRental2.Span.Slice(reservedBufferSize));
return DecodeFromUtf8Core(memoryRental2.Span, ref writer);
} finally {
memoryRental2.Dispose();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool DecodeFromUtf8<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> utf8Chars, [LifetimeAnnotation(true, false)] ref TWriter writer) where TWriter : IBufferWriter<byte>
{
switch (reservedBufferSize) {
case -1:
return false;
case 0:
return DecodeFromUtf8Core(utf8Chars, ref writer);
default:
return CopyAndDecodeFromUtf8(utf8Chars, ref writer);
}
}
public void DecodeFromUtf8([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> utf8Chars, [System.Runtime.CompilerServices.Nullable(1)] IBufferWriter<byte> output)
{
ArgumentNullException.ThrowIfNull((object)output, "output");
if (!DecodeFromUtf8(utf8Chars, ref output))
throw new FormatException(ExceptionMessages.MalformedBase64);
}
public void DecodeFromUtf8([In] [LifetimeAnnotation(true, false)] [IsReadOnly] ref ReadOnlySequence<byte> utf8Chars, [System.Runtime.CompilerServices.Nullable(1)] IBufferWriter<byte> output)
{
ArgumentNullException.ThrowIfNull((object)output, "output");
ReadOnlySequence<byte>.Enumerator enumerator = utf8Chars.GetEnumerator();
do {
if (!enumerator.MoveNext())
return;
} while (DecodeFromUtf8(enumerator.Current.Span, ref output));
throw new FormatException(ExceptionMessages.MalformedBase64);
}
public MemoryOwner<byte> DecodeFromUtf8([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> utf8Chars, [System.Runtime.CompilerServices.Nullable(2)] MemoryAllocator<byte> allocator = null)
{
MemoryOwnerWrapper<byte> writer = new MemoryOwnerWrapper<byte>(allocator);
if (utf8Chars.IsEmpty || DecodeFromUtf8(utf8Chars, ref writer))
return writer.Buffer;
writer.Buffer.Dispose();
throw new FormatException(ExceptionMessages.MalformedBase64);
}
[SkipLocalsInit]
private unsafe void DecodeFromUtf8Core<TConsumer>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> utf8Chars, TConsumer output) where TConsumer : IReadOnlySpanConsumer<byte>
{
Span<byte> bytes = new Span<byte>((void*)stackalloc byte[258], 258);
do {
int bytesConsumed;
int bytesWritten;
switch (Base64.DecodeFromUtf8(utf8Chars, bytes, out bytesConsumed, out bytesWritten, (utf8Chars.Length & 3) == 0)) {
default:
throw new FormatException(ExceptionMessages.MalformedBase64);
case OperationStatus.Done: {
int length = utf8Chars.Length;
reservedBufferSize = ((length >= 1 && utf8Chars[length - 1] == 61) ? (-1) : 0);
break;
}
case OperationStatus.DestinationTooSmall:
reservedBufferSize = 0;
break;
case OperationStatus.NeedMoreData:
reservedBufferSize = utf8Chars.Length - bytesConsumed;
utf8Chars.Slice(bytesConsumed).CopyTo(Span.AsBytes(ref reservedBuffer));
break;
}
if (bytesWritten == 0 || bytesConsumed == 0)
break;
((IReadOnlySpanConsumer<byte>)output).Invoke((ReadOnlySpan<byte>)bytes.Slice(0, bytesWritten));
utf8Chars = utf8Chars.Slice(bytesConsumed);
} while (reservedBufferSize != -1);
}
[SkipLocalsInit]
private unsafe void CopyAndDecodeFromUtf8<TConsumer>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> utf8Chars, TConsumer output) where TConsumer : IReadOnlySpanConsumer<byte>
{
int num = reservedBufferSize + utf8Chars.Length;
MemoryRental<byte> memoryRental;
if ((uint)num > (uint)MemoryRental<byte>.StackallocThreshold)
memoryRental = new MemoryRental<byte>(num, true);
else {
int num2 = num;
memoryRental = new Span<byte>((void*)stackalloc byte[(int)(uint)num2], num2);
}
MemoryRental<byte> memoryRental2 = memoryRental;
try {
AsReadOnlyBytes(ref reservedBuffer, reservedBufferSize).CopyTo(memoryRental2.Span);
utf8Chars.CopyTo(memoryRental2.Span.Slice(reservedBufferSize));
DecodeFromUtf8Core(memoryRental2.Span, output);
} finally {
memoryRental2.Dispose();
}
}
[System.Runtime.CompilerServices.NullableContext(1)]
public void DecodeFromUtf8<TConsumer>([LifetimeAnnotation(false, true)] [System.Runtime.CompilerServices.Nullable(0)] ReadOnlySpan<byte> utf8Chars, TConsumer output) where TConsumer : IReadOnlySpanConsumer<byte>
{
switch (reservedBufferSize) {
case -1:
throw new FormatException(ExceptionMessages.MalformedBase64);
case 0:
DecodeFromUtf8Core(utf8Chars, output);
break;
default:
CopyAndDecodeFromUtf8(utf8Chars, output);
break;
}
}
[System.Runtime.CompilerServices.NullableContext(1)]
public void DecodeFromUtf8<[System.Runtime.CompilerServices.Nullable(2)] TArg>([LifetimeAnnotation(false, true)] [System.Runtime.CompilerServices.Nullable(0)] ReadOnlySpan<byte> utf8Chars, ReadOnlySpanAction<byte, TArg> output, TArg arg)
{
DecodeFromUtf8(utf8Chars, new DelegatingReadOnlySpanConsumer<byte, TArg>(output, arg));
}
[CLSCompliant(false)]
public void DecodeFromUtf8<[System.Runtime.CompilerServices.Nullable(2)] TArg>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> utf8Chars, [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
0,
1
})] IntPtr output, [System.Runtime.CompilerServices.Nullable(1)] TArg arg)
{
DecodeFromUtf8(utf8Chars, new ReadOnlySpanConsumer<byte, TArg>(output, arg));
}
public void DecodeFromUtf8([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> utf8Chars, [System.Runtime.CompilerServices.Nullable(1)] Stream output)
{
DecodeFromUtf8(utf8Chars, (StreamConsumer)output);
}
[System.Runtime.CompilerServices.NullableContext(2)]
[AsyncIteratorStateMachine(typeof(<DecodeFromUtf8Async>d__36))]
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
1,
0
})]
public static IAsyncEnumerable<ReadOnlyMemory<byte>> DecodeFromUtf8Async([System.Runtime.CompilerServices.Nullable(new byte[] {
1,
0
})] IAsyncEnumerable<ReadOnlyMemory<byte>> utf8Chars, MemoryAllocator<byte> allocator = null, [EnumeratorCancellation] CancellationToken token = default(CancellationToken))
{
<DecodeFromUtf8Async>d__36 <DecodeFromUtf8Async>d__ = new <DecodeFromUtf8Async>d__36(-2);
<DecodeFromUtf8Async>d__.<>3__utf8Chars = utf8Chars;
<DecodeFromUtf8Async>d__.<>3__allocator = allocator;
<DecodeFromUtf8Async>d__.<>3__token = token;
return <DecodeFromUtf8Async>d__;
}
}
}