Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vectorize Convert.ToBase64String #71795

Merged
merged 17 commits into from
Jul 10, 2022
1 change: 1 addition & 0 deletions src/libraries/System.Memory/ref/System.Memory.Forwards.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Buffers.MemoryHandle))]
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Buffers.MemoryManager<>))]
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Buffers.OperationStatus))]
[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Buffers.Text.Base64))]
3 changes: 0 additions & 3 deletions src/libraries/System.Memory/src/System.Memory.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@
<Compile Include="System\Buffers\SequenceReader.cs" />
<Compile Include="System\Buffers\SequenceReader.Search.cs" />
<Compile Include="System\Buffers\SequenceReaderExtensions.Binary.cs" />
<Compile Include="System\Buffers\Text\Base64.cs" />
<Compile Include="System\Buffers\Text\Base64Decoder.cs" />
<Compile Include="System\Buffers\Text\Base64Encoder.cs" />
<Compile Include="System\Runtime\InteropServices\SequenceMarshal.cs" />
<Compile Include="System\Text\EncodingExtensions.cs" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\MemoryManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\OperationStatus.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\StandardFormat.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Base64.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Base64Encoder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Base64Decoder.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\FormattingHelpers.CountDigits.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Constants.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Buffers\Text\Utf8Formatter\FormattingHelpers.cs" />
Expand Down
28 changes: 27 additions & 1 deletion src/libraries/System.Private.CoreLib/src/System/Convert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Buffers;
using System.Buffers.Text;
using System.Text;

namespace System
{
Expand Down Expand Up @@ -2336,7 +2339,30 @@ public static string ToBase64String(ReadOnlySpan<byte> bytes, Base64FormattingOp
}

bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks);
string result = string.FastAllocateString(ToBase64_CalculateAndValidateOutputLength(bytes.Length, insertLineBreaks));
int outputLength = ToBase64_CalculateAndValidateOutputLength(bytes.Length, insertLineBreaks);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EgorBo, do we need something similar to this change for Convert.ToBase64CharArray?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephentoub makes sense!


if (!insertLineBreaks && bytes.Length >= 64)
{
// For large inputs it's faster to allocate a temp buffer and call UTF8 version
// which is then extended to UTF8 via Latin1.GetString (base64 is always ASCI)
[MethodImpl(MethodImplOptions.NoInlining)]
static string ToBase64StringLargeInputs(ReadOnlySpan<byte> data, int outputLen)
{
byte[]? rentedBytes = null;
Span<byte> utf8buffer = outputLen <= 256 ? stackalloc byte[256] : (rentedBytes = ArrayPool<byte>.Shared.Rent(outputLen));
OperationStatus status = Base64.EncodeToUtf8(data, utf8buffer, out int _, out int _);
Debug.Assert(status == OperationStatus.Done);
string result = Encoding.Latin1.GetString(utf8buffer);
if (rentedBytes != null)
{
ArrayPool<byte>.Shared.Return(rentedBytes);
}
return result;
}
return ToBase64StringLargeInputs(bytes, outputLength);
}

string result = string.FastAllocateString(outputLength);

unsafe
{
Expand Down