Base64Encoder
Represents base64 encoder suitable for encoding large binary
data using streaming approach.
using DotNext.IO;
using DotNext.Text;
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Unicode;
using System.Threading;
namespace DotNext.Buffers.Text
{
[StructLayout(LayoutKind.Auto)]
[DebuggerDisplay("BufferedDataSize = {BufferedDataSize}, BufferedData = {BufferedData}")]
public struct Base64Encoder
{
public const int MaxBufferedDataSize = 2;
public const int MaxCharsToFlush = 4;
public const int MaxInputSize = 1610612733;
private const int DecodingBufferSize = 258;
private const int EncodingBufferSize = 344;
private ushort reservedBuffer;
private int reservedBufferSize;
public bool HasBufferedData {
[IsReadOnly]
get {
return reservedBufferSize > 0;
}
}
public int BufferedDataSize {
[IsReadOnly]
get {
return reservedBufferSize;
}
}
[System.Runtime.CompilerServices.Nullable(1)]
[ExcludeFromCodeCoverage]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private string BufferedData {
[IsReadOnly]
get {
ReadOnlySpan<byte> bytes = AsReadOnlyBytes(ref reservedBuffer, reservedBufferSize);
if (!bytes.IsEmpty)
return Convert.ToBase64String(bytes, Base64FormattingOptions.None);
return string.Empty;
}
}
[IsReadOnly]
public int GetBufferedData([LifetimeAnnotation(false, true)] Span<byte> output)
{
AsReadOnlyBytes(ref reservedBuffer, reservedBufferSize).CopyTo(output);
return reservedBufferSize;
}
public void Reset()
{
reservedBufferSize = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ReadOnlySpan<byte> AsReadOnlyBytes([In] [IsReadOnly] ref ushort value, int length)
{
return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<ushort, byte>(ref Unsafe.AsRef(ref value)), length);
}
private void EncodeToCharsCore<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [LifetimeAnnotation(true, false)] ref TWriter writer, bool flush) where TWriter : IBufferWriter<char>
{
int charsWritten = bytes.Length % 3;
if ((charsWritten == 0) | flush)
Reset();
else {
charsWritten = bytes.Length - charsWritten;
ReadOnlySpan<byte> readOnlySpan = bytes.Slice(charsWritten);
readOnlySpan.CopyTo(Span.AsBytes(ref reservedBuffer));
reservedBufferSize = readOnlySpan.Length;
bytes = bytes.Slice(0, charsWritten);
}
Convert.TryToBase64Chars(bytes, ((IBufferWriter<char>)writer).GetSpan(Base64.GetMaxEncodedToUtf8Length(bytes.Length)), out charsWritten, Base64FormattingOptions.None);
((IBufferWriter<char>)writer).Advance(charsWritten);
}
[SkipLocalsInit]
private unsafe void CopyAndEncodeToChars<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [LifetimeAnnotation(true, false)] ref TWriter writer, bool flush) where TWriter : IBufferWriter<char>
{
int num = reservedBufferSize + bytes.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);
bytes.CopyTo(memoryRental2.Span.Slice(reservedBufferSize));
EncodeToCharsCore(memoryRental2.Span, ref writer, flush);
} finally {
memoryRental2.Dispose();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EncodeToChars<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [LifetimeAnnotation(true, false)] ref TWriter writer, bool flush) where TWriter : IBufferWriter<char>
{
if (HasBufferedData)
CopyAndEncodeToChars(bytes, ref writer, flush);
else
EncodeToCharsCore(bytes, ref writer, flush);
}
public void EncodeToChars([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [System.Runtime.CompilerServices.Nullable(1)] IBufferWriter<char> output, bool flush = false)
{
ArgumentNullException.ThrowIfNull((object)output, "output");
if (bytes.Length >= 1610612733)
throw new ArgumentException(ExceptionMessages.LargeBuffer, "bytes");
EncodeToChars(bytes, ref output, flush);
}
public MemoryOwner<char> EncodeToChars([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [System.Runtime.CompilerServices.Nullable(2)] MemoryAllocator<char> allocator = null, bool flush = false)
{
if (bytes.Length >= 1610612733)
throw new ArgumentException(ExceptionMessages.LargeBuffer, "bytes");
MemoryOwnerWrapper<char> writer = new MemoryOwnerWrapper<char>(allocator);
EncodeToChars(bytes, ref writer, flush);
return writer.Buffer;
}
[SkipLocalsInit]
private unsafe void EncodeToCharsCore<TConsumer>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, TConsumer output, bool flush) where TConsumer : IReadOnlySpanConsumer<char>
{
Span<char> span = new Span<char>((void*)stackalloc byte[688], 344);
int producedChars;
while (true) {
ReadOnlySpan<byte> input = bytes.TrimLength(258);
if (<EncodeToCharsCore>g__Encode|21_0<TConsumer>(input, span, out int consumedBytes, out producedChars))
Reset();
else {
reservedBufferSize = input.Length - consumedBytes;
input.Slice(consumedBytes).CopyTo(Span.AsBytes(ref reservedBuffer));
}
if (consumedBytes <= 0 || producedChars <= 0)
break;
((IReadOnlySpanConsumer<char>)output).Invoke((ReadOnlySpan<char>)span.Slice(0, producedChars));
bytes = bytes.Slice(consumedBytes);
}
if (HasBufferedData & flush) {
Convert.TryToBase64Chars(AsReadOnlyBytes(ref reservedBuffer, reservedBufferSize), span, out producedChars, Base64FormattingOptions.None);
Reset();
((IReadOnlySpanConsumer<char>)output).Invoke((ReadOnlySpan<char>)span.Slice(0, producedChars));
}
}
[SkipLocalsInit]
private unsafe void CopyAndEncodeToChars<TConsumer>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, TConsumer output, bool flush) where TConsumer : IReadOnlySpanConsumer<char>
{
int num = reservedBufferSize + bytes.Length;
MemoryRental<byte> memoryRental;
if ((uint)num > (uint)MemoryRental<char>.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);
bytes.CopyTo(memoryRental2.Span.Slice(reservedBufferSize));
EncodeToCharsCore(memoryRental2.Span, output, flush);
} finally {
memoryRental2.Dispose();
}
}
[System.Runtime.CompilerServices.NullableContext(1)]
public void EncodeToChars<TConsumer>([LifetimeAnnotation(false, true)] [System.Runtime.CompilerServices.Nullable(0)] ReadOnlySpan<byte> bytes, TConsumer output, bool flush = false) where TConsumer : IReadOnlySpanConsumer<char>
{
if (HasBufferedData)
CopyAndEncodeToChars(bytes, output, flush);
else
EncodeToCharsCore(bytes, output, flush);
}
[System.Runtime.CompilerServices.NullableContext(1)]
public void EncodeToChars<[System.Runtime.CompilerServices.Nullable(2)] TArg>([LifetimeAnnotation(false, true)] [System.Runtime.CompilerServices.Nullable(0)] ReadOnlySpan<byte> bytes, ReadOnlySpanAction<char, TArg> output, TArg arg, bool flush = false)
{
EncodeToChars(bytes, new DelegatingReadOnlySpanConsumer<char, TArg>(output, arg), flush);
}
[CLSCompliant(false)]
public void EncodeToChars<[System.Runtime.CompilerServices.Nullable(2)] TArg>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
0,
1
})] IntPtr output, [System.Runtime.CompilerServices.Nullable(1)] TArg arg, bool flush = false)
{
EncodeToChars(bytes, new ReadOnlySpanConsumer<char, TArg>(output, arg), flush);
}
public void EncodeToChars([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [System.Runtime.CompilerServices.Nullable(1)] TextWriter output, bool flush = false)
{
EncodeToChars(bytes, (TextConsumer)output, flush);
}
public void EncodeToChars([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [System.Runtime.CompilerServices.Nullable(1)] StringBuilder output, bool flush = false)
{
EncodeToChars(bytes, (StringBuilderConsumer)output, flush);
}
[System.Runtime.CompilerServices.NullableContext(2)]
[AsyncIteratorStateMachine(typeof(<EncodeToCharsAsync>d__28))]
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
1,
0
})]
public static IAsyncEnumerable<ReadOnlyMemory<char>> EncodeToCharsAsync([System.Runtime.CompilerServices.Nullable(new byte[] {
1,
0
})] IAsyncEnumerable<ReadOnlyMemory<byte>> bytes, MemoryAllocator<char> allocator = null, [EnumeratorCancellation] CancellationToken token = default(CancellationToken))
{
<EncodeToCharsAsync>d__28 <EncodeToCharsAsync>d__ = new <EncodeToCharsAsync>d__28(-2);
<EncodeToCharsAsync>d__.<>3__bytes = bytes;
<EncodeToCharsAsync>d__.<>3__allocator = allocator;
<EncodeToCharsAsync>d__.<>3__token = token;
return <EncodeToCharsAsync>d__;
}
public unsafe int Flush([LifetimeAnnotation(false, true)] Span<char> output)
{
int charsWritten;
if (reservedBufferSize == 0 || output.IsEmpty)
charsWritten = 0;
else {
Span<byte> output2 = new Span<byte>(stackalloc byte[4], 4);
charsWritten = Flush(output2);
Utf8.ToUtf16(output2.Slice(0, charsWritten), output, out int _, out charsWritten, true, true);
}
return charsWritten;
}
private void EncodeToUtf8Core<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [LifetimeAnnotation(true, false)] ref TWriter writer, bool flush) where TWriter : IBufferWriter<byte>
{
int bytesWritten = Base64.GetMaxEncodedToUtf8Length(bytes.Length);
Span<byte> span = ((IBufferWriter<byte>)writer).GetSpan(bytesWritten);
int bytesConsumed;
OperationStatus operationStatus = Base64.EncodeToUtf8(bytes, span, out bytesConsumed, out bytesWritten, (bytes.Length % 3 == 0) | flush);
if ((uint)operationStatus > 1) {
if (operationStatus == OperationStatus.NeedMoreData) {
reservedBufferSize = bytes.Length - bytesConsumed;
bytes.Slice(bytesConsumed).CopyTo(Span.AsBytes(ref reservedBuffer));
}
} else
Reset();
((IBufferWriter<byte>)writer).Advance(bytesWritten);
}
[SkipLocalsInit]
private unsafe void CopyAndEncodeToUtf8<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [LifetimeAnnotation(true, false)] ref TWriter writer, bool flush) where TWriter : IBufferWriter<byte>
{
int num = reservedBufferSize + bytes.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);
bytes.CopyTo(memoryRental2.Span.Slice(reservedBufferSize));
EncodeToUtf8Core(memoryRental2.Span, ref writer, flush);
} finally {
memoryRental2.Dispose();
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void EncodeToUtf8<TWriter>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [LifetimeAnnotation(true, false)] ref TWriter writer, bool flush) where TWriter : IBufferWriter<byte>
{
if (HasBufferedData)
CopyAndEncodeToUtf8(bytes, ref writer, flush);
else
EncodeToUtf8Core(bytes, ref writer, flush);
}
public void EncodeToUtf8([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [System.Runtime.CompilerServices.Nullable(1)] IBufferWriter<byte> output, bool flush = false)
{
ArgumentNullException.ThrowIfNull((object)output, "output");
if (bytes.Length > 1610612733)
throw new ArgumentException(ExceptionMessages.LargeBuffer, "bytes");
EncodeToUtf8(bytes, ref output, flush);
}
public MemoryOwner<byte> EncodeToUtf8([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [System.Runtime.CompilerServices.Nullable(2)] MemoryAllocator<byte> allocator = null, bool flush = false)
{
if (bytes.Length > 1610612733)
throw new ArgumentException(ExceptionMessages.LargeBuffer, "bytes");
MemoryOwnerWrapper<byte> writer = new MemoryOwnerWrapper<byte>(allocator);
EncodeToUtf8(bytes, ref writer, flush);
return writer.Buffer;
}
[SkipLocalsInit]
private unsafe void EncodeToUtf8Core<TConsumer>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, TConsumer output, bool flush) where TConsumer : IReadOnlySpanConsumer<byte>
{
Span<byte> utf = new Span<byte>((void*)stackalloc byte[344], 344);
int bytesConsumed;
int bytesWritten;
while (true) {
OperationStatus operationStatus = Base64.EncodeToUtf8(bytes, utf, out bytesConsumed, out bytesWritten, bytes.Length % 3 == 0);
if ((uint)operationStatus > 1) {
if (operationStatus == OperationStatus.NeedMoreData) {
reservedBufferSize = bytes.Length - bytesConsumed;
bytes.Slice(bytesConsumed).CopyTo(Span.AsBytes(ref reservedBuffer));
}
} else
Reset();
if (bytesWritten <= 0 || bytesConsumed <= 0)
break;
((IReadOnlySpanConsumer<byte>)output).Invoke((ReadOnlySpan<byte>)utf.Slice(0, bytesWritten));
bytes = bytes.Slice(bytesConsumed);
}
if (HasBufferedData & flush) {
Base64.EncodeToUtf8(AsReadOnlyBytes(ref reservedBuffer, reservedBufferSize), utf, out bytesConsumed, out bytesWritten, true);
Reset();
((IReadOnlySpanConsumer<byte>)output).Invoke((ReadOnlySpan<byte>)utf.Slice(0, bytesWritten));
}
}
[SkipLocalsInit]
private unsafe void CopyAndEncodeToUtf8<TConsumer>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, TConsumer output, bool flush) where TConsumer : IReadOnlySpanConsumer<byte>
{
int num = reservedBufferSize + bytes.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);
bytes.CopyTo(memoryRental2.Span.Slice(reservedBufferSize));
EncodeToUtf8Core(memoryRental2.Span, output, flush);
} finally {
memoryRental2.Dispose();
}
}
[System.Runtime.CompilerServices.NullableContext(1)]
public void EncodeToUtf8<TConsumer>([LifetimeAnnotation(false, true)] [System.Runtime.CompilerServices.Nullable(0)] ReadOnlySpan<byte> bytes, TConsumer output, bool flush = false) where TConsumer : IReadOnlySpanConsumer<byte>
{
if (HasBufferedData)
CopyAndEncodeToUtf8(bytes, output, flush);
else
EncodeToUtf8Core(bytes, output, flush);
}
[System.Runtime.CompilerServices.NullableContext(1)]
public void EncodeToUtf8<[System.Runtime.CompilerServices.Nullable(2)] TArg>([LifetimeAnnotation(false, true)] [System.Runtime.CompilerServices.Nullable(0)] ReadOnlySpan<byte> bytes, ReadOnlySpanAction<byte, TArg> output, TArg arg, bool flush = false)
{
EncodeToUtf8(bytes, new DelegatingReadOnlySpanConsumer<byte, TArg>(output, arg), flush);
}
[CLSCompliant(false)]
public void EncodeToUtf8<[System.Runtime.CompilerServices.Nullable(2)] TArg>([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [System.Runtime.CompilerServices.Nullable(new byte[] {
0,
0,
1
})] IntPtr output, [System.Runtime.CompilerServices.Nullable(1)] TArg arg, bool flush = false)
{
EncodeToUtf8(bytes, new ReadOnlySpanConsumer<byte, TArg>(output, arg), flush);
}
public void EncodeToUtf8([LifetimeAnnotation(false, true)] ReadOnlySpan<byte> bytes, [System.Runtime.CompilerServices.Nullable(1)] Stream output, bool flush = false)
{
EncodeToUtf8(bytes, (StreamConsumer)output, flush);
}
[System.Runtime.CompilerServices.NullableContext(2)]
[AsyncIteratorStateMachine(typeof(<EncodeToUtf8Async>d__41))]
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
1,
0
})]
public static IAsyncEnumerable<ReadOnlyMemory<byte>> EncodeToUtf8Async([System.Runtime.CompilerServices.Nullable(new byte[] {
1,
0
})] IAsyncEnumerable<ReadOnlyMemory<byte>> bytes, MemoryAllocator<byte> allocator = null, [EnumeratorCancellation] CancellationToken token = default(CancellationToken))
{
<EncodeToUtf8Async>d__41 <EncodeToUtf8Async>d__ = new <EncodeToUtf8Async>d__41(-2);
<EncodeToUtf8Async>d__.<>3__bytes = bytes;
<EncodeToUtf8Async>d__.<>3__allocator = allocator;
<EncodeToUtf8Async>d__.<>3__token = token;
return <EncodeToUtf8Async>d__;
}
public int Flush([LifetimeAnnotation(false, true)] Span<byte> output)
{
int bytesWritten;
if (reservedBufferSize != 0 && !output.IsEmpty) {
Base64.EncodeToUtf8(AsReadOnlyBytes(ref reservedBuffer, reservedBufferSize), output, out int bytesConsumed, out bytesWritten, true);
reservedBufferSize -= bytesConsumed;
return bytesWritten;
}
bytesWritten = 0;
return bytesWritten;
}
}
}