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

Add Base64Url support for netstandard 2.0 #103617

Merged
merged 12 commits into from
Jun 24, 2024
Prev Previous commit
Apply feedbacks
buyaa-n committed Jun 21, 2024
commit 6aaa2359d898ea40226e24e725b0864826c58b58
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using static System.Buffers.Text.Base64Helper;

namespace System.Buffers.Text
{
@@ -29,7 +30,7 @@ public static partial class Base64
/// or if the input is incomplete (i.e. not a multiple of 4) and <paramref name="isFinalBlock"/> is <see langword="true"/>.
/// </returns>
public static OperationStatus DecodeFromUtf8(ReadOnlySpan<byte> utf8, Span<byte> bytes, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true) =>
Base64Helper.DecodeFrom(Base64Helper.s_base64ByteDecoder, utf8, bytes, out bytesConsumed, out bytesWritten, isFinalBlock, ignoreWhiteSpace: true);
DecodeFrom(default(Base64DecoderByte), utf8, bytes, out bytesConsumed, out bytesWritten, isFinalBlock, ignoreWhiteSpace: true);

/// <summary>
/// Returns the maximum length (in bytes) of the result if you were to decode base 64 encoded text within a byte span of size "length".
@@ -61,6 +62,6 @@ public static int GetMaxDecodedFromUtf8Length(int length)
/// hence can only be called once with all the data in the buffer.
/// </returns>
public static OperationStatus DecodeFromUtf8InPlace(Span<byte> buffer, out int bytesWritten) =>
Base64Helper.DecodeFromUtf8InPlace(Base64Helper.s_base64ByteDecoder, buffer, out bytesWritten, ignoreWhiteSpace: true);
Base64Helper.DecodeFromUtf8InPlace(default(Base64DecoderByte), buffer, out bytesWritten, ignoreWhiteSpace: true);
}
}
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using static System.Buffers.Text.Base64Helper;

namespace System.Buffers.Text
{
@@ -13,7 +14,6 @@ namespace System.Buffers.Text
/// </summary>
public static partial class Base64
{

/// <summary>
/// Encode the span of binary data into UTF-8 encoded text represented as base64.
/// </summary>
@@ -32,7 +32,7 @@ public static partial class Base64
/// It does not return InvalidData since that is not possible for base64 encoding.
/// </returns>
public static OperationStatus EncodeToUtf8(ReadOnlySpan<byte> bytes, Span<byte> utf8, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true) =>
Base64Helper.EncodeTo(Base64Helper.s_base64ByteEncoder, bytes, utf8, out bytesConsumed, out bytesWritten, isFinalBlock);
EncodeTo(default(Base64EncoderByte), bytes, utf8, out bytesConsumed, out bytesWritten, isFinalBlock);

/// <summary>
/// Returns the maximum length (in bytes) of the result if you were to encode binary data within a byte span of size "length".
@@ -43,7 +43,7 @@ public static OperationStatus EncodeToUtf8(ReadOnlySpan<byte> bytes, Span<byte>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetMaxEncodedToUtf8Length(int length)
{
ArgumentOutOfRangeException.ThrowIfGreaterThan<uint>((uint)length, Base64Helper.MaximumEncodeLength);
ArgumentOutOfRangeException.ThrowIfGreaterThan<uint>((uint)length, MaximumEncodeLength);

return ((length + 2) / 3) * 4;
}
@@ -64,6 +64,6 @@ public static int GetMaxEncodedToUtf8Length(int length)
/// It does not return InvalidData since that is not possible for base 64 encoding.
/// </returns>
public static OperationStatus EncodeToUtf8InPlace(Span<byte> buffer, int dataLength, out int bytesWritten) =>
Base64Helper.EncodeToUtf8InPlace(Base64Helper.s_base64ByteEncoder, buffer, dataLength, out bytesWritten);
Base64Helper.EncodeToUtf8InPlace(default(Base64EncoderByte), buffer, dataLength, out bytesWritten);
}
}
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if NETCOREAPP
#if NET
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
using System.Runtime.Intrinsics;
@@ -14,10 +14,6 @@ namespace System.Buffers.Text
{
internal static partial class Base64Helper
{
#pragma warning disable CA1805 // Member 's_base64ByteDecoder' is explicitly initialized to its default value
internal static Base64DecoderByte s_base64ByteDecoder = default;
#pragma warning restore CA1805

internal static unsafe OperationStatus DecodeFrom<TBase64Decoder, T>(TBase64Decoder decoder, ReadOnlySpan<T> source, Span<byte> bytes,
out int bytesConsumed, out int bytesWritten, bool isFinalBlock, bool ignoreWhiteSpace)
where TBase64Decoder : IBase64Decoder<T>
@@ -50,7 +46,7 @@ internal static unsafe OperationStatus DecodeFrom<TBase64Decoder, T>(TBase64Deco
T* srcEnd = srcBytes + (uint)srcLength;
T* srcMax = srcBytes + (uint)maxSrcLength;

#if NETCOREAPP
#if NET
if (maxSrcLength >= 24)
{
T* end = srcMax - 88;
@@ -112,7 +108,7 @@ internal static unsafe OperationStatus DecodeFrom<TBase64Decoder, T>(TBase64Deco
// This should never overflow since destLength here is less than int.MaxValue / 4 * 3 (i.e. 1610612733)
// Therefore, (destLength / 3) * 4 will always be less than 2147483641
Debug.Assert(destLength < (int.MaxValue / 4 * 3));
#if NETCOREAPP
#if NET
(maxSrcLength, int remainder) = int.DivRem(destLength, 3);
maxSrcLength *= 4;
#else
@@ -643,13 +639,13 @@ private static OperationStatus DecodeWithWhiteSpaceFromUtf8InPlace<TBase64Decode
return status;
}

#if NETCOREAPP
#if NET
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[CompExactlyDependsOn(typeof(Avx512BW))]
[CompExactlyDependsOn(typeof(Avx512Vbmi))]
private static unsafe void Avx512Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* destBytes, T* srcEnd, int sourceLength, int destLength, T* srcStart, byte* destStart)
where TBase64Decoder : IBase64Decoder<T>
where T : unmanaged
where TBase64Decoder : IBase64Decoder<T>
where T : unmanaged
{
// Reference for VBMI implementation : https://github.com/WojciechMula/base64simd/tree/master/decode
// If we have AVX512 support, pick off 64 bytes at a time for as long as we can,
@@ -1243,20 +1239,15 @@ internal static bool IsWhiteSpace(int value)

public uint AdvSimdLutTwo3Uint1 => 0x1B1AFFFF;

public int GetMaxDecodedLength(int utf8Length) =>
#if NETCOREAPP
Base64.GetMaxDecodedFromUtf8Length(utf8Length);
#else
0;
#endif
public int GetMaxDecodedLength(int utf8Length) => Base64.GetMaxDecodedFromUtf8Length(utf8Length);

public bool IsInvalidLength(int bufferLength) => bufferLength % 4 != 0; // only decode input if it is a multiple of 4

public bool IsValidPadding(uint padChar) => padChar == EncodingPad;

public int SrcLength(bool _, int utf8Length) => utf8Length & ~0x3; // only decode input up to the closest multiple of 4.

#if NETCOREAPP
#if NET
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
[CompExactlyDependsOn(typeof(Ssse3))]
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#if NETCOREAPP
#if NET
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Intrinsics.X86;
@@ -13,10 +13,6 @@ namespace System.Buffers.Text
{
internal static partial class Base64Helper
{
#pragma warning disable CA1805 // Member 's_base64ByteEncoder' is explicitly initialized to its default value
internal static Base64EncoderByte s_base64ByteEncoder = default;
#pragma warning restore CA1805

internal static unsafe OperationStatus EncodeTo<TBase64Encoder, T>(TBase64Encoder encoder, ReadOnlySpan<byte> source,
Span<T> destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true)
where TBase64Encoder : IBase64Encoder<T>
@@ -41,7 +37,7 @@ internal static unsafe OperationStatus EncodeTo<TBase64Encoder, T>(TBase64Encode
byte* srcEnd = srcBytes + (uint)srcLength;
byte* srcMax = srcBytes + (uint)maxSrcLength;

#if NETCOREAPP
#if NET
if (maxSrcLength >= 16)
{
byte* end = srcMax - 64;
@@ -132,13 +128,13 @@ internal static unsafe OperationStatus EncodeTo<TBase64Encoder, T>(TBase64Encode
}
}

#if NETCOREAPP
#if NET
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[CompExactlyDependsOn(typeof(Avx512BW))]
[CompExactlyDependsOn(typeof(Avx512Vbmi))]
private static unsafe void Avx512Encode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, T* destStart)
where TBase64Encoder : IBase64Encoder<T>
where T : unmanaged
where TBase64Encoder : IBase64Encoder<T>
where T : unmanaged
{
// Reference for VBMI implementation : https://github.com/WojciechMula/base64simd/tree/master/encode
// If we have AVX512 support, pick off 48 bytes at a time for as long as we can.
@@ -664,21 +660,12 @@ private static unsafe uint Encode(byte* threeBytes, ref byte encodingMap)

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetMaxSrcLength(int srcLength, int destLength) =>
#if NETCOREAPP
srcLength <= MaximumEncodeLength && destLength >= Base64.GetMaxEncodedToUtf8Length(srcLength) ?
srcLength : (destLength >> 2) * 3;
#else
0;
#endif

public uint GetInPlaceDestinationLength(int encodedLength, int _) => (uint)(encodedLength - 4);

public int GetMaxEncodedLength(int srcLength) =>
#if NETCOREAPP
Base64.GetMaxEncodedToUtf8Length(srcLength);
#else
0;
#endif
public int GetMaxEncodedLength(int srcLength) => Base64.GetMaxEncodedToUtf8Length(srcLength);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, byte* dest, ref byte encodingMap)
@@ -734,7 +721,7 @@ public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, byte* dest, ref byt
}
}

#if NETCOREAPP
#if NET
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void StoreVector512ToDestination(byte* dest, byte* destStart, int destLength, Vector512<byte> str)
{
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@

using System.Diagnostics;
using System.Runtime.CompilerServices;
#if NETCOREAPP
#if NET
using System.Runtime.Intrinsics;
#endif

@@ -82,7 +82,7 @@ internal interface IBase64Encoder<T> where T : unmanaged
unsafe void EncodeThreeAndWrite(byte* threeBytes, T* destination, ref byte encodingMap);
int IncrementPadTwo { get; }
int IncrementPadOne { get; }
#if NETCOREAPP
#if NET
unsafe void StoreVector512ToDestination(T* dest, T* destStart, int destLength, Vector512<byte> str);
unsafe void StoreVector256ToDestination(T* dest, T* destStart, int destLength, Vector256<byte> str);
unsafe void StoreVector128ToDestination(T* dest, T* destStart, int destLength, Vector128<byte> str);
@@ -109,7 +109,7 @@ internal interface IBase64Decoder<T> where T : unmanaged
int GetMaxDecodedLength(int sourceLength);
bool IsInvalidLength(int bufferLength);
bool IsValidPadding(uint padChar);
#if NETCOREAPP
#if NET
bool TryDecode128Core(
Vector128<byte> str,
Vector128<byte> hiNibbles,
Original file line number Diff line number Diff line change
@@ -7,19 +7,14 @@ namespace System.Buffers.Text
{
internal static partial class Base64Helper
{
#pragma warning disable CA1805 // Member 's_base64ByteEncoder' is explicitly initialized to its default value
internal static Base64ByteValidatable s_base64ByteValidatable = default;
internal static Base64CharValidatable s_base64CharValidatable = default;
#pragma warning restore CA1805

internal static bool IsValid<T, TBase64Validatable>(TBase64Validatable validatable, ReadOnlySpan<T> base64Text, out int decodedLength)
where TBase64Validatable : IBase64Validatable<T>
{
int length = 0, paddingCount = 0;

if (!base64Text.IsEmpty)
{
#if NETCOREAPP
#if NET
while (true)
{

@@ -128,7 +123,7 @@ internal static bool IsValid<T, TBase64Validatable>(TBase64Validatable validatab

internal interface IBase64Validatable<T>
{
#if NETCOREAPP
#if NET
int IndexOfAnyExcept(ReadOnlySpan<T> span);
#else
int DecodeValue(T value);
@@ -140,7 +135,7 @@ internal interface IBase64Validatable<T>

internal readonly struct Base64CharValidatable : IBase64Validatable<char>
{
#if NETCOREAPP
#if NET
private static readonly SearchValues<char> s_validBase64Chars = SearchValues.Create("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");

public int IndexOfAnyExcept(ReadOnlySpan<char> span) => span.IndexOfAnyExcept(s_validBase64Chars);
@@ -153,24 +148,23 @@ public int DecodeValue(char value)
return -2;
}

return s_base64ByteDecoder.DecodingMap[value];
return default(Base64DecoderByte).DecodingMap[value];
}
#endif
public bool IsWhiteSpace(char value) => Base64Helper.IsWhiteSpace(value);
public bool IsEncodingPad(char value) => value == EncodingPad;
public bool ValidateAndDecodeLength(int length, int paddingCount, out int decodedLength) =>
s_base64ByteValidatable.ValidateAndDecodeLength(length, paddingCount, out decodedLength);
default(Base64ByteValidatable).ValidateAndDecodeLength(length, paddingCount, out decodedLength);
}

internal readonly struct Base64ByteValidatable : IBase64Validatable<byte>
{
#if NETCOREAPP
private static readonly SearchValues<byte> s_validBase64Chars = SearchValues.Create(s_base64ByteEncoder.EncodingMap);
#if NET
private static readonly SearchValues<byte> s_validBase64Chars = SearchValues.Create(default(Base64EncoderByte).EncodingMap);

public int IndexOfAnyExcept(ReadOnlySpan<byte> span) => span.IndexOfAnyExcept(s_validBase64Chars);
#else
public int DecodeValue(byte value) =>
s_base64ByteDecoder.DecodingMap[value];
public int DecodeValue(byte value) => default(Base64DecoderByte).DecodingMap[value];
#endif
public bool IsWhiteSpace(byte value) => Base64Helper.IsWhiteSpace(value);
public bool IsEncodingPad(byte value) => value == EncodingPad;
Loading