Base64Decoder
Represents base64 decoder suitable for decoding large base64-encoded binary
data using streaming approach.
using DotNext.Runtime;
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
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 MaxBufferedChars = 3;
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;
}
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private Span<char> CharBuffer {
get {
return MemoryMarshal.CreateSpan(ref Unsafe.As<ulong, char>(ref reservedBuffer), 4);
}
}
private ReadOnlySpan<char> BufferedChars {
[IsReadOnly]
get {
return MemoryMarshal.CreateReadOnlySpan(ref Intrinsics.InToRef<ulong, char>(ref reservedBuffer), reservedBufferSize);
}
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
[UnscopedRef]
private Span<byte> ByteBuffer {
get {
return Span.AsBytes(ref reservedBuffer);
}
}
private ReadOnlySpan<byte> BufferedBytes {
[IsReadOnly]
get {
return MemoryMarshal.CreateReadOnlySpan(ref Intrinsics.InToRef<ulong, byte>(ref reservedBuffer), reservedBufferSize);
}
}
public void Reset()
{
reservedBufferSize = 0;
}
[IsReadOnly]
private int GetMaxDecodedLength(int length)
{
return Base64.GetMaxDecodedFromUtf8Length(length) + reservedBufferSize;
}
private bool DecodeFromUtf16Core([ScopedRef] ReadOnlySpan<char> chars, ref BufferWriterSlim<byte> writer)
{
int bytesWritten = chars.Length & 3;
if (bytesWritten != 0) {
bytesWritten = chars.Length - bytesWritten;
ReadOnlySpan<char> readOnlySpan = chars.Slice(bytesWritten);
readOnlySpan.CopyTo(CharBuffer);
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, writer.InternalGetSpan(chars.Length), out bytesWritten);
if (num)
writer.Advance(bytesWritten);
return num;
}
private unsafe bool DecodeFromUtf16Buffered([ScopedRef] ReadOnlySpan<char> chars, ref BufferWriterSlim<byte> bytes)
{
if (NeedMoreData) {
Span<char> span = new Span<char>(stackalloc byte[8], 4);
SpanWriter<char> spanWriter = new SpanWriter<char>(span);
spanWriter.Write(BufferedChars);
chars = chars.Slice(spanWriter.Write(chars));
if (!DecodeFromUtf16Core(chars, ref bytes))
return false;
}
if (!chars.IsEmpty)
return DecodeFromUtf16Core(chars, ref bytes);
return true;
}
public void DecodeFromUtf16([ScopedRef] ReadOnlySpan<char> chars, [Nullable(1)] IBufferWriter<byte> bytes)
{
ArgumentNullException.ThrowIfNull((object)bytes, "bytes");
if (reservedBufferSize != -1) {
int maxDecodedLength = GetMaxDecodedLength(chars.Length);
BufferWriterSlim<byte> bytes2 = new BufferWriterSlim<byte>(bytes.GetSpan(maxDecodedLength), null);
if (DecodeFromUtf16Buffered(chars, ref bytes2)) {
bytes.Advance(bytes2.WrittenCount);
return;
}
}
throw new FormatException(ExceptionMessages.MalformedBase64);
}
public MemoryOwner<byte> DecodeFromUtf16([ScopedRef] ReadOnlySpan<char> chars, [Nullable(2)] MemoryAllocator<byte> allocator = null)
{
if (reservedBufferSize != -1) {
BufferWriterSlim<byte> bytes = new BufferWriterSlim<byte>(GetMaxDecodedLength(chars.Length), allocator);
if (DecodeFromUtf16Buffered(chars, ref bytes))
return bytes.DetachOrCopyBuffer();
bytes.Dispose();
}
throw new FormatException(ExceptionMessages.MalformedBase64);
}
public void DecodeFromUtf16(ReadOnlySpan<char> chars, ref BufferWriterSlim<byte> bytes)
{
if (reservedBufferSize == -1 || !DecodeFromUtf16Buffered(chars, ref bytes))
throw new FormatException(ExceptionMessages.MalformedBase64);
}
[NullableContext(2)]
[AsyncIteratorStateMachine(typeof(<DecodeFromUtf16Async>d__22))]
[return: Nullable(new byte[] {
1,
0
})]
public static IAsyncEnumerable<ReadOnlyMemory<byte>> DecodeFromUtf16Async([Nullable(new byte[] {
1,
0
})] IAsyncEnumerable<ReadOnlyMemory<char>> chars, MemoryAllocator<byte> allocator = null, [EnumeratorCancellation] CancellationToken token = default(CancellationToken))
{
<DecodeFromUtf16Async>d__22 <DecodeFromUtf16Async>d__ = new <DecodeFromUtf16Async>d__22(-2);
<DecodeFromUtf16Async>d__.<>3__chars = chars;
<DecodeFromUtf16Async>d__.<>3__allocator = allocator;
<DecodeFromUtf16Async>d__.<>3__token = token;
return <DecodeFromUtf16Async>d__;
}
private bool DecodeFromUtf8Core([ScopedRef] ReadOnlySpan<byte> chars, ref BufferWriterSlim<byte> bytes)
{
int maxDecodedFromUtf8Length = Base64.GetMaxDecodedFromUtf8Length(chars.Length);
Span<byte> span = bytes.InternalGetSpan(maxDecodedFromUtf8Length);
int num = default(int);
switch (Base64.DecodeFromUtf8(chars, span, ref num, ref maxDecodedFromUtf8Length, (chars.Length & 3) == 0)) {
default:
return false;
case OperationStatus.Done: {
int length = chars.Length;
reservedBufferSize = ((length >= 1 && chars[length - 1] == 61) ? (-1) : 0);
break;
}
case OperationStatus.DestinationTooSmall:
reservedBufferSize = 0;
break;
case OperationStatus.NeedMoreData:
reservedBufferSize = chars.Length - num;
chars.Slice(num).CopyTo(ByteBuffer);
break;
}
bytes.Advance(maxDecodedFromUtf8Length);
return true;
}
private unsafe bool DecodeFromUtf8Buffered([ScopedRef] ReadOnlySpan<byte> chars, ref BufferWriterSlim<byte> bytes)
{
if (NeedMoreData) {
Span<byte> span = new Span<byte>(stackalloc byte[4], 4);
SpanWriter<byte> spanWriter = new SpanWriter<byte>(span);
spanWriter.Write(BufferedBytes);
chars = chars.Slice(spanWriter.Write(chars));
if (!DecodeFromUtf8Core(chars, ref bytes))
return false;
}
if (!chars.IsEmpty)
return DecodeFromUtf8Core(chars, ref bytes);
return true;
}
public void DecodeFromUtf8(ReadOnlySpan<byte> chars, [Nullable(1)] IBufferWriter<byte> bytes)
{
ArgumentNullException.ThrowIfNull((object)bytes, "bytes");
if (reservedBufferSize != -1) {
int maxDecodedLength = GetMaxDecodedLength(chars.Length);
BufferWriterSlim<byte> bytes2 = new BufferWriterSlim<byte>(bytes.GetSpan(maxDecodedLength), null);
if (DecodeFromUtf8Buffered(chars, ref bytes2)) {
bytes.Advance(bytes2.WrittenCount);
return;
}
}
throw new FormatException(ExceptionMessages.MalformedBase64);
}
public MemoryOwner<byte> DecodeFromUtf8(ReadOnlySpan<byte> chars, [Nullable(2)] MemoryAllocator<byte> allocator = null)
{
if (reservedBufferSize != -1) {
BufferWriterSlim<byte> bytes = new BufferWriterSlim<byte>(GetMaxDecodedLength(chars.Length), allocator);
if (DecodeFromUtf8Buffered(chars, ref bytes))
return bytes.DetachOrCopyBuffer();
bytes.Dispose();
}
throw new FormatException(ExceptionMessages.MalformedBase64);
}
public void DecodeFromUtf8(ReadOnlySpan<byte> chars, ref BufferWriterSlim<byte> bytes)
{
if (reservedBufferSize == -1 || !DecodeFromUtf8Buffered(chars, ref bytes))
throw new FormatException(ExceptionMessages.MalformedBase64);
}
[NullableContext(2)]
[AsyncIteratorStateMachine(typeof(<DecodeFromUtf8Async>d__29))]
[return: Nullable(new byte[] {
1,
0
})]
public static IAsyncEnumerable<ReadOnlyMemory<byte>> DecodeFromUtf8Async([Nullable(new byte[] {
1,
0
})] IAsyncEnumerable<ReadOnlyMemory<byte>> utf8Chars, MemoryAllocator<byte> allocator = null, [EnumeratorCancellation] CancellationToken token = default(CancellationToken))
{
<DecodeFromUtf8Async>d__29 <DecodeFromUtf8Async>d__ = new <DecodeFromUtf8Async>d__29(-2);
<DecodeFromUtf8Async>d__.<>3__utf8Chars = utf8Chars;
<DecodeFromUtf8Async>d__.<>3__allocator = allocator;
<DecodeFromUtf8Async>d__.<>3__token = token;
return <DecodeFromUtf8Async>d__;
}
}
}