< Summary

Line coverage
89%
Covered lines: 966
Uncovered lines: 114
Coverable lines: 1080
Total lines: 3172
Line coverage: 89.4%
Branch coverage
75%
Covered branches: 328
Total branches: 436
Branch coverage: 75.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: DecodeFrom(...)71.25%808085.71%
File 1: InvalidDataFallback(TBase64Decoder,System.ReadOnlySpan`1<T>,System.Span`1<System.Byte>,System.Int32&,System.Int32&,System.Boolean)90%1010100%
File 1: DecodeFromUtf8InPlace(...)79.16%484884.61%
File 1: DecodeWithWhiteSpaceBlockwise(...)90%303095.65%
File 1: DecodeWithWhiteSpaceBlockwise(...)90%303095.65%
File 1: GetPaddingCount(...)83.33%66100%
File 1: GetPaddingCount(...)83.33%66100%
File 1: DecodeWithWhiteSpaceFromUtf8InPlace(...)87.5%242490.9%
File 1: Avx512Decode(...)100%66100%
File 1: Avx2Decode(...)100%66100%
File 1: SimdShuffle(...)25%8866.66%
File 1: Vector128Decode(...)40.9%222293.33%
File 1: WriteThreeLowOrderBytes(...)100%11100%
File 1: IsWhiteSpace(...)50%22100%
File 1: GetMaxDecodedLength(...)100%11100%
File 1: IsInvalidLength(...)100%11100%
File 1: IsValidPadding(...)100%11100%
File 1: SrcLength(...)100%11100%
File 1: TryDecode128Core(...)100%22100%
File 1: TryDecode256Core(...)100%22100%
File 1: TryLoadVector512(...)100%11100%
File 1: TryLoadAvxVector256(...)100%11100%
File 1: TryLoadVector128(...)100%11100%
File 1: TryLoadArmVector128x4(...)100%110%
File 1: DecodeFourElements(...)100%11100%
File 1: DecodeRemaining(...)33.33%6661.9%
File 1: IndexOfAnyExceptWhiteSpace(...)100%44100%
File 1: DecodeWithWhiteSpaceBlockwiseWrapper(...)100%11100%
File 1: GetMaxDecodedLength(...)100%11100%
File 1: IsInvalidLength(...)100%110%
File 1: IsValidPadding(...)100%11100%
File 1: SrcLength(...)100%11100%
File 1: TryDecode128Core(...)100%11100%
File 1: TryDecode256Core(...)100%11100%
File 1: TryLoadVector512(...)100%22100%
File 1: TryLoadAvxVector256(...)100%22100%
File 1: TryLoadVector128(...)100%22100%
File 1: TryLoadArmVector128x4(...)0%220%
File 1: DecodeFourElements(...)100%22100%
File 1: DecodeRemaining(...)50%8865.21%
File 1: IndexOfAnyExceptWhiteSpace(...)100%44100%
File 1: DecodeWithWhiteSpaceBlockwiseWrapper(...)100%11100%
File 2: EncodeTo(...)72.5%404088.67%
File 2: Avx512Encode(...)75%44100%
File 2: Avx2Encode(...)75%44100%
File 2: Vector128Encode(...)28.57%141493.93%
File 2: EncodeToUtf8InPlace(...)80%101084%
File 2: Encode(...)100%11100%
File 2: ConstructResult(...)100%11100%
File 2: EncodeOneOptionallyPadTwo(...)100%11100%
File 2: EncodeTwoOptionallyPadOne(...)100%11100%
File 2: GetMaxSrcLength(...)50%44100%
File 2: GetInPlaceDestinationLength(...)100%11100%
File 2: GetMaxEncodedLength(...)100%11100%
File 2: EncodeOneOptionallyPadTwo(...)100%11100%
File 2: EncodeTwoOptionallyPadOne(...)100%11100%
File 2: StoreVector512ToDestination(...)100%11100%
File 2: StoreVector256ToDestination(...)100%11100%
File 2: StoreVector128ToDestination(...)100%11100%
File 2: StoreArmVector128x4ToDestination(...)100%110%
File 2: EncodeThreeAndWrite(...)100%11100%
File 2: GetMaxSrcLength(...)100%11100%
File 2: GetInPlaceDestinationLength(...)100%110%
File 2: GetMaxEncodedLength(...)100%110%
File 2: EncodeOneOptionallyPadTwo(...)100%11100%
File 2: EncodeTwoOptionallyPadOne(...)100%11100%
File 2: StoreVector512ToDestination(...)100%11100%
File 2: StoreVector256ToDestination(...)100%11100%
File 2: StoreVector128ToDestination(...)100%11100%
File 2: StoreArmVector128x4ToDestination(...)100%110%
File 2: EncodeThreeAndWrite(...)100%11100%
File 3: AssertRead(...)50%2271.42%
File 3: AssertWrite(...)50%2271.42%
File 3: AssertRead(...)50%2271.42%
File 3: AssertWrite(...)50%2271.42%
File 3: ThrowUnreachableException()100%110%
File 4: IsValid(...)92.85%282893.93%
File 4: .cctor()100%110%
File 4: IndexOfAnyExcept(...)100%110%
File 4: IsWhiteSpace(...)100%110%
File 4: IsEncodingPad(...)100%110%
File 4: ValidateAndDecodeLength(...)100%110%
File 4: .cctor()100%11100%
File 4: IndexOfAnyExcept(...)100%11100%
File 4: IsWhiteSpace(...)100%11100%
File 4: IsEncodingPad(...)100%11100%
File 4: ValidateAndDecodeLength(...)100%1010100%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64DecoderHelper.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Runtime.CompilerServices;
 6using System.Runtime.InteropServices;
 7using System.Text;
 8using System.Diagnostics.CodeAnalysis;
 9
 10#if NET
 11using System.Runtime.Intrinsics.Arm;
 12using System.Runtime.Intrinsics.Wasm;
 13using System.Runtime.Intrinsics.X86;
 14using System.Runtime.Intrinsics;
 15#endif
 16
 17namespace System.Buffers.Text
 18{
 19    // AVX2 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arch/avx
 20    // Vector128 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arc
 21    internal static partial class Base64Helper
 22    {
 23        internal static unsafe OperationStatus DecodeFrom<TBase64Decoder, T>(TBase64Decoder decoder, ReadOnlySpan<T> sou
 24            out int bytesConsumed, out int bytesWritten, bool isFinalBlock, bool ignoreWhiteSpace)
 25            where TBase64Decoder : IBase64Decoder<T>
 26            where T : unmanaged
 27        {
 28            if (source.IsEmpty)
 29            {
 12030                bytesConsumed = 0;
 12031                bytesWritten = 0;
 12032                return OperationStatus.Done;
 33            }
 34
 20013235            fixed (T* srcBytes = &MemoryMarshal.GetReference(source))
 20013236            fixed (byte* destBytes = &MemoryMarshal.GetReference(bytes))
 37            {
 20013238                int srcLength = decoder.SrcLength(isFinalBlock, source.Length);
 20013239                int destLength = bytes.Length;
 20013240                int maxSrcLength = srcLength;
 20013241                int decodedLength = decoder.GetMaxDecodedLength(srcLength);
 42
 43                // max. 2 padding chars
 20013244                if (destLength < decodedLength - 2)
 45                {
 46                    // For overflow see comment below
 047                    maxSrcLength = destLength / 3 * 4;
 48                }
 49
 20013250                T* src = srcBytes;
 20013251                byte* dest = destBytes;
 20013252                T* srcEnd = srcBytes + (uint)srcLength;
 20013253                T* srcMax = srcBytes + (uint)maxSrcLength;
 54
 55#if NET
 20013256                if (maxSrcLength >= 24)
 57                {
 13534058                    T* end = srcMax - 88;
 13534059                    if (Vector512.IsHardwareAccelerated && Avx512Vbmi.IsSupported && (end >= src))
 60                    {
 11801461                        Avx512Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 62
 11801463                        if (src == srcEnd)
 64                        {
 65                            goto DoneExit;
 66                        }
 67                    }
 68
 13534069                    end = srcMax - 45;
 13534070                    if (Avx2.IsSupported && (end >= src))
 71                    {
 11232672                        Avx2Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 73
 11232674                        if (src == srcEnd)
 75                        {
 76                            goto DoneExit;
 77                        }
 78                    }
 79
 13534080                    end = srcMax - 66;
 81                    if (AdvSimd.Arm64.IsSupported && (end >= src))
 82                    {
 83                        AdvSimdDecode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 84
 85                        if (src == srcEnd)
 86                        {
 87                            goto DoneExit;
 88                        }
 89                    }
 90
 13534091                    end = srcMax - 24;
 13534092                    if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && BitConverter.IsLit
 93                    {
 12608694                        Vector128Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 95
 12608696                        if (src == srcEnd)
 97                        {
 98                            goto DoneExit;
 99                        }
 100                    }
 101                }
 102#endif
 103
 104                // Last bytes could have padding characters, so process them separately and treat them as valid only if 
 105                // if isFinalBlock is false, padding characters are considered invalid
 200132106                int skipLastChunk = isFinalBlock ? 4 : 0;
 107
 200132108                if (destLength >= decodedLength)
 109                {
 200132110                    maxSrcLength = srcLength - skipLastChunk;
 111                }
 112                else
 113                {
 114                    // This should never overflow since destLength here is less than int.MaxValue / 4 * 3 (i.e. 16106127
 115                    // Therefore, (destLength / 3) * 4 will always be less than 2147483641
 0116                    Debug.Assert(destLength < (int.MaxValue / 4 * 3));
 117#if NET
 0118                    (maxSrcLength, int remainder) = int.DivRem(destLength, 3);
 0119                    maxSrcLength *= 4;
 120#else
 121                    maxSrcLength = (destLength / 3) * 4;
 122                    int remainder = (int)((uint)destLength % 3);
 123#endif
 0124                    if (isFinalBlock && remainder > 0)
 125                    {
 0126                        srcLength &= ~0x3; // In case of Base64UrlDecoder source can be not a multiple of 4, round down 
 127                    }
 128                }
 129
 200132130                ref sbyte decodingMap = ref MemoryMarshal.GetReference(decoder.DecodingMap);
 200132131                srcMax = srcBytes + maxSrcLength;
 132
 584834133                while (src < srcMax)
 134                {
 485494135                    int result = decoder.DecodeFourElements(src, ref decodingMap);
 136
 485494137                    if (result < 0)
 138                    {
 139                        goto InvalidDataExit;
 140                    }
 141
 384702142                    WriteThreeLowOrderBytes(dest, result);
 384702143                    src += 4;
 384702144                    dest += 3;
 145                }
 146
 99340147                if (maxSrcLength != srcLength - skipLastChunk)
 148                {
 149                    goto DestinationTooSmallExit;
 150                }
 151
 99340152                if (src == srcEnd)
 153                {
 51630154                    if (isFinalBlock)
 155                    {
 156                        goto InvalidDataExit;
 157                    }
 158
 50074159                    if (src == srcBytes + source.Length)
 160                    {
 50074161                        goto DoneExit;
 162                    }
 163
 164                    goto NeedMoreDataExit;
 165                }
 166
 167                // if isFinalBlock is false, we will never reach this point
 168                // Handle remaining bytes, for Base64 its always 4 bytes, for Base64Url up to 8 bytes left.
 169                // If more than 4 bytes remained it will end up in DestinationTooSmallExit or InvalidDataExit (might suc
 47710170                long remaining = srcEnd - src;
 47710171                Debug.Assert(typeof(TBase64Decoder) == typeof(Base64DecoderByte) ? remaining == 4 : remaining < 8);
 47710172                int i0 = decoder.DecodeRemaining(srcEnd, ref decodingMap, remaining, out uint t2, out uint t3);
 173
 47710174                if (i0 < 0)
 175                {
 176                    goto InvalidDataExit;
 177                }
 178
 46004179                byte* destMax = destBytes + (uint)destLength;
 180
 46004181                if (!decoder.IsValidPadding(t3))
 182                {
 13702183                    int i2 = Unsafe.Add(ref decodingMap, (IntPtr)t2);
 13702184                    int i3 = Unsafe.Add(ref decodingMap, (IntPtr)t3);
 185
 13702186                    i2 <<= 6;
 187
 13702188                    i0 |= i3;
 13702189                    i0 |= i2;
 190
 13702191                    if (i0 < 0)
 192                    {
 193                        goto InvalidDataExit;
 194                    }
 13504195                    if (dest + 3 > destMax)
 196                    {
 197                        goto DestinationTooSmallExit;
 198                    }
 199
 13504200                    WriteThreeLowOrderBytes(dest, i0);
 13504201                    dest += 3;
 13504202                    src += 4;
 203                }
 32302204                else if (!decoder.IsValidPadding(t2))
 205                {
 13570206                    int i2 = Unsafe.Add(ref decodingMap, (IntPtr)t2);
 207
 13570208                    i2 <<= 6;
 209
 13570210                    i0 |= i2;
 211
 13570212                    if ((i0 & 0x800000c0) != 0) // if negative or 2 unused bits are not 0.
 213                    {
 214                        goto InvalidDataExit;
 215                    }
 13444216                    if (dest + 2 > destMax)
 217                    {
 218                        goto DestinationTooSmallExit;
 219                    }
 220
 13444221                    dest[0] = (byte)(i0 >> 16);
 13444222                    dest[1] = (byte)(i0 >> 8);
 13444223                    dest += 2;
 13444224                    src += remaining;
 225                }
 226                else
 227                {
 18732228                    if ((i0 & 0x8000F000) != 0) // if negative or 4 unused bits are not 0.
 229                    {
 230                        goto InvalidDataExit;
 231                    }
 18678232                    if (dest + 1 > destMax)
 233                    {
 234                        goto DestinationTooSmallExit;
 235                    }
 236
 18678237                    dest[0] = (byte)(i0 >> 16);
 18678238                    dest += 1;
 18678239                    src += remaining;
 240                }
 241
 45626242                if (srcLength != source.Length)
 243                {
 244                    goto InvalidDataExit;
 245                }
 246
 247            DoneExit:
 95626248                bytesConsumed = (int)(src - srcBytes);
 95626249                bytesWritten = (int)(dest - destBytes);
 95626250                return OperationStatus.Done;
 251
 252            DestinationTooSmallExit:
 0253                if (srcLength != source.Length && isFinalBlock)
 254                {
 255                    goto InvalidDataExit; // if input is not a multiple of 4, and there is no more data, return invalid 
 256                }
 257
 0258                if (ignoreWhiteSpace)
 259                {
 260                    // Fall through to InvalidDataFallback which strips whitespace and re-evaluates destination size req
 261                    goto InvalidDataExit;
 262                }
 263
 0264                bytesConsumed = (int)(src - srcBytes);
 0265                bytesWritten = (int)(dest - destBytes);
 0266                return OperationStatus.DestinationTooSmall;
 267
 268            NeedMoreDataExit:
 0269                bytesConsumed = (int)(src - srcBytes);
 0270                bytesWritten = (int)(dest - destBytes);
 0271                return OperationStatus.NeedMoreData;
 272
 273            InvalidDataExit:
 104506274                bytesConsumed = (int)(src - srcBytes);
 104506275                bytesWritten = (int)(dest - destBytes);
 104506276                return ignoreWhiteSpace ?
 104506277                    InvalidDataFallback(decoder, source, bytes, ref bytesConsumed, ref bytesWritten, isFinalBlock) :
 104506278                    OperationStatus.InvalidData;
 279            }
 280
 281            static OperationStatus InvalidDataFallback(TBase64Decoder decoder, ReadOnlySpan<T> source, Span<byte> bytes,
 282            {
 12174283                source = source.Slice(bytesConsumed);
 12174284                bytes = bytes.Slice(bytesWritten);
 285
 286                OperationStatus status;
 287                do
 288                {
 97448289                    int localConsumed = decoder.IndexOfAnyExceptWhiteSpace(source);
 97448290                    if (localConsumed < 0)
 291                    {
 292                        // The remainder of the input is all whitespace. Mark it all as having been consumed,
 293                        // and mark the operation as being done.
 64294                        bytesConsumed += source.Length;
 64295                        status = OperationStatus.Done;
 64296                        break;
 297                    }
 298
 97384299                    if (localConsumed == 0)
 300                    {
 301                        // Non-whitespace was found at the beginning of the input. Since it wasn't consumed
 302                        // by the previous call to DecodeFromUtf8, it must be part of a Base64 sequence
 303                        // that was interrupted by whitespace or something else considered invalid.
 304                        // Fall back to block-wise decoding. This is very slow, but it's also very non-standard
 305                        // formatting of the input; whitespace is typically only found between blocks, such as
 306                        // when Convert.ToBase64String inserts a line break every 76 output characters.
 7418307                        return decoder.DecodeWithWhiteSpaceBlockwiseWrapper(decoder, source, bytes, ref bytesConsumed, r
 308                    }
 309
 310                    // Skip over the starting whitespace and continue.
 89966311                    bytesConsumed += localConsumed;
 89966312                    source = source.Slice(localConsumed);
 313
 314                    // Try again after consumed whitespace
 89966315                    status = DecodeFrom(decoder, source, bytes, out localConsumed, out int localWritten, isFinalBlock, i
 89966316                    bytesConsumed += localConsumed;
 89966317                    bytesWritten += localWritten;
 318
 89966319                    if (status is OperationStatus.Done or OperationStatus.NeedMoreData)
 320                    {
 321                        break;
 322                    }
 323
 324                    // The DecodeFrom helper will return DestinationTooSmall if the destination is too small,
 325                    // regardless of whether it's actually too small once you skip whitespace characters.
 326                    // In that case we loop again and fall back to block-wise decoding if we can't make progress.
 327
 85274328                    source = source.Slice(localConsumed);
 85274329                    bytes = bytes.Slice(localWritten);
 330                }
 85274331                while (!source.IsEmpty);
 332
 4756333                return status;
 334            }
 335        }
 336
 337        internal static unsafe OperationStatus DecodeFromUtf8InPlace<TBase64Decoder>(TBase64Decoder decoder, Span<byte> 
 338            where TBase64Decoder : IBase64Decoder<byte>
 339        {
 340            if (buffer.IsEmpty)
 341            {
 0342                bytesWritten = 0;
 0343                return OperationStatus.Done;
 344            }
 345
 131164346            fixed (byte* bufferBytes = &MemoryMarshal.GetReference(buffer))
 347            {
 131164348                uint bufferLength = (uint)buffer.Length;
 131164349                uint sourceIndex = 0;
 131164350                uint destIndex = 0;
 351
 131164352                if (decoder.IsInvalidLength(buffer.Length))
 353                {
 354                    goto InvalidExit;
 355                }
 356
 128712357                ref sbyte decodingMap = ref MemoryMarshal.GetReference(decoder.DecodingMap);
 358
 359#if NET
 360                // Decode in place using the same vectorized helpers as DecodeFrom. This is safe because the
 361                // write cursor (dest) always trails the read cursor (src) by 25%, so each vector store -- including
 362                // its zero-padded overshoot -- ends at or before the next vector load and never clobbers source
 363                // that hasn't been read yet.
 128712364                if (bufferLength >= 24)
 365                {
 3992366                    byte* src = bufferBytes;
 3992367                    byte* dest = bufferBytes;
 3992368                    int length = (int)bufferLength;
 3992369                    byte* srcMax = bufferBytes + length;
 370
 3992371                    byte* end = srcMax - 88;
 3992372                    if (Vector512.IsHardwareAccelerated && Avx512Vbmi.IsSupported && (end >= src))
 373                    {
 2950374                        Avx512Decode(decoder, ref src, ref dest, end, length, length, bufferBytes, bufferBytes);
 375                    }
 376
 3992377                    end = srcMax - 45;
 3992378                    if (Avx2.IsSupported && (end >= src))
 379                    {
 2412380                        Avx2Decode(decoder, ref src, ref dest, end, length, length, bufferBytes, bufferBytes);
 381                    }
 382
 3992383                    end = srcMax - 66;
 384                    if (AdvSimd.Arm64.IsSupported && (end >= src))
 385                    {
 386                        AdvSimdDecode(decoder, ref src, ref dest, end, length, length, bufferBytes, bufferBytes);
 387                    }
 388
 3992389                    end = srcMax - 24;
 3992390                    if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && BitConverter.IsLit
 391                    {
 3342392                        Vector128Decode(decoder, ref src, ref dest, end, length, length, bufferBytes, bufferBytes);
 393                    }
 394
 3992395                    sourceIndex = (uint)(src - bufferBytes);
 3992396                    destIndex = (uint)(dest - bufferBytes);
 397                }
 398#endif
 399
 128712400                if (bufferLength > 4)
 401                {
 13460402                    while (sourceIndex < bufferLength - 4)
 403                    {
 9766404                        int result = decoder.DecodeFourElements(bufferBytes + sourceIndex, ref decodingMap);
 9766405                        if (result < 0)
 406                        {
 407                            goto InvalidExit;
 408                        }
 409
 8602410                        WriteThreeLowOrderBytes(bufferBytes + destIndex, result);
 8602411                        destIndex += 3;
 8602412                        sourceIndex += 4;
 413                    }
 414                }
 415
 416                uint t0;
 417                uint t1;
 418                uint t2;
 419                uint t3;
 420
 127548421                switch (bufferLength - sourceIndex)
 422                {
 423                    case 2:
 0424                        t0 = bufferBytes[bufferLength - 2];
 0425                        t1 = bufferBytes[bufferLength - 1];
 0426                        t2 = EncodingPad;
 0427                        t3 = EncodingPad;
 0428                        break;
 429                    case 3:
 0430                        t0 = bufferBytes[bufferLength - 3];
 0431                        t1 = bufferBytes[bufferLength - 2];
 0432                        t2 = bufferBytes[bufferLength - 1];
 0433                        t3 = EncodingPad;
 0434                        break;
 435                    case 4:
 127548436                        t0 = bufferBytes[bufferLength - 4];
 127548437                        t1 = bufferBytes[bufferLength - 3];
 127548438                        t2 = bufferBytes[bufferLength - 2];
 127548439                        t3 = bufferBytes[bufferLength - 1];
 440                        break;
 441                    default:
 442                        goto InvalidExit;
 443                }
 444
 127548445                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 127548446                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 447
 127548448                i0 <<= 18;
 127548449                i1 <<= 12;
 450
 127548451                i0 |= i1;
 452
 127548453                if (!decoder.IsValidPadding(t3))
 454                {
 124586455                    int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 124586456                    int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 457
 124586458                    i2 <<= 6;
 459
 124586460                    i0 |= i3;
 124586461                    i0 |= i2;
 462
 124586463                    if (i0 < 0)
 464                    {
 465                        goto InvalidExit;
 466                    }
 467
 121758468                    WriteThreeLowOrderBytes(bufferBytes + destIndex, i0);
 121758469                    destIndex += 3;
 470                }
 2962471                else if (!decoder.IsValidPadding(t2))
 472                {
 1346473                    int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 474
 1346475                    i2 <<= 6;
 476
 1346477                    i0 |= i2;
 478
 1346479                    if ((i0 & 0x800000c0) != 0) // if negative or 2 unused bits are not 0.
 480                    {
 481                        goto InvalidExit;
 482                    }
 483
 1256484                    bufferBytes[destIndex] = (byte)(i0 >> 16);
 1256485                    bufferBytes[destIndex + 1] = (byte)(i0 >> 8);
 1256486                    destIndex += 2;
 487                }
 488                else
 489                {
 1616490                    if ((i0 & 0x8000F000) != 0) // if negative or 4 unused bits are not 0.
 491                    {
 492                        goto InvalidExit;
 493                    }
 494
 1562495                    bufferBytes[destIndex] = (byte)(i0 >> 16);
 1562496                    destIndex += 1;
 497                }
 498
 124576499                bytesWritten = (int)destIndex;
 124576500                return OperationStatus.Done;
 501
 502            InvalidExit:
 6588503                bytesWritten = (int)destIndex;
 6588504                return ignoreWhiteSpace ?
 6588505                    DecodeWithWhiteSpaceFromUtf8InPlace<TBase64Decoder>(decoder, buffer, ref bytesWritten, sourceIndex) 
 6588506                    OperationStatus.InvalidData;
 507            }
 508        }
 509
 510        internal static unsafe OperationStatus DecodeWithWhiteSpaceBlockwise<TBase64Decoder>(TBase64Decoder decoder, Rea
 511            where TBase64Decoder : IBase64Decoder<byte>
 512        {
 513            const int BlockSize = 4;
 3688514            Span<byte> buffer = stackalloc byte[BlockSize];
 3688515            OperationStatus status = OperationStatus.Done;
 516
 39212517            while (!source.IsEmpty)
 518            {
 519                // Skip over any leading whitespace
 39188520                if (IsWhiteSpace(source[0]))
 521                {
 4774522                    source = source.Slice(1);
 4774523                    bytesConsumed++;
 4774524                    continue;
 525                }
 526
 34414527                int encodedIdx = 0;
 34414528                int bufferIdx = 0;
 34414529                int skipped = 0;
 530
 325430531                for (; encodedIdx < source.Length && (uint)bufferIdx < (uint)buffer.Length; ++encodedIdx)
 532                {
 145508533                    if (IsWhiteSpace(source[encodedIdx]))
 534                    {
 8860535                        skipped++;
 536                    }
 537                    else
 538                    {
 136648539                        buffer[bufferIdx] = source[encodedIdx];
 136648540                        bufferIdx++;
 541                    }
 542                }
 543
 34414544                source = source.Slice(encodedIdx);
 34414545                Debug.Assert(bufferIdx > 0);
 546
 547                bool hasAnotherBlock;
 548
 34414549                if (typeof(TBase64Decoder) == typeof(Base64DecoderByte))
 550                {
 34414551                    hasAnotherBlock = source.Length >= BlockSize;
 552                }
 553                else
 554                {
 0555                    hasAnotherBlock = source.Length > 1;
 556                }
 557
 34414558                bool localIsFinalBlock = !hasAnotherBlock;
 559
 560                // If this block contains padding and there's another block, then only whitespace may follow for being v
 34414561                if (hasAnotherBlock)
 562                {
 33454563                    int paddingCount = GetPaddingCount(decoder, ref buffer[BlockSize - 1]);
 33454564                    if (paddingCount > 0)
 565                    {
 196566                        hasAnotherBlock = false;
 196567                        localIsFinalBlock = true;
 568                    }
 569                }
 570
 34414571                if (localIsFinalBlock && !isFinalBlock)
 572                {
 0573                    localIsFinalBlock = false;
 574                }
 575
 34414576                status = DecodeFrom<TBase64Decoder, byte>(decoder, buffer.Slice(0, bufferIdx), bytes, out int localConsu
 577
 34414578                if (status != OperationStatus.Done)
 579                {
 3368580                    Debug.Assert(localConsumed == 0 && localWritten == 0, "On failure, should not have consumed or writt
 3368581                    return status;
 582                }
 583
 31046584                bytesConsumed += skipped;
 31046585                bytesConsumed += localConsumed;
 31046586                bytesWritten += localWritten;
 587
 588                // The remaining data must all be whitespace in order to be valid.
 31046589                if (!hasAnotherBlock)
 590                {
 5616591                    for (int i = 0; i < source.Length; ++i)
 592                    {
 2730593                        if (!IsWhiteSpace(source[i]))
 594                        {
 595                            // Revert previous dest increment, since an invalid state followed.
 218596                            bytesConsumed -= localConsumed;
 218597                            bytesWritten -= localWritten;
 598
 218599                            return OperationStatus.InvalidData;
 600                        }
 601
 2512602                        bytesConsumed++;
 603                    }
 604
 78605                    break;
 606                }
 607
 30750608                bytes = bytes.Slice(localWritten);
 30750609                Debug.Assert(!source.IsEmpty);
 610            }
 611
 102612            return status;
 613        }
 614
 615        internal static unsafe OperationStatus DecodeWithWhiteSpaceBlockwise<TBase64Decoder>(TBase64Decoder decoder, Rea
 616            where TBase64Decoder : IBase64Decoder<ushort>
 617        {
 618            const int BlockSize = 4;
 3730619            Span<ushort> buffer = stackalloc ushort[BlockSize];
 3730620            OperationStatus status = OperationStatus.Done;
 621
 21670622            while (!source.IsEmpty)
 623            {
 624                // Skip over any leading whitespace
 21662625                if (IsWhiteSpace(source[0]))
 626                {
 6028627                    source = source.Slice(1);
 6028628                    bytesConsumed++;
 6028629                    continue;
 630                }
 631
 15634632                int encodedIdx = 0;
 15634633                int bufferIdx = 0;
 15634634                int skipped = 0;
 635
 178162636                for (; encodedIdx < source.Length && (uint)bufferIdx < (uint)buffer.Length; ++encodedIdx)
 637                {
 81264638                    if (IsWhiteSpace(source[encodedIdx]))
 639                    {
 19668640                        skipped++;
 641                    }
 642                    else
 643                    {
 61596644                        buffer[bufferIdx] = source[encodedIdx];
 61596645                        bufferIdx++;
 646                    }
 647                }
 648
 15634649                source = source.Slice(encodedIdx);
 15634650                Debug.Assert(bufferIdx > 0);
 651
 652                bool hasAnotherBlock;
 653
 15634654                if (decoder is Base64DecoderChar)
 655                {
 15634656                    hasAnotherBlock = source.Length >= BlockSize;
 657                }
 658                else
 659                {
 0660                    hasAnotherBlock = source.Length > 1;
 661                }
 662
 15634663                bool localIsFinalBlock = !hasAnotherBlock;
 664
 665                // If this block contains padding and there's another block, then only whitespace may follow for being v
 15634666                if (hasAnotherBlock)
 667                {
 14682668                    int paddingCount = GetPaddingCount(decoder, ref buffer[BlockSize - 1]);
 14682669                    if (paddingCount > 0)
 670                    {
 36671                        hasAnotherBlock = false;
 36672                        localIsFinalBlock = true;
 673                    }
 674                }
 675
 15634676                if (localIsFinalBlock && !isFinalBlock)
 677                {
 0678                    localIsFinalBlock = false;
 679                }
 680
 15634681                status = DecodeFrom(decoder, buffer.Slice(0, bufferIdx), bytes, out int localConsumed, out int localWrit
 682
 15634683                if (status != OperationStatus.Done)
 684                {
 3690685                    Debug.Assert(localConsumed == 0 && localWritten == 0, "On failure, should not have consumed or writt
 3690686                    return status;
 687                }
 688
 11944689                bytesConsumed += skipped;
 11944690                bytesConsumed += localConsumed;
 11944691                bytesWritten += localWritten;
 692
 693                // The remaining data must all be whitespace in order to be valid.
 11944694                if (!hasAnotherBlock)
 695                {
 76696                    for (int i = 0; i < source.Length; ++i)
 697                    {
 18698                        if (!IsWhiteSpace(source[i]))
 699                        {
 700                            // Revert previous dest increment, since an invalid state followed.
 12701                            bytesConsumed -= localConsumed;
 12702                            bytesWritten -= localWritten;
 703
 12704                            return OperationStatus.InvalidData;
 705                        }
 706                    }
 707
 20708                    bytesConsumed += source.Length;
 20709                    break;
 710                }
 711
 11912712                bytes = bytes.Slice(localWritten);
 11912713                Debug.Assert(!source.IsEmpty);
 714            }
 715
 28716            return status;
 717        }
 718
 719        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 720        private static int GetPaddingCount<TBase64Decoder>(TBase64Decoder decoder, ref byte ptrToLastElement)
 721            where TBase64Decoder : IBase64Decoder<byte>
 722        {
 33454723            int padding = 0;
 724
 33454725            if (decoder.IsValidPadding(ptrToLastElement))
 726            {
 160727                padding++;
 728            }
 729
 33454730            if (decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))
 731            {
 42732                padding++;
 733            }
 734
 33454735            return padding;
 736        }
 737
 738        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 739        private static int GetPaddingCount<TBase64Decoder>(TBase64Decoder decoder, ref ushort ptrToLastElement)
 740            where TBase64Decoder : IBase64Decoder<ushort>
 741        {
 14682742            int padding = 0;
 743
 14682744            if (decoder.IsValidPadding(ptrToLastElement))
 745            {
 32746                padding++;
 747            }
 748
 14682749            if (decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))
 750            {
 4751                padding++;
 752            }
 753
 14682754            return padding;
 755        }
 756
 757        private static unsafe OperationStatus DecodeWithWhiteSpaceFromUtf8InPlace<TBase64Decoder>(TBase64Decoder decoder
 758            where TBase64Decoder : IBase64Decoder<byte>
 759        {
 3746760            int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);
 3746761            Span<byte> buffer = stackalloc byte[BlockSize];
 762
 3746763            OperationStatus status = OperationStatus.Done;
 3746764            int localDestIndex = destIndex;
 3746765            bool hasPaddingBeenProcessed = false;
 3746766            int localBytesWritten = 0;
 767
 124650768            while (sourceIndex < (uint)source.Length)
 769            {
 124490770                int bufferIdx = 0;
 771
 668934772                while (bufferIdx < BlockSize && sourceIndex < (uint)source.Length)
 773                {
 544444774                    if (!IsWhiteSpace(source[(int)sourceIndex]))
 775                    {
 496146776                        buffer[bufferIdx] = source[(int)sourceIndex];
 496146777                        bufferIdx++;
 778                    }
 779
 544444780                    sourceIndex++;
 781                }
 782
 124490783                if (bufferIdx == 0)
 784                {
 785                    continue;
 786                }
 787
 124376788                if (bufferIdx != 4)
 789                {
 790                    // Base64 require 4 bytes, for Base64Url it can be less than 4 bytes but not 1 byte.
 676791                    if (decoder is Base64DecoderByte || bufferIdx == 1)
 792                    {
 676793                        status = OperationStatus.InvalidData;
 676794                        break;
 795                    }
 796                    else // For Base64Url fill empty slots in last block with padding
 797                    {
 0798                        while (bufferIdx < BlockSize)  // Can happen only for last block
 799                        {
 0800                            Debug.Assert(source.Length == sourceIndex);
 0801                            buffer[bufferIdx++] = (byte)EncodingPad;
 802                        }
 803                    }
 804                }
 805
 123700806                if (hasPaddingBeenProcessed)
 807                {
 808                    // Padding has already been processed, a new valid block cannot be processed.
 809                    // Revert previous dest increment, since an invalid state followed.
 68810                    localDestIndex -= localBytesWritten;
 68811                    status = OperationStatus.InvalidData;
 68812                    break;
 813                }
 814
 123632815                status = DecodeFromUtf8InPlace<TBase64Decoder>(decoder, buffer, out localBytesWritten, ignoreWhiteSpace:
 123632816                localDestIndex += localBytesWritten;
 123632817                hasPaddingBeenProcessed = localBytesWritten < 3;
 818
 123632819                if (status != OperationStatus.Done)
 820                {
 821                    break;
 822                }
 823
 824                // Write result to source span in place.
 966012825                for (int i = 0; i < localBytesWritten; i++)
 826                {
 362216827                    source[localDestIndex - localBytesWritten + i] = buffer[i];
 828                }
 829            }
 830
 3746831            destIndex = localDestIndex;
 3746832            return status;
 833        }
 834
 835#if NET
 836        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 837        [CompExactlyDependsOn(typeof(Avx512BW))]
 838        [CompExactlyDependsOn(typeof(Avx512Vbmi))]
 839        private static unsafe void Avx512Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* de
 840            where TBase64Decoder : IBase64Decoder<T>
 841            where T : unmanaged
 842        {
 843            // Reference for VBMI implementation : https://github.com/WojciechMula/base64simd/tree/master/decode
 844            // If we have AVX512 support, pick off 64 bytes at a time for as long as we can,
 845            // but make sure that we quit before seeing any == markers at the end of the
 846            // string. Also, because we write 16 zeroes at the end of the output, ensure
 847            // that there are at least 22 valid bytes of input data remaining to close the
 848            // gap. 64 + 2 + 22 = 88 bytes.
 120964849            T* src = srcBytes;
 120964850            byte* dest = destBytes;
 851
 852            // The JIT won't hoist these "constants", so help it
 120964853            Vector512<sbyte> vbmiLookup0 = Vector512.Create(decoder.VbmiLookup0).AsSByte();
 120964854            Vector512<sbyte> vbmiLookup1 = Vector512.Create(decoder.VbmiLookup1).AsSByte();
 120964855            Vector512<byte> vbmiPackedLanesControl = Vector512.Create(
 120964856                0x06000102, 0x090a0405, 0x0c0d0e08, 0x16101112,
 120964857                0x191a1415, 0x1c1d1e18, 0x26202122, 0x292a2425,
 120964858                0x2c2d2e28, 0x36303132, 0x393a3435, 0x3c3d3e38,
 120964859                0x00000000, 0x00000000, 0x00000000, 0x00000000).AsByte();
 860
 120964861            Vector512<sbyte> mergeConstant0 = Vector512.Create(0x01400140).AsSByte();
 120964862            Vector512<short> mergeConstant1 = Vector512.Create(0x00011000).AsInt16();
 863
 864            // This algorithm requires AVX512VBMI support.
 865            // Vbmi was first introduced in CannonLake and is available from IceLake on.
 866            do
 867            {
 602858868                if (!decoder.TryLoadVector512(src, srcStart, sourceLength, out Vector512<sbyte> str))
 869                {
 870                    break;
 871                }
 872
 873                // Step 1: Translate encoded Base64 input to their original indices
 874                // This step also checks for invalid inputs and exits.
 875                // After this, we have indices which are verified to have upper 2 bits set to 0 in each byte.
 876                // origIndex      = [...|00dddddd|00cccccc|00bbbbbb|00aaaaaa]
 601348877                Vector512<sbyte> origIndex = Avx512Vbmi.PermuteVar64x8x2(vbmiLookup0, str, vbmiLookup1);
 601348878                Vector512<sbyte> errorVec = (origIndex.AsInt32() | str.AsInt32()).AsSByte();
 601348879                if (errorVec.ExtractMostSignificantBits() != 0)
 880                {
 881                    break;
 882                }
 883
 884                // Step 2: Now we need to reshuffle bits to remove the 0 bits.
 885                // multiAdd1: [...|0000cccc|ccdddddd|0000aaaa|aabbbbbb]
 515042886                Vector512<short> multiAdd1 = Avx512BW.MultiplyAddAdjacent(origIndex.AsByte(), mergeConstant0);
 887                // multiAdd1: [...|00000000|aaaaaabb|bbbbcccc|ccdddddd]
 515042888                Vector512<int> multiAdd2 = Avx512BW.MultiplyAddAdjacent(multiAdd1, mergeConstant1);
 889
 890                // Step 3: Pack 48 bytes
 515042891                str = Avx512Vbmi.PermuteVar64x8(multiAdd2.AsByte(), vbmiPackedLanesControl).AsSByte();
 892
 515042893                AssertWrite<Vector512<sbyte>>(dest, destStart, destLength);
 515042894                str.Store((sbyte*)dest);
 515042895                src += 64;
 515042896                dest += 48;
 897            }
 515042898            while (src <= srcEnd);
 899
 120964900            srcBytes = src;
 120964901            destBytes = dest;
 120964902        }
 903
 904        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 905        [CompExactlyDependsOn(typeof(Avx2))]
 906        private static unsafe void Avx2Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* dest
 907            where TBase64Decoder : IBase64Decoder<T>
 908            where T : unmanaged
 909        {
 910            // If we have AVX2 support, pick off 32 bytes at a time for as long as we can,
 911            // but make sure that we quit before seeing any == markers at the end of the
 912            // string. Also, because we write 8 zeroes at the end of the output, ensure
 913            // that there are at least 11 valid bytes of input data remaining to close the
 914            // gap. 32 + 2 + 11 = 45 bytes.
 915
 916            // See SSSE3-version below for an explanation of how the code works.
 917
 918            // The JIT won't hoist these "constants", so help it
 114738919            Vector256<sbyte> lutHi = Vector256.Create(decoder.Avx2LutHigh);
 920
 114738921            Vector256<sbyte> lutLo = Vector256.Create(decoder.Avx2LutLow);
 922
 114738923            Vector256<sbyte> lutShift = Vector256.Create(decoder.Avx2LutShift);
 924
 114738925            Vector256<sbyte> packBytesInLaneMask = Vector256.Create(
 114738926                2, 1, 0, 6,
 114738927                5, 4, 10, 9,
 114738928                8, 14, 13, 12,
 114738929                -1, -1, -1, -1,
 114738930                2, 1, 0, 6,
 114738931                5, 4, 10, 9,
 114738932                8, 14, 13, 12,
 114738933                -1, -1, -1, -1);
 934
 114738935            Vector256<int> packLanesControl = Vector256.Create(
 114738936                 0, 0, 0, 0,
 114738937                1, 0, 0, 0,
 114738938                2, 0, 0, 0,
 114738939                4, 0, 0, 0,
 114738940                5, 0, 0, 0,
 114738941                6, 0, 0, 0,
 114738942                -1, -1, -1, -1,
 114738943                -1, -1, -1, -1).AsInt32();
 944
 114738945            Vector256<sbyte> maskSlashOrUnderscore = Vector256.Create((sbyte)decoder.MaskSlashOrUnderscore);
 114738946            Vector256<sbyte> shiftForUnderscore = Vector256.Create((sbyte)33);
 114738947            Vector256<sbyte> mergeConstant0 = Vector256.Create(0x01400140).AsSByte();
 114738948            Vector256<short> mergeConstant1 = Vector256.Create(0x00011000).AsInt16();
 949
 114738950            T* src = srcBytes;
 114738951            byte* dest = destBytes;
 952
 953            //while (remaining >= 45)
 954            do
 955            {
 120022956                if (!decoder.TryLoadAvxVector256(src, srcStart, sourceLength, out Vector256<sbyte> str))
 957                {
 958                    break;
 959                }
 960
 118310961                Vector256<sbyte> hiNibbles = ((str.AsInt32()) >>> 4).AsSByte() & maskSlashOrUnderscore;
 962
 118310963                if (!decoder.TryDecode256Core(str, hiNibbles, maskSlashOrUnderscore, lutLo, lutHi, lutShift, shiftForUnd
 964                {
 965                    break;
 966                }
 967
 968                // in, lower lane, bits, upper case are most significant bits, lower case are least significant bits:
 969                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 970                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 971                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 972                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 973
 28750974                Vector256<short> merge_ab_and_bc = Avx2.MultiplyAddAdjacent(str.AsByte(), mergeConstant0);
 975                // 0000kkkk LLllllll 0000JJJJ JJjjKKKK
 976                // 0000hhhh IIiiiiii 0000GGGG GGggHHHH
 977                // 0000eeee FFffffff 0000DDDD DDddEEEE
 978                // 0000bbbb CCcccccc 0000AAAA AAaaBBBB
 979
 28750980                Vector256<int> output = Avx2.MultiplyAddAdjacent(merge_ab_and_bc, mergeConstant1);
 981                // 00000000 JJJJJJjj KKKKkkkk LLllllll
 982                // 00000000 GGGGGGgg HHHHhhhh IIiiiiii
 983                // 00000000 DDDDDDdd EEEEeeee FFffffff
 984                // 00000000 AAAAAAaa BBBBbbbb CCcccccc
 985
 986                // Pack bytes together in each lane:
 28750987                output = Avx2.Shuffle(output.AsSByte(), packBytesInLaneMask).AsInt32();
 988                // 00000000 00000000 00000000 00000000
 989                // LLllllll KKKKkkkk JJJJJJjj IIiiiiii
 990                // HHHHhhhh GGGGGGgg FFffffff EEEEeeee
 991                // DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa
 992
 993                // Pack lanes
 28750994                str = Avx2.PermuteVar8x32(output, packLanesControl).AsSByte();
 995
 28750996                AssertWrite<Vector256<sbyte>>(dest, destStart, destLength);
 28750997                Avx.Store(dest, str.AsByte());
 998
 28750999                src += 32;
 287501000                dest += 24;
 1001            }
 287501002            while (src <= srcEnd);
 1003
 1147381004            srcBytes = src;
 1147381005            destBytes = dest;
 1147381006        }
 1007
 1008        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1009        [CompExactlyDependsOn(typeof(Ssse3))]
 1010        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1011        [CompExactlyDependsOn(typeof(PackedSimd))]
 1012        internal static Vector128<byte> SimdShuffle(Vector128<byte> left, Vector128<byte> right, Vector128<byte> mask8F)
 1013        {
 1014            Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && BitConverter.IsLi
 1015
 4600561016            if (Ssse3.IsSupported)
 1017            {
 4600561018                return Ssse3.Shuffle(left, right);
 1019            }
 1020            else if (PackedSimd.IsSupported)
 1021            {
 1022                return PackedSimd.Swizzle(left, right & mask8F);
 1023            }
 1024            else
 1025            {
 01026                return AdvSimd.Arm64.VectorTableLookup(left, right & mask8F);
 1027            }
 1028        }
 1029
 1030        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1031        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1032        private static unsafe void AdvSimdDecode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* d
 1033            where TBase64Decoder : IBase64Decoder<T>
 1034            where T : unmanaged
 1035        {
 1036            // C# implementation of https://github.com/aklomp/base64/blob/3a5add8652076612a8407627a42c768736a4263f/lib/a
 1037            // If we have AdvSimd support, pick off 64 bytes at a time for as long as we can,
 1038            // but make sure that we quit before seeing any == markers at the end of the
 1039            // string. 64 + 2 = 66 bytes.
 1040
 1041            // In the decoding process, we want to map each byte, representing a Base64 value, to its 6-bit (0-63) repre
 1042            // It uses the following mapping. Values outside the following groups are invalid and, we abort decoding whe
 1043            //
 1044            // #    From       To         Char
 1045            // 1    [43]       [62]       +
 1046            // 2    [47]       [63]       /
 1047            // 3    [48..57]   [52..61]   0..9
 1048            // 4    [65..90]   [0..25]    A..Z
 1049            // 5    [97..122]  [26..51]   a..z
 1050            //
 1051            // To map an input value to its Base64 representation, we use look-up tables 'decLutOne' and 'decLutTwo'.
 1052            // 'decLutOne' helps to map groups 1, 2 and 3 while 'decLutTwo' maps groups 4 and 5 in the above list.
 1053            // After mapping, each value falls between 0-63. Consequently, the last six bits of each byte now hold a val
 1054            // We then compress four such bytes (with valid 4 * 6 = 24 bits) to three UTF8 bytes (3 * 8 = 24 bits).
 1055            // For faster decoding, we use SIMD operations that allow the processing of multiple bytes together.
 1056            // However, the compress operation on adjacent values of a vector could be slower. Thus, we de-interleave wh
 1057            // the input bytes that store adjacent bytes in separate vectors. This later simplifies the compress step wi
 1058            // of logical operations. This requires interleaving while storing the decoded result.
 1059
 1060            // Values in 'decLutOne' maps input values from 0 to 63.
 1061            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
 1062            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
 1063            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63
 1064            //    52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 255, 255, 255
 1065            var decLutOne = (Vector128<byte>.AllBitsSet,
 1066                             Vector128<byte>.AllBitsSet,
 1067                             Vector128.Create(decoder.AdvSimdLutOne3).AsByte(),
 1068                             Vector128.Create(0x37363534, 0x3B3A3938, 0xFFFF3D3C, 0xFFFFFFFF).AsByte());
 1069
 1070            // Values in 'decLutTwo' maps input values from 63 to 127.
 1071            //    0, 255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13
 1072            //   14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255
 1073            //  255, 255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39
 1074            //   40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255
 1075            var decLutTwo = (Vector128.Create(0x0100FF00, 0x05040302, 0x09080706, 0x0D0C0B0A).AsByte(),
 1076                             Vector128.Create(0x11100F0E, 0x15141312, 0x19181716, 0xFFFFFFFF).AsByte(),
 1077                             Vector128.Create(decoder.AdvSimdLutTwo3Uint1, 0x1F1E1D1C, 0x23222120, 0x27262524).AsByte(),
 1078                             Vector128.Create(0x2B2A2928, 0x2F2E2D2C, 0x33323130, 0xFFFFFFFF).AsByte());
 1079
 1080            T* src = srcBytes;
 1081            byte* dest = destBytes;
 1082            Vector128<byte> offset = Vector128.Create<byte>(63);
 1083
 1084            do
 1085            {
 1086                // Step 1: Load 64 bytes and de-interleave.
 1087                if (!decoder.TryLoadArmVector128x4(src, srcStart, sourceLength,
 1088                    out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> st
 1089                {
 1090                    break;
 1091                }
 1092
 1093                // Step 2: Map each valid input to its Base64 value.
 1094                // We use two look-ups to compute partial results and combine them later.
 1095
 1096                // Step 2.1: Detect valid Base64 values from the first three groups. Maps input as,
 1097                //  0 to  63 (Invalid) => 255
 1098                //  0 to  63 (Valid)   => Their Base64 equivalent
 1099                // 64 to 255           => 0
 1100
 1101                // Each input value acts as an index in the look-up table 'decLutOne'.
 1102                // e.g., for group 1: index 43 maps to 62 (Base64 '+').
 1103                // Group 4 and 5 values are out-of-range (>64), so they are mapped to zero.
 1104                // Other valid indices but invalid values are mapped to 255.
 1105                Vector128<byte> decOne1 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str1);
 1106                Vector128<byte> decOne2 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str2);
 1107                Vector128<byte> decOne3 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str3);
 1108                Vector128<byte> decOne4 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str4);
 1109
 1110                // Step 2.2: Detect valid Base64 values from groups 4 and 5. Maps input as,
 1111                //   0 to  63           => 0
 1112                //  64 to 122 (Valid)   => Their Base64 equivalent
 1113                //  64 to 122 (Invalid) => 255
 1114                // 123 to 255           => Remains unchanged
 1115
 1116                // Subtract/offset each input value by 63 so that it can be used as a valid offset.
 1117                // Subtract saturate makes values from the first three groups set to zero that are
 1118                // then mapped to zero in the subsequent look-up.
 1119                Vector128<byte> decTwo1 = AdvSimd.SubtractSaturate(str1, offset);
 1120                Vector128<byte> decTwo2 = AdvSimd.SubtractSaturate(str2, offset);
 1121                Vector128<byte> decTwo3 = AdvSimd.SubtractSaturate(str3, offset);
 1122                Vector128<byte> decTwo4 = AdvSimd.SubtractSaturate(str4, offset);
 1123
 1124                // We use VTBX to map values where out-of-range indices are unchanged.
 1125                decTwo1 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo1, decLutTwo, decTwo1);
 1126                decTwo2 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo2, decLutTwo, decTwo2);
 1127                decTwo3 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo3, decLutTwo, decTwo3);
 1128                decTwo4 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo4, decLutTwo, decTwo4);
 1129
 1130                // Step 3: Combine the partial result.
 1131                // Each look-up above maps valid values to their Base64 equivalent or zero.
 1132                // Thus the intermediate results 'decOne' and 'decTwo' could be OR-ed to get final values.
 1133                str1 = (decOne1 | decTwo1);
 1134                str2 = (decOne2 | decTwo2);
 1135                str3 = (decOne3 | decTwo3);
 1136                str4 = (decOne4 | decTwo4);
 1137
 1138                // Step 4: Detect an invalid input value.
 1139                // Invalid values < 122 are set to 255 while the ones above 122 are unchanged.
 1140                // Check for invalid input, any value larger than 63.
 1141                Vector128<byte> classified = (Vector128.GreaterThan(str1, offset)
 1142                                            | Vector128.GreaterThan(str2, offset)
 1143                                            | Vector128.GreaterThan(str3, offset)
 1144                                            | Vector128.GreaterThan(str4, offset));
 1145
 1146                // Check that all bits are zero.
 1147                if (classified != Vector128<byte>.Zero)
 1148                {
 1149                    break;
 1150                }
 1151
 1152                // Step 5: Compress four bytes into three.
 1153                Vector128<byte> res1 = ((str1 << 2) | (str2 >> 4));
 1154                Vector128<byte> res2 = ((str2 << 4) | (str3 >> 2));
 1155                Vector128<byte> res3 = ((str3 << 6) | str4);
 1156
 1157                // Step 6: Interleave and store decoded results.
 1158                AssertWrite<Vector128<byte>>(dest, destStart, destLength);
 1159                AdvSimd.Arm64.StoreVectorAndZip(dest, (res1, res2, res3));
 1160
 1161                src += 64;
 1162                dest += 48;
 1163            }
 1164            while (src <= srcEnd);
 1165
 1166            srcBytes = src;
 1167            destBytes = dest;
 1168        }
 1169
 1170        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1171        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1172        [CompExactlyDependsOn(typeof(Ssse3))]
 1173        [CompExactlyDependsOn(typeof(PackedSimd))]
 1174        private static unsafe void Vector128Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte*
 1175            where TBase64Decoder : IBase64Decoder<T>
 1176            where T : unmanaged
 1177        {
 1178            Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && BitConverter.IsLi
 1179
 1180            // If we have Vector128 support, pick off 16 bytes at a time for as long as we can,
 1181            // but make sure that we quit before seeing any == markers at the end of the
 1182            // string. Also, because we write four zeroes at the end of the output, ensure
 1183            // that there are at least 6 valid bytes of input data remaining to close the
 1184            // gap. 16 + 2 + 6 = 24 bytes.
 1185
 1186            // The input consists of six character sets in the Base64 alphabet,
 1187            // which we need to map back to the 6-bit values they represent.
 1188            // There are three ranges, two singles, and then there's the rest.
 1189            //
 1190            //  #  From       To        Add  Characters
 1191            //  1  [43]       [62]      +19  +
 1192            //  2  [47]       [63]      +16  /
 1193            //  3  [48..57]   [52..61]   +4  0..9
 1194            //  4  [65..90]   [0..25]   -65  A..Z
 1195            //  5  [97..122]  [26..51]  -71  a..z
 1196            // (6) Everything else => invalid input
 1197
 1198            // We will use LUTS for character validation & offset computation
 1199            // Remember that 0x2X and 0x0X are the same index for _mm_shuffle_epi8,
 1200            // this allows to mask with 0x2F instead of 0x0F and thus save one constant declaration (register and/or mem
 1201
 1202            // For offsets:
 1203            // Perfect hash for lut = ((src>>4)&0x2F)+((src==0x2F)?0xFF:0x00)
 1204            // 0000 = garbage
 1205            // 0001 = /
 1206            // 0010 = +
 1207            // 0011 = 0-9
 1208            // 0100 = A-Z
 1209            // 0101 = A-Z
 1210            // 0110 = a-z
 1211            // 0111 = a-z
 1212            // 1000 >= garbage
 1213
 1214            // For validation, here's the table.
 1215            // A character is valid if and only if the AND of the 2 lookups equals 0:
 1216
 1217            // hi \ lo              0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
 1218            //      LUT             0x15 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x13 0x1A 0x1B 0x1B 0x1B 0x1A
 1219
 1220            // 0000 0X10 char        NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL   BS   HT   LF   VT   FF   CR   SO   SI
 1221            //           andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1222
 1223            // 0001 0x10 char        DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB  CAN   EM  SUB  ESC   FS   GS   RS   US
 1224            //           andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1225
 1226            // 0010 0x01 char               !    "    #    $    %    &    '    (    )    *    +    ,    -    .    /
 1227            //           andlut     0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00 0x01 0x01 0x01 0x00
 1228
 1229            // 0011 0x02 char          0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?
 1230            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x02 0x02 0x02 0x02 0x02
 1231
 1232            // 0100 0x04 char          @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    0
 1233            //           andlut     0x04 0x00 0x00 0x00 0X00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
 1234
 1235            // 0101 0x08 char          P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _
 1236            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08
 1237
 1238            // 0110 0x04 char          `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o
 1239            //           andlut     0x04 0x00 0x00 0x00 0X00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
 1240            // 0111 0X08 char          p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~
 1241            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08
 1242
 1243            // 1000 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1244            // 1001 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1245            // 1010 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1246            // 1011 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1247            // 1100 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1248            // 1101 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1249            // 1110 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1250            // 1111 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1251
 1252            // The JIT won't hoist these "constants", so help it
 1294281253            Vector128<byte> lutHi = Vector128.Create(decoder.Vector128LutHigh).AsByte();
 1294281254            Vector128<byte> lutLo = Vector128.Create(decoder.Vector128LutLow).AsByte();
 1294281255            Vector128<sbyte> lutShift = Vector128.Create(decoder.Vector128LutShift).AsSByte();
 1294281256            Vector128<sbyte> packBytesMask = Vector128.Create(0x06000102, 0x090A0405, 0x0C0D0E08, 0xffffffff).AsSByte();
 1294281257            Vector128<byte> mergeConstant0 = Vector128.Create(0x01400140).AsByte();
 1294281258            Vector128<short> mergeConstant1 = Vector128.Create(0x00011000).AsInt16();
 1294281259            Vector128<byte> one = Vector128<byte>.One;
 1294281260            Vector128<byte> mask2F = Vector128.Create(decoder.MaskSlashOrUnderscore);
 1294281261            Vector128<byte> mask8F = Vector128.Create((byte)0x8F);
 1294281262            Vector128<byte> shiftForUnderscore = Vector128.Create((byte)33);
 1294281263            T* src = srcBytes;
 1294281264            byte* dest = destBytes;
 1265
 1266            //while (remaining >= 24)
 1267            do
 1268            {
 1473041269                if (!decoder.TryLoadVector128(src, srcStart, sourceLength, out Vector128<byte> str))
 1270                {
 1271                    break;
 1272                }
 1273
 1274                // lookup
 1452201275                Vector128<byte> hiNibbles = Vector128.ShiftRightLogical(str.AsInt32(), 4).AsByte() & mask2F;
 1276
 1452201277                if (!decoder.TryDecode128Core(str, hiNibbles, mask2F, mask8F, lutLo, lutHi, lutShift, shiftForUnderscore
 1278                {
 1279                    break;
 1280                }
 1281
 1282                // in, bits, upper case are most significant bits, lower case are least significant bits
 1283                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 1284                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 1285                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 1286                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 1287
 1288                Vector128<short> merge_ab_and_bc;
 526941289                if (Ssse3.IsSupported)
 1290                {
 526941291                    merge_ab_and_bc = Ssse3.MultiplyAddAdjacent(str.AsByte(), mergeConstant0.AsSByte());
 1292                }
 1293                else if (AdvSimd.Arm64.IsSupported)
 1294                {
 1295                    Vector128<ushort> evens = AdvSimd.ShiftLeftLogicalWideningLower(AdvSimd.Arm64.UnzipEven(str, one).Ge
 1296                    Vector128<ushort> odds = AdvSimd.Arm64.TransposeOdd(str, Vector128<byte>.Zero).AsUInt16();
 1297                    merge_ab_and_bc = Vector128.Add(evens, odds).AsInt16();
 1298                }
 1299                else if (PackedSimd.IsSupported)
 1300                {
 1301                    // MultiplyAddAdjacent by {64,1,...} is the even byte (low of each u16 lane) times 64 plus the odd b
 1302                    Vector128<ushort> u = str.AsUInt16();
 1303                    Vector128<ushort> evens = Vector128.ShiftLeft(u & Vector128.Create((ushort)0x00FF), 6);
 1304                    Vector128<ushort> odds = Vector128.ShiftRightLogical(u, 8);
 1305                    merge_ab_and_bc = (evens + odds).AsInt16();
 1306                }
 1307                else
 1308                {
 1309                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 01310                    ThrowUnreachableException();
 1311                    merge_ab_and_bc = default;
 1312                }
 1313                // 0000kkkk LLllllll 0000JJJJ JJjjKKKK
 1314                // 0000hhhh IIiiiiii 0000GGGG GGggHHHH
 1315                // 0000eeee FFffffff 0000DDDD DDddEEEE
 1316                // 0000bbbb CCcccccc 0000AAAA AAaaBBBB
 1317
 1318                Vector128<int> output;
 526941319                if (Ssse3.IsSupported)
 1320                {
 526941321                    output = Sse2.MultiplyAddAdjacent(merge_ab_and_bc, mergeConstant1);
 1322                }
 1323                else if (AdvSimd.Arm64.IsSupported)
 1324                {
 1325                    Vector128<int> ievens = AdvSimd.ShiftLeftLogicalWideningLower(AdvSimd.Arm64.UnzipEven(merge_ab_and_b
 1326                    Vector128<int> iodds = AdvSimd.Arm64.TransposeOdd(merge_ab_and_bc, Vector128<short>.Zero).AsInt32();
 1327                    output = Vector128.Add(ievens, iodds).AsInt32();
 1328                }
 1329                else if (PackedSimd.IsSupported)
 1330                {
 1331                    // MultiplyAddAdjacent by {4096,1,...} is the even i16 (low of each i32 lane) times 4096 plus the od
 1332                    Vector128<uint> m = merge_ab_and_bc.AsUInt32();
 1333                    Vector128<uint> ievens = Vector128.ShiftLeft(m & Vector128.Create(0x0000FFFFu), 12);
 1334                    Vector128<uint> iodds = Vector128.ShiftRightLogical(m, 16);
 1335                    output = (ievens + iodds).AsInt32();
 1336                }
 1337                else
 1338                {
 1339                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 01340                    ThrowUnreachableException();
 1341                    output = default;
 1342                }
 1343                // 00000000 JJJJJJjj KKKKkkkk LLllllll
 1344                // 00000000 GGGGGGgg HHHHhhhh IIiiiiii
 1345                // 00000000 DDDDDDdd EEEEeeee FFffffff
 1346                // 00000000 AAAAAAaa BBBBbbbb CCcccccc
 1347
 1348                // Pack bytes together:
 526941349                str = SimdShuffle(output.AsByte(), packBytesMask.AsByte(), mask8F);
 1350                // 00000000 00000000 00000000 00000000
 1351                // LLllllll KKKKkkkk JJJJJJjj IIiiiiii
 1352                // HHHHhhhh GGGGGGgg FFffffff EEEEeeee
 1353                // DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa
 1354
 526941355                AssertWrite<Vector128<sbyte>>(dest, destStart, destLength);
 526941356                str.Store(dest);
 1357
 526941358                src += 16;
 526941359                dest += 12;
 1360            }
 526941361            while (src <= srcEnd);
 1362
 1294281363            srcBytes = src;
 1294281364            destBytes = dest;
 1294281365        }
 1366#endif
 1367
 1368        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1369        private static unsafe void WriteThreeLowOrderBytes(byte* destination, int value)
 1370        {
 5285661371            destination[0] = (byte)(value >> 16);
 5285661372            destination[1] = (byte)(value >> 8);
 5285661373            destination[2] = (byte)value;
 5285661374        }
 1375
 1376        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1377        internal static bool IsWhiteSpace(int value)
 1378        {
 11706721379            Debug.Assert(value >= 0 && value <= ushort.MaxValue);
 1380            uint charMinusLowUInt32;
 11706721381            return (int)((0xC8000100U << (short)(charMinusLowUInt32 = (ushort)(value - '\t'))) & (charMinusLowUInt32 - 3
 1382        }
 1383
 1384        internal readonly struct Base64DecoderByte : IBase64Decoder<byte>
 1385        {
 1386            // Pre-computing this table using a custom string(s_characters) and GenerateDecodingMapAndVerify (found in t
 1387            public ReadOnlySpan<sbyte> DecodingMap =>
 3290441388                [
 3290441389                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 3290441390                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 3290441391                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,         //62 is placed at index 43 (
 3290441392                    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,         //52-61 are placed at index 
 3290441393                    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
 3290441394                    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,         //0-25 are placed at index 6
 3290441395                    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
 3290441396                    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,         //26-51 are placed at index 
 3290441397                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,         // Bytes over 122 ('z') are 
 3290441398                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,         // Hence, padding the map wi
 3290441399                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 3290441400                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 3290441401                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 3290441402                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 3290441403                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 3290441404                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 3290441405                ];
 1406
 1407            public ReadOnlySpan<uint> VbmiLookup0 =>
 1209641408                [
 1209641409                    0x80808080, 0x80808080, 0x80808080, 0x80808080,
 1209641410                    0x80808080, 0x80808080, 0x80808080, 0x80808080,
 1209641411                    0x80808080, 0x80808080, 0x3e808080, 0x3f808080,
 1209641412                    0x37363534, 0x3b3a3938, 0x80803d3c, 0x80808080
 1209641413                ];
 1414
 1415            public ReadOnlySpan<uint> VbmiLookup1 =>
 1209641416                [
 1209641417                    0x02010080, 0x06050403, 0x0a090807, 0x0e0d0c0b,
 1209641418                    0x1211100f, 0x16151413, 0x80191817, 0x80808080,
 1209641419                    0x1c1b1a80, 0x201f1e1d, 0x24232221, 0x28272625,
 1209641420                    0x2c2b2a29, 0x302f2e2d, 0x80333231, 0x80808080
 1209641421                ];
 1422
 1423            public ReadOnlySpan<sbyte> Avx2LutHigh =>
 1147381424                [
 1147381425                    0x10, 0x10, 0x01, 0x02,
 1147381426                    0x04, 0x08, 0x04, 0x08,
 1147381427                    0x10, 0x10, 0x10, 0x10,
 1147381428                    0x10, 0x10, 0x10, 0x10,
 1147381429                    0x10, 0x10, 0x01, 0x02,
 1147381430                    0x04, 0x08, 0x04, 0x08,
 1147381431                    0x10, 0x10, 0x10, 0x10,
 1147381432                    0x10, 0x10, 0x10, 0x10
 1147381433                ];
 1434
 1435            public ReadOnlySpan<sbyte> Avx2LutLow =>
 1147381436                [
 1147381437                    0x15, 0x11, 0x11, 0x11,
 1147381438                    0x11, 0x11, 0x11, 0x11,
 1147381439                    0x11, 0x11, 0x13, 0x1A,
 1147381440                    0x1B, 0x1B, 0x1B, 0x1A,
 1147381441                    0x15, 0x11, 0x11, 0x11,
 1147381442                    0x11, 0x11, 0x11, 0x11,
 1147381443                    0x11, 0x11, 0x13, 0x1A,
 1147381444                    0x1B, 0x1B, 0x1B, 0x1A
 1147381445                ];
 1446
 1447            public ReadOnlySpan<sbyte> Avx2LutShift =>
 1147381448                [
 1147381449                    0, 16, 19, 4,
 1147381450                    -65, -65, -71, -71,
 1147381451                    0, 0, 0, 0,
 1147381452                    0, 0, 0, 0,
 1147381453                    0, 16, 19, 4,
 1147381454                    -65, -65, -71, -71,
 1147381455                    0, 0, 0, 0,
 1147381456                    0, 0, 0, 0
 1147381457                ];
 1458
 2441661459            public byte MaskSlashOrUnderscore => (byte)'/';
 1460
 1294281461            public ReadOnlySpan<int> Vector128LutHigh => [0x02011010, 0x08040804, 0x10101010, 0x10101010];
 1462
 1294281463            public ReadOnlySpan<int> Vector128LutLow => [0x11111115, 0x11111111, 0x1A131111, 0x1A1B1B1B];
 1464
 1294281465            public ReadOnlySpan<uint> Vector128LutShift => [0x04131000, 0xb9b9bfbf, 0x00000000, 0x00000000];
 1466
 01467            public ReadOnlySpan<uint> AdvSimdLutOne3 => [0xFFFFFFFF, 0xFFFFFFFF, 0x3EFFFFFF, 0x3FFFFFFF];
 1468
 01469            public uint AdvSimdLutTwo3Uint1 => 0x1B1AFFFF;
 1470
 864621471            public int GetMaxDecodedLength(int utf8Length) => Base64.GetMaxDecodedFromUtf8Length(utf8Length);
 1472
 1311641473            public bool IsInvalidLength(int bufferLength) => bufferLength % 4 != 0; // only decode input if it is a mult
 1474
 2240381475            public bool IsValidPadding(uint padChar) => padChar == EncodingPad;
 1476
 864621477            public int SrcLength(bool _, int utf8Length) => utf8Length & ~0x3;  // only decode input up to the closest m
 1478
 1479#if NET
 1480            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1481            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1482            [CompExactlyDependsOn(typeof(Ssse3))]
 1483            [CompExactlyDependsOn(typeof(PackedSimd))]
 1484            public bool TryDecode128Core(
 1485                Vector128<byte> str,
 1486                Vector128<byte> hiNibbles,
 1487                Vector128<byte> maskSlashOrUnderscore,
 1488                Vector128<byte> mask8F,
 1489                Vector128<byte> lutLow,
 1490                Vector128<byte> lutHigh,
 1491                Vector128<sbyte> lutShift,
 1492                Vector128<byte> _,
 1493                out Vector128<byte> result)
 1494            {
 1452201495                Vector128<byte> loNibbles = str & maskSlashOrUnderscore;
 1452201496                Vector128<byte> hi = SimdShuffle(lutHigh, hiNibbles, mask8F);
 1452201497                Vector128<byte> lo = SimdShuffle(lutLow, loNibbles, mask8F);
 1498
 1499                // Check for invalid input: if any "and" values from lo and hi are not zero,
 1500                // fall back on bytewise code to do error checking and reporting:
 1452201501                if ((lo & hi) != Vector128<byte>.Zero)
 1502                {
 925261503                    result = default;
 925261504                    return false;
 1505                }
 1506
 526941507                Vector128<byte> eq2F = Vector128.Equals(str, maskSlashOrUnderscore);
 526941508                Vector128<byte> shift = SimdShuffle(lutShift.AsByte(), (eq2F + hiNibbles), mask8F);
 1509
 1510                // Now simply add the delta values to the input:
 526941511                result = str + shift;
 1512
 526941513                return true;
 1514            }
 1515
 1516            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1517            [CompExactlyDependsOn(typeof(Avx2))]
 1518            public bool TryDecode256Core(
 1519                Vector256<sbyte> str,
 1520                Vector256<sbyte> hiNibbles,
 1521                Vector256<sbyte> maskSlashOrUnderscore,
 1522                Vector256<sbyte> lutLow,
 1523                Vector256<sbyte> lutHigh,
 1524                Vector256<sbyte> lutShift,
 1525                Vector256<sbyte> _,
 1526                out Vector256<sbyte> result)
 1527            {
 1183101528                Vector256<sbyte> loNibbles = str & maskSlashOrUnderscore;
 1183101529                Vector256<sbyte> hi = Avx2.Shuffle(lutHigh, hiNibbles);
 1183101530                Vector256<sbyte> lo = Avx2.Shuffle(lutLow, loNibbles);
 1531
 1183101532                if ((lo & hi) != Vector256<sbyte>.Zero)
 1533                {
 895601534                    result = default;
 895601535                    return false;
 1536                }
 1537
 287501538                Vector256<sbyte> eq2F = Avx2.CompareEqual(str, maskSlashOrUnderscore);
 287501539                Vector256<sbyte> shift = Avx2.Shuffle(lutShift, eq2F + hiNibbles);
 1540
 287501541                result = str + shift;
 1542
 287501543                return true;
 1544            }
 1545
 1546            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1547            public unsafe bool TryLoadVector512(byte* src, byte* srcStart, int sourceLength, out Vector512<sbyte> str)
 1548            {
 2413341549                AssertRead<Vector512<sbyte>>(src, srcStart, sourceLength);
 2413341550                str = Vector512.Load(src).AsSByte();
 2413341551                return true;
 1552            }
 1553
 1554            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1555            [CompExactlyDependsOn(typeof(Avx2))]
 1556            public unsafe bool TryLoadAvxVector256(byte* src, byte* srcStart, int sourceLength, out Vector256<sbyte> str
 1557            {
 437041558                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 437041559                str = Avx.LoadVector256(src).AsSByte();
 437041560                return true;
 1561            }
 1562
 1563            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1564            public unsafe bool TryLoadVector128(byte* src, byte* srcStart, int sourceLength, out Vector128<byte> str)
 1565            {
 588681566                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 588681567                str = Vector128.LoadUnsafe(ref *src);
 588681568                return true;
 1569            }
 1570
 1571            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1572            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1573            public unsafe bool TryLoadArmVector128x4(byte* src, byte* srcStart, int sourceLength,
 1574                out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> str4)
 1575            {
 01576                AssertRead<Vector128<byte>>(src, srcStart, sourceLength);
 01577                (str1, str2, str3, str4) = AdvSimd.Arm64.Load4xVector128AndUnzip(src);
 1578
 01579                return true;
 1580            }
 1581#endif // NET
 1582
 1583            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1584            public unsafe int DecodeFourElements(byte* source, ref sbyte decodingMap)
 1585            {
 1586                // The 'source' span expected to have at least 4 elements, and the 'decodingMap' consists 256 sbytes
 1587181587                uint t0 = source[0];
 1587181588                uint t1 = source[1];
 1587181589                uint t2 = source[2];
 1587181590                uint t3 = source[3];
 1591
 1587181592                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 1587181593                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 1587181594                int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 1587181595                int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 1596
 1587181597                i0 <<= 18;
 1587181598                i1 <<= 12;
 1587181599                i2 <<= 6;
 1600
 1587181601                i0 |= i3;
 1587181602                i1 |= i2;
 1603
 1587181604                i0 |= i1;
 1587181605                return i0;
 1606            }
 1607
 1608            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1609            public unsafe int DecodeRemaining(byte* srcEnd, ref sbyte decodingMap, long remaining, out uint t2, out uint
 1610            {
 1611                uint t0;
 1612                uint t1;
 161601613                t2 = EncodingPad;
 161601614                t3 = EncodingPad;
 1615                switch (remaining)
 1616                {
 1617                    case 2:
 01618                        t0 = srcEnd[-2];
 01619                        t1 = srcEnd[-1];
 01620                        break;
 1621                    case 3:
 01622                        t0 = srcEnd[-3];
 01623                        t1 = srcEnd[-2];
 01624                        t2 = srcEnd[-1];
 01625                        break;
 1626                    case 4:
 161601627                        t0 = srcEnd[-4];
 161601628                        t1 = srcEnd[-3];
 161601629                        t2 = srcEnd[-2];
 161601630                        t3 = srcEnd[-1];
 161601631                        break;
 1632                    default:
 01633                        return -1;
 1634                }
 1635
 161601636                int i0 = Unsafe.Add(ref decodingMap, (IntPtr)t0);
 161601637                int i1 = Unsafe.Add(ref decodingMap, (IntPtr)t1);
 1638
 161601639                i0 <<= 18;
 161601640                i1 <<= 12;
 1641
 161601642                i0 |= i1;
 161601643                return i0;
 1644            }
 1645
 1646            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1647            public int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<byte> span)
 1648            {
 1307641649                for (int i = 0; i < span.Length; i++)
 1650                {
 653321651                    if (!IsWhiteSpace(span[i]))
 1652                    {
 332001653                        return i;
 1654                    }
 1655                }
 1656
 501657                return -1;
 1658            }
 1659
 1660            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1661            public OperationStatus DecodeWithWhiteSpaceBlockwiseWrapper<TBase64Decoder>(TBase64Decoder decoder, ReadOnly
 1662                Span<byte> bytes, ref int bytesConsumed, ref int bytesWritten, bool isFinalBlock = true)
 1663                where TBase64Decoder : IBase64Decoder<byte> =>
 36881664                DecodeWithWhiteSpaceBlockwise(decoder, utf8, bytes, ref bytesConsumed, ref bytesWritten, isFinalBlock);
 1665        }
 1666
 1667        internal readonly struct Base64DecoderChar : IBase64Decoder<ushort>
 1668        {
 1136701669            public ReadOnlySpan<sbyte> DecodingMap => default(Base64DecoderByte).DecodingMap;
 1670
 769341671            public ReadOnlySpan<uint> VbmiLookup0 => default(Base64DecoderByte).VbmiLookup0;
 1672
 769341673            public ReadOnlySpan<uint> VbmiLookup1 => default(Base64DecoderByte).VbmiLookup1;
 1674
 734561675            public ReadOnlySpan<sbyte> Avx2LutHigh => default(Base64DecoderByte).Avx2LutHigh;
 1676
 734561677            public ReadOnlySpan<sbyte> Avx2LutLow => default(Base64DecoderByte).Avx2LutLow;
 1678
 734561679            public ReadOnlySpan<sbyte> Avx2LutShift => default(Base64DecoderByte).Avx2LutShift;
 1680
 1558501681            public byte MaskSlashOrUnderscore => default(Base64DecoderByte).MaskSlashOrUnderscore;
 1682
 823941683            public ReadOnlySpan<int> Vector128LutHigh => default(Base64DecoderByte).Vector128LutHigh;
 1684
 823941685            public ReadOnlySpan<int> Vector128LutLow => default(Base64DecoderByte).Vector128LutLow;
 1686
 823941687            public ReadOnlySpan<uint> Vector128LutShift => default(Base64DecoderByte).Vector128LutShift;
 1688
 01689            public ReadOnlySpan<uint> AdvSimdLutOne3 => default(Base64DecoderByte).AdvSimdLutOne3;
 1690
 01691            public uint AdvSimdLutTwo3Uint1 => default(Base64DecoderByte).AdvSimdLutTwo3Uint1;
 1692
 1136701693            public int GetMaxDecodedLength(int sourceLength) => Base64.GetMaxDecodedFromUtf8Length(sourceLength);
 1694
 01695            public bool IsInvalidLength(int bufferLength) => bufferLength % 4 != 0;
 1696
 810501697            public bool IsValidPadding(uint padChar) => padChar == EncodingPad;
 1698
 1136701699            public int SrcLength(bool _, int sourceLength) => sourceLength & ~0x3;
 1700
 1701#if NET
 1702            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1703            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1704            [CompExactlyDependsOn(typeof(Ssse3))]
 1705            [CompExactlyDependsOn(typeof(PackedSimd))]
 1706            public bool TryDecode128Core(Vector128<byte> str, Vector128<byte> hiNibbles, Vector128<byte> maskSlashOrUnde
 1707                Vector128<byte> lutLow, Vector128<byte> lutHigh, Vector128<sbyte> lutShift, Vector128<byte> shiftForUnde
 863521708                default(Base64DecoderByte).TryDecode128Core(str, hiNibbles, maskSlashOrUnderscore, mask8F, lutLow, lutHi
 1709
 1710            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1711            [CompExactlyDependsOn(typeof(Avx2))]
 1712            public bool TryDecode256Core(Vector256<sbyte> str, Vector256<sbyte> hiNibbles, Vector256<sbyte> maskSlashOrU
 1713                Vector256<sbyte> lutHigh, Vector256<sbyte> lutShift, Vector256<sbyte> shiftForUnderscore, out Vector256<
 746061714                default(Base64DecoderByte).TryDecode256Core(str, hiNibbles, maskSlashOrUnderscore, lutLow, lutHigh, lutS
 1715
 1716            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1717            public unsafe bool TryLoadVector512(ushort* src, ushort* srcStart, int sourceLength, out Vector512<sbyte> st
 1718            {
 3615241719                AssertRead<Vector512<ushort>>(src, srcStart, sourceLength);
 3615241720                Vector512<ushort> utf16VectorLower = Vector512.Load(src);
 3615241721                Vector512<ushort> utf16VectorUpper = Vector512.Load(src + 32);
 3615241722                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1723                {
 15101724                    str = default;
 15101725                    return false;
 1726                }
 1727
 3600141728                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper).AsSByte();
 3600141729                return true;
 1730            }
 1731
 1732            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1733            [CompExactlyDependsOn(typeof(Avx2))]
 1734            public unsafe bool TryLoadAvxVector256(ushort* src, ushort* srcStart, int sourceLength, out Vector256<sbyte>
 1735            {
 763181736                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 763181737                Vector256<ushort> utf16VectorLower = Avx.LoadVector256(src);
 763181738                Vector256<ushort> utf16VectorUpper = Avx.LoadVector256(src + 16);
 1739
 763181740                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1741                {
 17121742                    str = default;
 17121743                    return false;
 1744                }
 1745
 746061746                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper).AsSByte();
 746061747                return true;
 1748            }
 1749
 1750            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1751            public unsafe bool TryLoadVector128(ushort* src, ushort* srcStart, int sourceLength, out Vector128<byte> str
 1752            {
 884361753                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 884361754                Vector128<ushort> utf16VectorLower = Vector128.LoadUnsafe(ref *src);
 884361755                Vector128<ushort> utf16VectorUpper = Vector128.LoadUnsafe(ref *src, 8);
 884361756                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1757                {
 20841758                    str = default;
 20841759                    return false;
 1760                }
 1761
 863521762                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper);
 863521763                return true;
 1764            }
 1765
 1766            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1767            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1768            public unsafe bool TryLoadArmVector128x4(ushort* src, ushort* srcStart, int sourceLength,
 1769                out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> str4)
 1770            {
 01771                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 01772                var (s11, s12, s21, s22) = AdvSimd.Arm64.Load4xVector128AndUnzip(src);
 01773                var (s31, s32, s41, s42) = AdvSimd.Arm64.Load4xVector128AndUnzip(src + 32);
 1774
 01775                if (Ascii.VectorContainsNonAsciiChar(s11 | s12 | s21 | s22 | s31 | s32 | s41 | s42))
 1776                {
 01777                    str1 = str2 = str3 = str4 = default;
 01778                    return false;
 1779                }
 1780
 01781                str1 = Ascii.ExtractAsciiVector(s11, s31);
 01782                str2 = Ascii.ExtractAsciiVector(s12, s32);
 01783                str3 = Ascii.ExtractAsciiVector(s21, s41);
 01784                str4 = Ascii.ExtractAsciiVector(s22, s42);
 1785
 01786                return true;
 1787            }
 1788#endif // NET
 1789
 1790            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1791            public unsafe int DecodeFourElements(ushort* source, ref sbyte decodingMap)
 1792            {
 1793                // The 'source' span expected to have at least 4 elements, and the 'decodingMap' consists 256 sbytes
 3365421794                uint t0 = source[0];
 3365421795                uint t1 = source[1];
 3365421796                uint t2 = source[2];
 3365421797                uint t3 = source[3];
 1798
 3365421799                if (((t0 | t1 | t2 | t3) & 0xffffff00) != 0)
 1800                {
 47321801                    return -1; // One or more chars falls outside the 00..ff range, invalid Base64 character.
 1802                }
 1803
 3318101804                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 3318101805                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 3318101806                int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 3318101807                int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 1808
 3318101809                i0 <<= 18;
 3318101810                i1 <<= 12;
 3318101811                i2 <<= 6;
 1812
 3318101813                i0 |= i3;
 3318101814                i1 |= i2;
 1815
 3318101816                i0 |= i1;
 3318101817                return i0;
 1818            }
 1819
 1820            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1821            public unsafe int DecodeRemaining(ushort* srcEnd, ref sbyte decodingMap, long remaining, out uint t2, out ui
 1822            {
 1823                uint t0;
 1824                uint t1;
 315501825                t2 = EncodingPad;
 315501826                t3 = EncodingPad;
 1827                switch (remaining)
 1828                {
 1829                    case 2:
 01830                        t0 = srcEnd[-2];
 01831                        t1 = srcEnd[-1];
 01832                        break;
 1833                    case 3:
 01834                        t0 = srcEnd[-3];
 01835                        t1 = srcEnd[-2];
 01836                        t2 = srcEnd[-1];
 01837                        break;
 1838                    case 4:
 315501839                        t0 = srcEnd[-4];
 315501840                        t1 = srcEnd[-3];
 315501841                        t2 = srcEnd[-2];
 315501842                        t3 = srcEnd[-1];
 315501843                        break;
 1844                    default:
 01845                        return -1;
 1846                }
 1847
 315501848                if (((t0 | t1 | t2 | t3) & 0xffffff00) != 0)
 1849                {
 6641850                    return -1;
 1851                }
 1852
 308861853                int i0 = Unsafe.Add(ref decodingMap, (IntPtr)t0);
 308861854                int i1 = Unsafe.Add(ref decodingMap, (IntPtr)t1);
 1855
 308861856                i0 <<= 18;
 308861857                i1 <<= 12;
 1858
 308861859                i0 |= i1;
 308861860                return i0;
 1861            }
 1862
 1863            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1864            public int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<ushort> span)
 1865            {
 3711921866                for (int i = 0; i < span.Length; i++)
 1867                {
 1855821868                    if (!IsWhiteSpace(span[i]))
 1869                    {
 641841870                        return i;
 1871                    }
 1872                }
 1873
 141874                return -1;
 1875            }
 1876
 1877            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1878            public OperationStatus DecodeWithWhiteSpaceBlockwiseWrapper<TBase64Decoder>(TBase64Decoder decoder, ReadOnly
 1879                Span<byte> bytes, ref int bytesConsumed, ref int bytesWritten, bool isFinalBlock = true) where TBase64De
 37301880                DecodeWithWhiteSpaceBlockwise(default(Base64DecoderChar), source, bytes, ref bytesConsumed, ref bytesWri
 1881        }
 1882    }
 1883}
 1884

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64EncoderHelper.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics.CodeAnalysis;
 5using System.Runtime.CompilerServices;
 6using System.Runtime.InteropServices;
 7#if NET
 8using System.Runtime.Intrinsics;
 9using System.Runtime.Intrinsics.Arm;
 10using System.Runtime.Intrinsics.Wasm;
 11using System.Runtime.Intrinsics.X86;
 12#endif
 13
 14namespace System.Buffers.Text
 15{
 16    // AVX2 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arch/avx
 17    // Vector128 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arc
 18    internal static partial class Base64Helper
 19    {
 20        internal static unsafe OperationStatus EncodeTo<TBase64Encoder, T>(TBase64Encoder encoder, ReadOnlySpan<byte> so
 21            Span<T> destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true)
 22            where TBase64Encoder : IBase64Encoder<T>
 23            where T : unmanaged
 24        {
 25            if (source.IsEmpty)
 26            {
 027                bytesConsumed = 0;
 028                bytesWritten = 0;
 029                return OperationStatus.Done;
 30            }
 31
 11663832            fixed (byte* srcBytes = &MemoryMarshal.GetReference(source))
 11663833            fixed (T* destBytes = &MemoryMarshal.GetReference(destination))
 34            {
 11663835                int srcLength = source.Length;
 11663836                int destLength = destination.Length;
 11663837                int maxSrcLength = encoder.GetMaxSrcLength(srcLength, destLength);
 38
 11663839                byte* src = srcBytes;
 11663840                T* dest = destBytes;
 11663841                byte* srcEnd = srcBytes + (uint)srcLength;
 11663842                byte* srcMax = srcBytes + (uint)maxSrcLength;
 43
 44#if NET
 11663845                if (maxSrcLength >= 16)
 46                {
 10403847                    byte* end = srcMax - 64;
 10403848                    if (Vector512.IsHardwareAccelerated && Avx512Vbmi.IsSupported && (end >= src))
 49                    {
 2958850                        Avx512Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 51
 2958852                        if (src == srcEnd)
 53                            goto DoneExit;
 54                    }
 55
 10403856                    end = srcMax - 32;
 10403857                    if (Avx2.IsSupported && (end >= src))
 58                    {
 8467859                        Avx2Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 60
 8467861                        if (src == srcEnd)
 62                            goto DoneExit;
 63                    }
 64
 10403865                    end = srcMax - 48;
 66                    if (AdvSimd.Arm64.IsSupported && (end >= src))
 67                    {
 68                        AdvSimdEncode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 69
 70                        if (src == srcEnd)
 71                            goto DoneExit;
 72                    }
 73
 10403874                    end = srcMax - 16;
 10403875                    if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && BitConverter.IsLit
 76                    {
 2840677                        Vector128Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 78
 2840679                        if (src == srcEnd)
 80                            goto DoneExit;
 81                    }
 82                }
 83#endif
 11663884                ref byte encodingMap = ref MemoryMarshal.GetReference(encoder.EncodingMap);
 85
 11663886                srcMax -= 2;
 43928487                while (src < srcMax)
 88                {
 32264689                    encoder.EncodeThreeAndWrite(src, dest, ref encodingMap);
 32264690                    src += 3;
 32264691                    dest += 4;
 92                }
 93
 11663894                if (srcMax + 2 != srcEnd)
 95                    goto DestinationTooSmallExit;
 96
 11663897                if (!isFinalBlock)
 98                {
 753299                    if (src == srcEnd)
 2204100                        goto DoneExit;
 101
 102                    goto NeedMoreData;
 103                }
 104
 109106105                if (src + 1 == srcEnd)
 106                {
 20228107                    encoder.EncodeOneOptionallyPadTwo(src, dest, ref encodingMap);
 20228108                    src += 1;
 20228109                    dest += encoder.IncrementPadTwo;
 110                }
 88878111                else if (src + 2 == srcEnd)
 112                {
 14404113                    encoder.EncodeTwoOptionallyPadOne(src, dest, ref encodingMap);
 14404114                    src += 2;
 14404115                    dest += encoder.IncrementPadOne;
 116                }
 117
 118            DoneExit:
 111310119                bytesConsumed = (int)(src - srcBytes);
 111310120                bytesWritten = (int)(dest - destBytes);
 111310121                return OperationStatus.Done;
 122
 123            DestinationTooSmallExit:
 0124                bytesConsumed = (int)(src - srcBytes);
 0125                bytesWritten = (int)(dest - destBytes);
 0126                return OperationStatus.DestinationTooSmall;
 127
 128            NeedMoreData:
 5328129                bytesConsumed = (int)(src - srcBytes);
 5328130                bytesWritten = (int)(dest - destBytes);
 5328131                return OperationStatus.NeedMoreData;
 132            }
 133        }
 134
 135#if NET
 136        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 137        [CompExactlyDependsOn(typeof(Avx512BW))]
 138        [CompExactlyDependsOn(typeof(Avx512Vbmi))]
 139        private static unsafe void Avx512Encode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T* de
 140            where TBase64Encoder : IBase64Encoder<T>
 141            where T : unmanaged
 142        {
 143            // Reference for VBMI implementation : https://github.com/WojciechMula/base64simd/tree/master/encode
 144            // If we have AVX512 support, pick off 48 bytes at a time for as long as we can.
 145            // But because we read 64 bytes at a time, ensure we have enough room to do a
 146            // full 64-byte read without segfaulting.
 147
 29588148            byte* src = srcBytes;
 29588149            T* dest = destBytes;
 150
 151            // The JIT won't hoist these "constants", so help it
 29588152            Vector512<sbyte> shuffleVecVbmi = Vector512.Create(
 29588153                0x01020001, 0x04050304, 0x07080607, 0x0a0b090a,
 29588154                0x0d0e0c0d, 0x10110f10, 0x13141213, 0x16171516,
 29588155                0x191a1819, 0x1c1d1b1c, 0x1f201e1f, 0x22232122,
 29588156                0x25262425, 0x28292728, 0x2b2c2a2b, 0x2e2f2d2e).AsSByte();
 29588157            Vector512<sbyte> vbmiLookup = Vector512.Create(encoder.EncodingMap).AsSByte();
 158
 29588159            Vector512<ushort> maskAC = Vector512.Create((uint)0x0fc0fc00).AsUInt16();
 29588160            Vector512<uint> maskBB = Vector512.Create((uint)0x3f003f00);
 29588161            Vector512<ushort> shiftAC = Vector512.Create((uint)0x0006000a).AsUInt16();
 29588162            Vector512<ushort> shiftBB = Vector512.Create((uint)0x00080004).AsUInt16();
 163
 29588164            AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 165
 166            // This algorithm requires AVX512VBMI support.
 167            // Vbmi was first introduced in CannonLake and is available from IceLake on.
 168
 169            // str = [...|PONM|LKJI|HGFE|DCBA]
 29588170            Vector512<sbyte> str = Vector512.Load(src).AsSByte();
 171
 425464172            while (true)
 173            {
 174                // Step 1 : Split 48 bytes into 64 bytes with each byte using 6-bits from input
 175                // str = [...|KLJK|HIGH|EFDE|BCAB]
 455052176                str = Avx512Vbmi.PermuteVar64x8(str, shuffleVecVbmi);
 177
 178                // TO-DO- This can be achieved faster with multishift
 179                // Consider the first 4 bytes - BCAB
 180                // temp1    = [...|0000cccc|cc000000|aaaaaa00|00000000]
 455052181                Vector512<ushort> temp1 = (str.AsUInt16() & maskAC);
 182
 183                // temp2    = [...|00000000|00cccccc|00000000|00aaaaaa]
 455052184                Vector512<ushort> temp2 = Avx512BW.ShiftRightLogicalVariable(temp1, shiftAC).AsUInt16();
 185
 186                // temp3    = [...|ccdddddd|00000000|aabbbbbb|cccc0000]
 455052187                Vector512<ushort> temp3 = Avx512BW.ShiftLeftLogicalVariable(str.AsUInt16(), shiftBB).AsUInt16();
 188
 189                // str      = [...|00dddddd|00cccccc|00bbbbbb|00aaaaaa]
 455052190                str = Vector512.ConditionalSelect(maskBB, temp3.AsUInt32(), temp2.AsUInt32()).AsSByte();
 191
 192                // Step 2: Now we have the indices calculated. Next step is to use these indices to translate.
 455052193                str = Avx512Vbmi.PermuteVar64x8(vbmiLookup, str);
 194
 455052195                encoder.StoreVector512ToDestination(dest, destStart, destLength, str.AsByte());
 196
 455052197                src += 48;
 455052198                dest += 64;
 199
 455052200                if (src > srcEnd)
 201                    break;
 202
 425464203                AssertRead<Vector512<sbyte>>(src, srcStart, sourceLength);
 425464204                str = Vector512.Load(src).AsSByte();
 205            }
 206
 29588207            srcBytes = src;
 29588208            destBytes = dest;
 29588209        }
 210
 211        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 212        [CompExactlyDependsOn(typeof(Avx2))]
 213        private static unsafe void Avx2Encode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T* dest
 214            where TBase64Encoder : IBase64Encoder<T>
 215            where T : unmanaged
 216        {
 217            // If we have AVX2 support, pick off 24 bytes at a time for as long as we can.
 218            // But because we read 32 bytes at a time, ensure we have enough room to do a
 219            // full 32-byte read without segfaulting.
 220
 221            // translation from SSSE3 into AVX2 of procedure
 222            // This one works with shifted (4 bytes) input in order to
 223            // be able to work efficiently in the 2 128-bit lanes
 224
 225            // srcBytes, bytes MSB to LSB:
 226            // 0 0 0 0 x w v u t s r q p o n m
 227            // l k j i h g f e d c b a 0 0 0 0
 228
 229            // The JIT won't hoist these "constants", so help it
 84678230            Vector256<sbyte> shuffleVec = Vector256.Create(
 84678231                5, 4, 6, 5,
 84678232                8, 7, 9, 8,
 84678233                11, 10, 12, 11,
 84678234                14, 13, 15, 14,
 84678235                1, 0, 2, 1,
 84678236                4, 3, 5, 4,
 84678237                7, 6, 8, 7,
 84678238                10, 9, 11, 10);
 239
 84678240            Vector256<sbyte> lut = Vector256.Create(
 84678241                65, 71, -4, -4,
 84678242                -4, -4, -4, -4,
 84678243                -4, -4, -4, -4,
 84678244                encoder.Avx2LutChar62, encoder.Avx2LutChar63, 0, 0,
 84678245                65, 71, -4, -4,
 84678246                -4, -4, -4, -4,
 84678247                -4, -4, -4, -4,
 84678248                encoder.Avx2LutChar62, encoder.Avx2LutChar63, 0, 0);
 249
 84678250            Vector256<sbyte> maskAC = Vector256.Create(0x0fc0fc00).AsSByte();
 84678251            Vector256<sbyte> maskBB = Vector256.Create(0x003f03f0).AsSByte();
 84678252            Vector256<ushort> shiftAC = Vector256.Create(0x04000040).AsUInt16();
 84678253            Vector256<short> shiftBB = Vector256.Create(0x01000010).AsInt16();
 84678254            Vector256<byte> const51 = Vector256.Create((byte)51);
 84678255            Vector256<sbyte> const25 = Vector256.Create((sbyte)25);
 256
 84678257            byte* src = srcBytes;
 84678258            T* dest = destBytes;
 259
 260            // first load is done at c-0 not to get a segfault
 84678261            AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 84678262            Vector256<sbyte> str = Avx.LoadVector256(src).AsSByte();
 263
 264            // shift by 4 bytes, as required by Reshuffle
 84678265            str = Avx2.PermuteVar8x32(str.AsInt32(), Vector256.Create(
 84678266                0, 0, 0, 0,
 84678267                0, 0, 0, 0,
 84678268                1, 0, 0, 0,
 84678269                2, 0, 0, 0,
 84678270                3, 0, 0, 0,
 84678271                4, 0, 0, 0,
 84678272                5, 0, 0, 0,
 84678273                6, 0, 0, 0).AsInt32()).AsSByte();
 274
 275            // Next loads are done at src-4, as required by Reshuffle, so shift it once
 84678276            src -= 4;
 277
 68224278            while (true)
 279            {
 280                // Reshuffle
 152902281                str = Avx2.Shuffle(str, shuffleVec);
 282                // str, bytes MSB to LSB:
 283                // w x v w
 284                // t u s t
 285                // q r p q
 286                // n o m n
 287                // k l j k
 288                // h i g h
 289                // e f d e
 290                // b c a b
 291
 152902292                Vector256<sbyte> t0 = str & maskAC;
 293                // bits, upper case are most significant bits, lower case are least significant bits.
 294                // 0000wwww XX000000 VVVVVV00 00000000
 295                // 0000tttt UU000000 SSSSSS00 00000000
 296                // 0000qqqq RR000000 PPPPPP00 00000000
 297                // 0000nnnn OO000000 MMMMMM00 00000000
 298                // 0000kkkk LL000000 JJJJJJ00 00000000
 299                // 0000hhhh II000000 GGGGGG00 00000000
 300                // 0000eeee FF000000 DDDDDD00 00000000
 301                // 0000bbbb CC000000 AAAAAA00 00000000
 302
 152902303                Vector256<sbyte> t2 = str & maskBB;
 304                // 00000000 00xxxxxx 000000vv WWWW0000
 305                // 00000000 00uuuuuu 000000ss TTTT0000
 306                // 00000000 00rrrrrr 000000pp QQQQ0000
 307                // 00000000 00oooooo 000000mm NNNN0000
 308                // 00000000 00llllll 000000jj KKKK0000
 309                // 00000000 00iiiiii 000000gg HHHH0000
 310                // 00000000 00ffffff 000000dd EEEE0000
 311                // 00000000 00cccccc 000000aa BBBB0000
 312
 152902313                Vector256<ushort> t1 = Avx2.MultiplyHigh(t0.AsUInt16(), shiftAC);
 314                // 00000000 00wwwwXX 00000000 00VVVVVV
 315                // 00000000 00ttttUU 00000000 00SSSSSS
 316                // 00000000 00qqqqRR 00000000 00PPPPPP
 317                // 00000000 00nnnnOO 00000000 00MMMMMM
 318                // 00000000 00kkkkLL 00000000 00JJJJJJ
 319                // 00000000 00hhhhII 00000000 00GGGGGG
 320                // 00000000 00eeeeFF 00000000 00DDDDDD
 321                // 00000000 00bbbbCC 00000000 00AAAAAA
 322
 152902323                Vector256<short> t3 = t2.AsInt16() * shiftBB;
 324                // 00xxxxxx 00000000 00vvWWWW 00000000
 325                // 00uuuuuu 00000000 00ssTTTT 00000000
 326                // 00rrrrrr 00000000 00ppQQQQ 00000000
 327                // 00oooooo 00000000 00mmNNNN 00000000
 328                // 00llllll 00000000 00jjKKKK 00000000
 329                // 00iiiiii 00000000 00ggHHHH 00000000
 330                // 00ffffff 00000000 00ddEEEE 00000000
 331                // 00cccccc 00000000 00aaBBBB 00000000
 332
 152902333                str = t1.AsSByte() | t3.AsSByte();
 334                // 00xxxxxx 00wwwwXX 00vvWWWW 00VVVVVV
 335                // 00uuuuuu 00ttttUU 00ssTTTT 00SSSSSS
 336                // 00rrrrrr 00qqqqRR 00ppQQQQ 00PPPPPP
 337                // 00oooooo 00nnnnOO 00mmNNNN 00MMMMMM
 338                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 339                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 340                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 341                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 342
 343                // Translation
 344                // LUT contains Absolute offset for all ranges:
 345                // Translate values 0..63 to the Base64 alphabet. There are five sets:
 346                // #  From      To         Abs    Index  Characters
 347                // 0  [0..25]   [65..90]   +65        0  ABCDEFGHIJKLMNOPQRSTUVWXYZ
 348                // 1  [26..51]  [97..122]  +71        1  abcdefghijklmnopqrstuvwxyz
 349                // 2  [52..61]  [48..57]    -4  [2..11]  0123456789
 350                // 3  [62]      [43]       -19       12  +
 351                // 4  [63]      [47]       -16       13  /
 352
 353                // Create LUT indices from input:
 354                // the index for range #0 is right, others are 1 less than expected:
 152902355                Vector256<byte> indices = Avx2.SubtractSaturate(str.AsByte(), const51);
 356
 357                // mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0:
 152902358                Vector256<sbyte> mask = Avx2.CompareGreaterThan(str, const25);
 359
 360                // subtract -1, so add 1 to indices for range #[1..4], All indices are now correct:
 152902361                Vector256<sbyte> tmp = indices.AsSByte() - mask;
 362
 363                // Add offsets to input values:
 152902364                str += Avx2.Shuffle(lut, tmp);
 365
 152902366                encoder.StoreVector256ToDestination(dest, destStart, destLength, str.AsByte());
 367
 152902368                src += 24;
 152902369                dest += 32;
 370
 152902371                if (src > srcEnd)
 372                    break;
 373
 374                // Load at src-4, as required by Reshuffle (already shifted by -4)
 68224375                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 68224376                str = Avx.LoadVector256(src).AsSByte();
 377            }
 378
 84678379            srcBytes = src + 4;
 84678380            destBytes = dest;
 84678381        }
 382
 383        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 384        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 385        private static unsafe void AdvSimdEncode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T* d
 386            where TBase64Encoder : IBase64Encoder<T>
 387            where T : unmanaged
 388        {
 389            // C# implementation of https://github.com/aklomp/base64/blob/3a5add8652076612a8407627a42c768736a4263f/lib/a
 390            Vector128<byte> str1;
 391            Vector128<byte> str2;
 392            Vector128<byte> str3;
 393            Vector128<byte> res1;
 394            Vector128<byte> res2;
 395            Vector128<byte> res3;
 396            Vector128<byte> res4;
 397            Vector128<byte> tblEnc1 = Vector128.Create("ABCDEFGHIJKLMNOP"u8).AsByte();
 398            Vector128<byte> tblEnc2 = Vector128.Create("QRSTUVWXYZabcdef"u8).AsByte();
 399            Vector128<byte> tblEnc3 = Vector128.Create("ghijklmnopqrstuv"u8).AsByte();
 400            Vector128<byte> tblEnc4 = Vector128.Create(encoder.AdvSimdLut4).AsByte();
 401            byte* src = srcBytes;
 402            T* dest = destBytes;
 403
 404            // If we have Neon support, pick off 48 bytes at a time for as long as we can.
 405            do
 406            {
 407                // Load 48 bytes and deinterleave:
 408                AssertRead<Vector128<byte>>(src, srcStart, sourceLength);
 409                (str1, str2, str3) = AdvSimd.Arm64.Load3xVector128AndUnzip(src);
 410
 411                // Divide bits of three input bytes over four output bytes:
 412                res1 = str1 >>> 2;
 413                res2 = str2 >>> 4;
 414                res3 = str3 >>> 6;
 415                res2 = AdvSimd.ShiftLeftAndInsert(res2, str1, 4);
 416                res3 = AdvSimd.ShiftLeftAndInsert(res3, str2, 2);
 417
 418                // Clear top two bits:
 419                res2 &= AdvSimd.DuplicateToVector128((byte)0x3F);
 420                res3 &= AdvSimd.DuplicateToVector128((byte)0x3F);
 421                res4 = str3 & AdvSimd.DuplicateToVector128((byte)0x3F);
 422
 423                // The bits have now been shifted to the right locations;
 424                // translate their values 0..63 to the Base64 alphabet.
 425                // Use a 64-byte table lookup:
 426                res1 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res1);
 427                res2 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res2);
 428                res3 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res3);
 429                res4 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res4);
 430
 431                // Interleave and store result:
 432                encoder.StoreArmVector128x4ToDestination(dest, destStart, destLength, res1, res2, res3, res4);
 433
 434                src += 48;
 435                dest += 64;
 436            } while (src <= srcEnd);
 437
 438            srcBytes = src;
 439            destBytes = dest;
 440        }
 441
 442        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 443        [CompExactlyDependsOn(typeof(Ssse3))]
 444        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 445        [CompExactlyDependsOn(typeof(PackedSimd))]
 446        private static unsafe void Vector128Encode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T*
 447            where TBase64Encoder : IBase64Encoder<T>
 448            where T : unmanaged
 449        {
 450            // If we have SSSE3 support, pick off 12 bytes at a time for as long as we can.
 451            // But because we read 16 bytes at a time, ensure we have enough room to do a
 452            // full 16-byte read without segfaulting.
 453
 454            // srcBytes, bytes MSB to LSB:
 455            // 0 0 0 0 l k j i h g f e d c b a
 456
 457            // The JIT won't hoist these "constants", so help it
 458            Vector128<byte> shuffleVec = Vector128.Create(0x01020001, 0x04050304, 0x07080607, 0x0A0B090A).AsByte();
 28406459            Vector128<byte> lut = Vector128.Create(0xFCFC4741, 0xFCFCFCFC, 0xFCFCFCFC, encoder.Ssse3AdvSimdLutE3).AsByte
 28406460            Vector128<byte> maskAC = Vector128.Create(0x0fc0fc00).AsByte();
 28406461            Vector128<byte> maskBB = Vector128.Create(0x003f03f0).AsByte();
 28406462            Vector128<ushort> shiftAC = Vector128.Create(0x04000040).AsUInt16();
 28406463            Vector128<short> shiftBB = Vector128.Create(0x01000010).AsInt16();
 28406464            Vector128<byte> const51 = Vector128.Create((byte)51);
 28406465            Vector128<sbyte> const25 = Vector128.Create((sbyte)25);
 28406466            Vector128<byte> mask8F = Vector128.Create((byte)0x8F);
 467
 28406468            byte* src = srcBytes;
 28406469            T* dest = destBytes;
 470
 471            //while (remaining >= 16)
 472            do
 473            {
 32114474                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 32114475                Vector128<byte> str = Vector128.LoadUnsafe(ref *src);
 476
 477                // Reshuffle
 32114478                str = SimdShuffle(str, shuffleVec, mask8F);
 479                // str, bytes MSB to LSB:
 480                // k l j k
 481                // h i g h
 482                // e f d e
 483                // b c a b
 484
 32114485                Vector128<byte> t0 = str & maskAC;
 486                // bits, upper case are most significant bits, lower case are least significant bits
 487                // 0000kkkk LL000000 JJJJJJ00 00000000
 488                // 0000hhhh II000000 GGGGGG00 00000000
 489                // 0000eeee FF000000 DDDDDD00 00000000
 490                // 0000bbbb CC000000 AAAAAA00 00000000
 491
 32114492                Vector128<byte> t2 = str & maskBB;
 493                // 00000000 00llllll 000000jj KKKK0000
 494                // 00000000 00iiiiii 000000gg HHHH0000
 495                // 00000000 00ffffff 000000dd EEEE0000
 496                // 00000000 00cccccc 000000aa BBBB0000
 497
 498                Vector128<ushort> t1;
 32114499                if (Ssse3.IsSupported)
 500                {
 32114501                    t1 = Sse2.MultiplyHigh(t0.AsUInt16(), shiftAC);
 502                }
 503                else if (AdvSimd.Arm64.IsSupported)
 504                {
 505                    Vector128<ushort> odd = Vector128.ShiftRightLogical(AdvSimd.Arm64.UnzipOdd(t0.AsUInt16(), t0.AsUInt1
 506                    Vector128<ushort> even = Vector128.ShiftRightLogical(AdvSimd.Arm64.UnzipEven(t0.AsUInt16(), t0.AsUIn
 507                    t1 = AdvSimd.Arm64.ZipLow(even, odd);
 508                }
 509                else if (PackedSimd.IsSupported)
 510                {
 511                    // MultiplyHigh by {2^6, 2^10} is a right shift of the even u16 lanes by 10 and the odd lanes by 6.
 512                    Vector128<ushort> shr6 = Vector128.ShiftRightLogical(t0.AsUInt16(), 6);
 513                    Vector128<ushort> shr10 = Vector128.ShiftRightLogical(t0.AsUInt16(), 10);
 514                    t1 = Vector128.ConditionalSelect(Vector128.Create(0x0000FFFFu).AsUInt16(), shr10, shr6);
 515                }
 516                else
 517                {
 518                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 0519                    ThrowUnreachableException();
 520                    t1 = default;
 521                }
 522                // 00000000 00kkkkLL 00000000 00JJJJJJ
 523                // 00000000 00hhhhII 00000000 00GGGGGG
 524                // 00000000 00eeeeFF 00000000 00DDDDDD
 525                // 00000000 00bbbbCC 00000000 00AAAAAA
 526
 32114527                Vector128<short> t3 = t2.AsInt16() * shiftBB;
 528                // 00llllll 00000000 00jjKKKK 00000000
 529                // 00iiiiii 00000000 00ggHHHH 00000000
 530                // 00ffffff 00000000 00ddEEEE 00000000
 531                // 00cccccc 00000000 00aaBBBB 00000000
 532
 32114533                str = t1.AsByte() | t3.AsByte();
 534                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 535                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 536                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 537                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 538
 539                // Translation
 540                // LUT contains Absolute offset for all ranges:
 541                // Translate values 0..63 to the Base64 alphabet. There are five sets:
 542                // #  From      To         Abs    Index  Characters
 543                // 0  [0..25]   [65..90]   +65        0  ABCDEFGHIJKLMNOPQRSTUVWXYZ
 544                // 1  [26..51]  [97..122]  +71        1  abcdefghijklmnopqrstuvwxyz
 545                // 2  [52..61]  [48..57]    -4  [2..11]  0123456789
 546                // 3  [62]      [43]       -19       12  +
 547                // 4  [63]      [47]       -16       13  /
 548
 549                // Create LUT indices from input:
 550                // the index for range #0 is right, others are 1 less than expected:
 551                Vector128<byte> indices;
 32114552                if (Ssse3.IsSupported)
 553                {
 32114554                    indices = Sse2.SubtractSaturate(str.AsByte(), const51);
 555                }
 556                else if (AdvSimd.IsSupported)
 557                {
 558                    indices = AdvSimd.SubtractSaturate(str.AsByte(), const51);
 559                }
 560                else if (PackedSimd.IsSupported)
 561                {
 562                    indices = PackedSimd.SubtractSaturate(str.AsByte(), const51);
 563                }
 564                else
 565                {
 566                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 0567                    ThrowUnreachableException();
 568                    indices = default;
 569                }
 570
 571                // mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0:
 32114572                Vector128<sbyte> mask = Vector128.GreaterThan(str.AsSByte(), const25);
 573
 574                // subtract -1, so add 1 to indices for range #[1..4], All indices are now correct:
 32114575                Vector128<sbyte> tmp = indices.AsSByte() - mask;
 576
 577                // Add offsets to input values:
 32114578                str += SimdShuffle(lut, tmp.AsByte(), mask8F);
 579
 32114580                encoder.StoreVector128ToDestination(dest, destStart, destLength, str);
 581
 32114582                src += 12;
 32114583                dest += 16;
 584            }
 32114585            while (src <= srcEnd);
 586
 28406587            srcBytes = src;
 28406588            destBytes = dest;
 28406589        }
 590#endif
 591
 592        internal static unsafe OperationStatus EncodeToUtf8InPlace<TBase64Encoder>(TBase64Encoder encoder, Span<byte> bu
 593            where TBase64Encoder : IBase64Encoder<byte>
 594        {
 7532595            if (buffer.IsEmpty)
 596            {
 0597                bytesWritten = 0;
 0598                return OperationStatus.Done;
 599            }
 600
 7532601            fixed (byte* bufferBytes = &MemoryMarshal.GetReference(buffer))
 602            {
 7532603                int encodedLength = encoder.GetMaxEncodedLength(dataLength);
 7532604                if (buffer.Length < encodedLength)
 605                {
 0606                    bytesWritten = 0;
 0607                    return OperationStatus.DestinationTooSmall;
 608                }
 609
 7532610                int leftover = (int)((uint)dataLength % 3); // how many bytes after packs of 3
 611
 7532612                uint destinationIndex = encoder.GetInPlaceDestinationLength(encodedLength, leftover);
 7532613                uint sourceIndex = (uint)(dataLength - leftover);
 7532614                ref byte encodingMap = ref MemoryMarshal.GetReference(encoder.EncodingMap);
 615
 616                // encode last pack to avoid conditional in the main loop
 7532617                if (leftover != 0)
 618                {
 5328619                    if (leftover == 1)
 620                    {
 3112621                        encoder.EncodeOneOptionallyPadTwo(bufferBytes + sourceIndex, bufferBytes + destinationIndex, ref
 622                    }
 623                    else
 624                    {
 2216625                        encoder.EncodeTwoOptionallyPadOne(bufferBytes + sourceIndex, bufferBytes + destinationIndex, ref
 626                    }
 627
 5328628                    destinationIndex -= 4;
 629                }
 630
 7532631                sourceIndex -= 3;
 1201552632                while ((int)sourceIndex >= 0)
 633                {
 1194020634                    uint result = Encode(bufferBytes + sourceIndex, ref encodingMap);
 1194020635                    Unsafe.WriteUnaligned(bufferBytes + destinationIndex, result);
 1194020636                    destinationIndex -= 4;
 1194020637                    sourceIndex -= 3;
 638                }
 639
 7532640                bytesWritten = encodedLength;
 7532641                return OperationStatus.Done;
 642            }
 643        }
 644
 645        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 646        private static unsafe uint Encode(byte* threeBytes, ref byte encodingMap)
 647        {
 1241990648            uint t0 = threeBytes[0];
 1241990649            uint t1 = threeBytes[1];
 1241990650            uint t2 = threeBytes[2];
 651
 1241990652            uint i = (t0 << 16) | (t1 << 8) | t2;
 653
 1241990654            uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 1241990655            uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 1241990656            uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 1241990657            uint i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F));
 658
 1241990659            return ConstructResult(i0, i1, i2, i3);
 660        }
 661
 662        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 663        private static uint ConstructResult(uint i0, uint i1, uint i2, uint i3)
 664        {
 665            if (BitConverter.IsLittleEndian)
 666            {
 1257974667                return i0 | (i1 << 8) | (i2 << 16) | (i3 << 24);
 668            }
 669            else
 670            {
 671                return (i0 << 24) | (i1 << 16) | (i2 << 8) | i3;
 672            }
 673        }
 674
 675        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 676        public static unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, ushort* dest, ref byte encodingMap)
 677        {
 678            uint t0 = oneByte[0];
 679
 14004680            uint i = t0 << 8;
 681
 14004682            uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10));
 14004683            uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F));
 684
 685            uint result;
 686
 14004687            if (BitConverter.IsLittleEndian)
 688            {
 14004689                result = (i0 | (i1 << 16));
 690            }
 691            else
 692            {
 693                result = ((i0 << 16) | i1);
 694            }
 695
 14004696            Unsafe.WriteUnaligned(dest, result);
 14004697        }
 698
 699        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 700        public static unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, ushort* dest, ref byte encodingMap)
 701        {
 9972702            uint t0 = twoBytes[0];
 9972703            uint t1 = twoBytes[1];
 704
 9972705            uint i = (t0 << 16) | (t1 << 8);
 706
 9972707            ushort i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 9972708            ushort i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 9972709            ushort i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 710
 9972711            dest[0] = i0;
 9972712            dest[1] = i1;
 9972713            dest[2] = i2;
 9972714        }
 715
 716        internal const uint EncodingPad = '='; // '=', for padding
 717
 718        internal const int MaximumEncodeLength = (int.MaxValue / 4) * 3; // 1610612733
 719
 720        internal readonly struct Base64EncoderByte : IBase64Encoder<byte>
 721        {
 153759722            public ReadOnlySpan<byte> EncodingMap => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"u
 723
 169356724            public sbyte Avx2LutChar62 => -19;  // char '+' diff
 725
 169356726            public sbyte Avx2LutChar63 => -16;   // char '/' diff
 727
 0728            public ReadOnlySpan<byte> AdvSimdLut4 => "wxyz0123456789+/"u8;
 729
 28406730            public uint Ssse3AdvSimdLutE3 => 0x0000F0ED;
 731
 20228732            public int IncrementPadTwo => 4;
 733
 14404734            public int IncrementPadOne => 4;
 735
 736            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 737            public int GetMaxSrcLength(int srcLength, int destLength) =>
 116638738                srcLength <= MaximumEncodeLength && destLength >= Base64.GetMaxEncodedToUtf8Length(srcLength) ?
 116638739                srcLength : (destLength >> 2) * 3;
 740
 7532741            public uint GetInPlaceDestinationLength(int encodedLength, int _) => (uint)(encodedLength - 4);
 742
 7532743            public int GetMaxEncodedLength(int srcLength) => Base64.GetMaxEncodedToUtf8Length(srcLength);
 744
 745            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 746            public unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, byte* dest, ref byte encodingMap)
 747            {
 9336748                uint t0 = oneByte[0];
 749
 9336750                uint i = t0 << 8;
 751
 9336752                uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10));
 9336753                uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F));
 754
 9336755                uint result = ConstructResult(i0, i1, EncodingPad, EncodingPad);
 9336756                Unsafe.WriteUnaligned(dest, result);
 9336757            }
 758
 759            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 760            public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, byte* dest, ref byte encodingMap)
 761            {
 6648762                uint t0 = twoBytes[0];
 6648763                uint t1 = twoBytes[1];
 764
 6648765                uint i = (t0 << 16) | (t1 << 8);
 766
 6648767                uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 6648768                uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 6648769                uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 770
 6648771                uint result = ConstructResult(i0, i1, i2, EncodingPad);
 6648772                Unsafe.WriteUnaligned(dest, result);
 6648773            }
 774
 775#if NET
 776            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 777            public unsafe void StoreVector512ToDestination(byte* dest, byte* destStart, int destLength, Vector512<byte> 
 778            {
 175020779                AssertWrite<Vector512<sbyte>>(dest, destStart, destLength);
 175020780                str.Store(dest);
 175020781            }
 782
 783            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 784            [CompExactlyDependsOn(typeof(Avx2))]
 785            public unsafe void StoreVector256ToDestination(byte* dest, byte* destStart, int destLength, Vector256<byte> 
 786            {
 11550787                AssertWrite<Vector256<sbyte>>(dest, destStart, destLength);
 11550788                Avx.Store(dest, str.AsByte());
 11550789            }
 790
 791            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 792            public unsafe void StoreVector128ToDestination(byte* dest, byte* destStart, int destLength, Vector128<byte> 
 793            {
 11090794                AssertWrite<Vector128<sbyte>>(dest, destStart, destLength);
 11090795                str.Store(dest);
 11090796            }
 797
 798            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 799            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 800            public unsafe void StoreArmVector128x4ToDestination(byte* dest, byte* destStart, int destLength,
 801                Vector128<byte> res1, Vector128<byte> res2, Vector128<byte> res3, Vector128<byte> res4)
 802            {
 0803                AssertWrite<Vector128<byte>>(dest, destStart, destLength);
 0804                AdvSimd.Arm64.StoreVectorAndZip(dest, (res1, res2, res3, res4));
 0805            }
 806#endif // NET
 807
 808            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 809            public unsafe void EncodeThreeAndWrite(byte* threeBytes, byte* destination, ref byte encodingMap)
 810            {
 47970811                uint result = Encode(threeBytes, ref encodingMap);
 47970812                Unsafe.WriteUnaligned(destination, result);
 47970813            }
 814        }
 815
 816        internal readonly struct Base64EncoderChar : IBase64Encoder<ushort>
 817        {
 116016818            public ReadOnlySpan<byte> EncodingMap => default(Base64EncoderByte).EncodingMap;
 819
 152096820            public sbyte Avx2LutChar62 => default(Base64EncoderByte).Avx2LutChar62;
 821
 152096822            public sbyte Avx2LutChar63 => default(Base64EncoderByte).Avx2LutChar63;
 823
 0824            public ReadOnlySpan<byte> AdvSimdLut4 => default(Base64EncoderByte).AdvSimdLut4;
 825
 18616826            public uint Ssse3AdvSimdLutE3 => default(Base64EncoderByte).Ssse3AdvSimdLutE3;
 827
 14004828            public int IncrementPadTwo => default(Base64EncoderByte).IncrementPadTwo;
 829
 9972830            public int IncrementPadOne => default(Base64EncoderByte).IncrementPadOne;
 831
 832            public int GetMaxSrcLength(int srcLength, int destLength) =>
 97808833                default(Base64EncoderByte).GetMaxSrcLength(srcLength, destLength);
 834
 0835            public uint GetInPlaceDestinationLength(int encodedLength, int _) => 0; // not used for char encoding
 836
 0837            public int GetMaxEncodedLength(int _) => 0;  // not used for char encoding
 838
 839            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 840            public unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, ushort* dest, ref byte encodingMap)
 841            {
 14004842                Base64Helper.EncodeOneOptionallyPadTwo(oneByte, dest, ref encodingMap);
 14004843                dest[2] = (ushort)EncodingPad;
 14004844                dest[3] = (ushort)EncodingPad;
 14004845            }
 846
 847            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 848            public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, ushort* dest, ref byte encodingMap)
 849            {
 9972850                Base64Helper.EncodeTwoOptionallyPadOne(twoBytes, dest, ref encodingMap);
 9972851                dest[3] = (ushort)EncodingPad;
 9972852            }
 853
 854#if NET
 855            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 856            public unsafe void StoreVector512ToDestination(ushort* dest, ushort* destStart, int destLength, Vector512<by
 857            {
 280032858                AssertWrite<Vector512<short>>(dest, destStart, destLength);
 280032859                (Vector512<ushort> utf16LowVector, Vector512<ushort> utf16HighVector) = Vector512.Widen(str);
 280032860                utf16LowVector.Store(dest);
 280032861                utf16HighVector.Store(dest + 32);
 280032862            }
 863
 864            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 865            public unsafe void StoreVector256ToDestination(ushort* dest, ushort* destStart, int destLength, Vector256<by
 866            {
 141352867                AssertWrite<Vector256<short>>(dest, destStart, destLength);
 141352868                (Vector256<ushort> utf16LowVector, Vector256<ushort> utf16HighVector) = Vector256.Widen(str);
 141352869                utf16LowVector.Store(dest);
 141352870                utf16HighVector.Store(dest + 16);
 141352871            }
 872
 873            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 874            public unsafe void StoreVector128ToDestination(ushort* dest, ushort* destStart, int destLength, Vector128<by
 875            {
 21024876                AssertWrite<Vector128<short>>(dest, destStart, destLength);
 21024877                (Vector128<ushort> utf16LowVector, Vector128<ushort> utf16HighVector) = Vector128.Widen(str);
 21024878                utf16LowVector.Store(dest);
 21024879                utf16HighVector.Store(dest + 8);
 21024880            }
 881
 882            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 883            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 884            public unsafe void StoreArmVector128x4ToDestination(ushort* dest, ushort* destStart, int destLength,
 885                Vector128<byte> res1, Vector128<byte> res2, Vector128<byte> res3, Vector128<byte> res4)
 886            {
 0887                AssertWrite<Vector128<short>>(dest, destStart, destLength);
 0888                (Vector128<ushort> utf16LowVector1, Vector128<ushort> utf16HighVector1) = Vector128.Widen(res1);
 0889                (Vector128<ushort> utf16LowVector2, Vector128<ushort> utf16HighVector2) = Vector128.Widen(res2);
 0890                (Vector128<ushort> utf16LowVector3, Vector128<ushort> utf16HighVector3) = Vector128.Widen(res3);
 0891                (Vector128<ushort> utf16LowVector4, Vector128<ushort> utf16HighVector4) = Vector128.Widen(res4);
 0892                AdvSimd.Arm64.StoreVectorAndZip(dest, (utf16LowVector1, utf16LowVector2, utf16LowVector3, utf16LowVector
 0893                AdvSimd.Arm64.StoreVectorAndZip(dest + 32, (utf16HighVector1, utf16HighVector2, utf16HighVector3, utf16H
 0894            }
 895#endif // NET
 896
 897            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 898            public unsafe void EncodeThreeAndWrite(byte* threeBytes, ushort* destination, ref byte encodingMap)
 899            {
 900                uint t0 = threeBytes[0];
 274676901                uint t1 = threeBytes[1];
 274676902                uint t2 = threeBytes[2];
 903
 274676904                uint i = (t0 << 16) | (t1 << 8) | t2;
 905
 274676906                ulong i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 274676907                ulong i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 274676908                ulong i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 274676909                ulong i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F));
 910
 911                ulong result;
 274676912                if (BitConverter.IsLittleEndian)
 913                {
 274676914                    result = i0 | (i1 << 16) | (i2 << 32) | (i3 << 48);
 915                }
 916                else
 917                {
 918                    result = (i0 << 48) | (i1 << 32) | (i2 << 16) | i3;
 919                }
 920
 274676921                Unsafe.WriteUnaligned(destination, result);
 274676922            }
 923        }
 924    }
 925}
 926

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64Helper.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Diagnostics.CodeAnalysis;
 6using System.Runtime.CompilerServices;
 7#if NET
 8using System.Runtime.Intrinsics;
 9#endif
 10
 11namespace System.Buffers.Text
 12{
 13    internal static partial class Base64Helper
 14    {
 15        [Conditional("DEBUG")]
 16        internal static unsafe void AssertRead<TVector>(byte* src, byte* srcStart, int srcLength)
 17        {
 98397418            int vectorElements = sizeof(TVector);
 98397419            byte* readEnd = src + vectorElements;
 98397420            byte* srcEnd = srcStart + srcLength;
 21
 98397422            if (readEnd > srcEnd)
 23            {
 024                int srcIndex = (int)(src - srcStart);
 025                Debug.Fail($"Read for {typeof(TVector)} is not within safe bounds. srcIndex: {srcIndex}, srcLength: {src
 26            }
 98397427        }
 28
 29        [Conditional("DEBUG")]
 30        internal static unsafe void AssertWrite<TVector>(byte* dest, byte* destStart, int destLength)
 31        {
 79414632            int vectorElements = sizeof(TVector);
 79414633            byte* writeEnd = dest + vectorElements;
 79414634            byte* destEnd = destStart + destLength;
 35
 79414636            if (writeEnd > destEnd)
 37            {
 038                int destIndex = (int)(dest - destStart);
 039                Debug.Fail($"Write for {typeof(TVector)} is not within safe bounds. destIndex: {destIndex}, destLength: 
 40            }
 79414641        }
 42
 43        [Conditional("DEBUG")]
 44        internal static unsafe void AssertRead<TVector>(ushort* src, ushort* srcStart, int srcLength)
 45        {
 52627846            int vectorElements = sizeof(TVector);
 52627847            ushort* readEnd = src + vectorElements;
 52627848            ushort* srcEnd = srcStart + srcLength;
 49
 52627850            if (readEnd > srcEnd)
 51            {
 052                int srcIndex = (int)(src - srcStart);
 053                Debug.Fail($"Read for {typeof(TVector)} is not within safe bounds. srcIndex: {srcIndex}, srcLength: {src
 54            }
 52627855        }
 56
 57        [Conditional("DEBUG")]
 58        internal static unsafe void AssertWrite<TVector>(ushort* dest, ushort* destStart, int destLength)
 59        {
 44240860            int vectorElements = sizeof(TVector);
 44240861            ushort* writeEnd = dest + vectorElements;
 44240862            ushort* destEnd = destStart + destLength;
 63
 44240864            if (writeEnd > destEnd)
 65            {
 066                int destIndex = (int)(dest - destStart);
 067                Debug.Fail($"Write for {typeof(TVector)} is not within safe bounds. destIndex: {destIndex}, destLength: 
 68            }
 44240869        }
 70
 71        [DoesNotReturn]
 72        internal static void ThrowUnreachableException()
 73        {
 74#if NET
 075            throw new UnreachableException();
 76#else
 77            throw new Exception("Unreachable");
 78#endif
 79        }
 80
 81        internal interface IBase64Encoder<T> where T : unmanaged
 82        {
 83            ReadOnlySpan<byte> EncodingMap { get; }
 84            sbyte Avx2LutChar62 { get; }
 85            sbyte Avx2LutChar63 { get; }
 86            ReadOnlySpan<byte> AdvSimdLut4 { get; }
 87            uint Ssse3AdvSimdLutE3 { get; }
 88            int GetMaxSrcLength(int srcLength, int destLength);
 89            int GetMaxEncodedLength(int srcLength);
 90            uint GetInPlaceDestinationLength(int encodedLength, int leftOver);
 91            unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, T* dest, ref byte encodingMap);
 92            unsafe void EncodeTwoOptionallyPadOne(byte* oneByte, T* dest, ref byte encodingMap);
 93            unsafe void EncodeThreeAndWrite(byte* threeBytes, T* destination, ref byte encodingMap);
 94            int IncrementPadTwo { get; }
 95            int IncrementPadOne { get; }
 96#if NET
 97            unsafe void StoreVector512ToDestination(T* dest, T* destStart, int destLength, Vector512<byte> str);
 98            unsafe void StoreVector256ToDestination(T* dest, T* destStart, int destLength, Vector256<byte> str);
 99            unsafe void StoreVector128ToDestination(T* dest, T* destStart, int destLength, Vector128<byte> str);
 100            unsafe void StoreArmVector128x4ToDestination(T* dest, T* destStart, int destLength, Vector128<byte> res1,
 101                Vector128<byte> res2, Vector128<byte> res3, Vector128<byte> res4);
 102#endif // NET
 103        }
 104
 105        internal interface IBase64Decoder<T> where T : unmanaged
 106        {
 107            ReadOnlySpan<sbyte> DecodingMap { get; }
 108            ReadOnlySpan<uint> VbmiLookup0 { get; }
 109            ReadOnlySpan<uint> VbmiLookup1 { get; }
 110            ReadOnlySpan<sbyte> Avx2LutHigh { get; }
 111            ReadOnlySpan<sbyte> Avx2LutLow { get; }
 112            ReadOnlySpan<sbyte> Avx2LutShift { get; }
 113            byte MaskSlashOrUnderscore { get; }
 114            ReadOnlySpan<int> Vector128LutHigh { get; }
 115            ReadOnlySpan<int> Vector128LutLow { get; }
 116            ReadOnlySpan<uint> Vector128LutShift { get; }
 117            ReadOnlySpan<uint> AdvSimdLutOne3 { get; }
 118            uint AdvSimdLutTwo3Uint1 { get; }
 119            int SrcLength(bool isFinalBlock, int sourceLength);
 120            int GetMaxDecodedLength(int sourceLength);
 121            bool IsInvalidLength(int bufferLength);
 122            bool IsValidPadding(uint padChar);
 123#if NET
 124            bool TryDecode128Core(
 125                Vector128<byte> str,
 126                Vector128<byte> hiNibbles,
 127                Vector128<byte> maskSlashOrUnderscore,
 128                Vector128<byte> mask8F,
 129                Vector128<byte> lutLow,
 130                Vector128<byte> lutHigh,
 131                Vector128<sbyte> lutShift,
 132                Vector128<byte> shiftForUnderscore,
 133                out Vector128<byte> result);
 134            bool TryDecode256Core(
 135                Vector256<sbyte> str,
 136                Vector256<sbyte> hiNibbles,
 137                Vector256<sbyte> maskSlashOrUnderscore,
 138                Vector256<sbyte> lutLow,
 139                Vector256<sbyte> lutHigh,
 140                Vector256<sbyte> lutShift,
 141                Vector256<sbyte> shiftForUnderscore,
 142                out Vector256<sbyte> result);
 143            unsafe bool TryLoadVector512(T* src, T* srcStart, int sourceLength, out Vector512<sbyte> str);
 144            unsafe bool TryLoadAvxVector256(T* src, T* srcStart, int sourceLength, out Vector256<sbyte> str);
 145            unsafe bool TryLoadVector128(T* src, T* srcStart, int sourceLength, out Vector128<byte> str);
 146            unsafe bool TryLoadArmVector128x4(T* src, T* srcStart, int sourceLength,
 147                out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> str4);
 148#endif // NET
 149            unsafe int DecodeFourElements(T* source, ref sbyte decodingMap);
 150            unsafe int DecodeRemaining(T* srcEnd, ref sbyte decodingMap, long remaining, out uint t2, out uint t3);
 151            int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<T> span);
 152            OperationStatus DecodeWithWhiteSpaceBlockwiseWrapper<TTBase64Decoder>(TTBase64Decoder decoder, ReadOnlySpan<
 153                Span<byte> bytes, ref int bytesConsumed, ref int bytesWritten, bool isFinalBlock = true)
 154                where TTBase64Decoder : IBase64Decoder<T>;
 155        }
 156    }
 157}
 158

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Helper/Base64ValidatorHelper.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Runtime.CompilerServices;
 5
 6namespace System.Buffers.Text
 7{
 8    internal static partial class Base64Helper
 9    {
 10        internal static bool IsValid<T, TBase64Validatable>(TBase64Validatable validatable, ReadOnlySpan<T> base64Text, 
 11            where TBase64Validatable : IBase64Validatable<T>
 12            where T : struct
 13        {
 753214            int length = 0, paddingCount = 0;
 376615            T lastChar = default;
 16
 376617            if (!base64Text.IsEmpty)
 18            {
 19#if NET
 3868420                while (!base64Text.IsEmpty)
 21                {
 3856222                    int index = validatable.IndexOfAnyExcept(base64Text);
 3856223                    if ((uint)index >= (uint)base64Text.Length)
 24                    {
 17625                        length += base64Text.Length;
 17626                        lastChar = base64Text[base64Text.Length - 1];
 17627                        break;
 28                    }
 29
 3838630                    length += index;
 3838631                    if (index != 0)
 32                    {
 3512033                        lastChar = base64Text[index - 1];
 34                    }
 35
 3838636                    T charToValidate = base64Text[index];
 3838637                    base64Text = base64Text.Slice(index + 1);
 38
 3838639                    if (validatable.IsWhiteSpace(charToValidate))
 40                    {
 41                        // It's common if there's whitespace for there to be multiple whitespace characters in a row,
 42                        // e.g. \r\n.  Optimize for that case by looping here.
 4273043                        while (!base64Text.IsEmpty && validatable.IsWhiteSpace(base64Text[0]))
 44                        {
 781245                            base64Text = base64Text.Slice(1);
 46                        }
 3479647                        continue;
 48                    }
 49
 346850                    if (!validatable.IsEncodingPad(charToValidate))
 51                    {
 52                        // Invalid char was found.
 53                        goto Fail;
 54                    }
 55
 56                    // Encoding pad found. Determine if padding is valid, then stop processing.
 42857                    paddingCount = 1;
 867058                    foreach (T charToValidateInPadding in base64Text)
 59                    {
 60#else
 61                for (int i = 0; i < base64Text.Length; i++)
 62                {
 63                    T charToValidate = base64Text[i];
 64                    int value = validatable.DecodeValue(charToValidate);
 65                    if (value == -2)
 66                    {
 67                        // Not an Ascii char
 68                        goto Fail;
 69                    }
 70
 71                    if (value >= 0) // valid char
 72                    {
 73                        length++;
 74                        lastChar = charToValidate;
 75                        continue;
 76                    }
 77                    if (validatable.IsWhiteSpace(charToValidate))
 78                    {
 79                        continue;
 80                    }
 81
 82                    if (!validatable.IsEncodingPad(charToValidate))
 83                    {
 84                        // Invalid char was found.
 85                        goto Fail;
 86                    }
 87
 88                    // Encoding pad found. Determine if padding is valid, then stop processing.
 89                    paddingCount = 1;
 90                    for (i++; i < base64Text.Length; i++)
 91                    {
 92                        T charToValidateInPadding = base64Text[i];
 93#endif
 405094                        if (validatable.IsEncodingPad(charToValidateInPadding))
 95                        {
 96                            // There can be at most 2 padding chars.
 10097                            if (paddingCount >= 2)
 98                            {
 99                                goto Fail;
 100                            }
 101
 90102                            paddingCount++;
 103                        }
 3950104                        else if (!validatable.IsWhiteSpace(charToValidateInPadding))
 105                        {
 106                            // Invalid char was found.
 107                            goto Fail;
 108                        }
 109                    }
 110
 142111                    length += paddingCount;
 142112                    break;
 113                }
 114
 440115                if (!validatable.ValidateAndDecodeLength(lastChar, length, paddingCount, out decodedLength))
 116                {
 117                    goto Fail;
 118                }
 119
 180120                return true;
 121            }
 122
 0123            decodedLength = 0;
 0124            return true;
 125
 126        Fail:
 3586127            decodedLength = 0;
 3586128            return false;
 129        }
 130
 131        internal interface IBase64Validatable<T>
 132        {
 133#if NET
 134            int IndexOfAnyExcept(ReadOnlySpan<T> span);
 135#else
 136            int DecodeValue(T value);
 137#endif
 138            bool IsWhiteSpace(T value);
 139            bool IsEncodingPad(T value);
 140            bool ValidateAndDecodeLength(T lastChar, int length, int paddingCount, out int decodedLength);
 141        }
 142
 143        internal readonly struct Base64CharValidatable : IBase64Validatable<char>
 144        {
 145#if NET
 0146            private static readonly SearchValues<char> s_validBase64Chars = SearchValues.Create("ABCDEFGHIJKLMNOPQRSTUVW
 147
 0148            public int IndexOfAnyExcept(ReadOnlySpan<char> span) => span.IndexOfAnyExcept(s_validBase64Chars);
 149#else
 150            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 151            public int DecodeValue(char value)
 152            {
 153                if (value > byte.MaxValue)
 154                {
 155                    // Invalid char was found.
 156                    return -2;
 157                }
 158
 159                return default(Base64DecoderByte).DecodingMap[value];
 160            }
 161#endif
 0162            public bool IsWhiteSpace(char value) => Base64Helper.IsWhiteSpace(value);
 0163            public bool IsEncodingPad(char value) => value == EncodingPad;
 164            public bool ValidateAndDecodeLength(char lastChar, int length, int paddingCount, out int decodedLength) =>
 0165                default(Base64ByteValidatable).ValidateAndDecodeLength((byte)lastChar, length, paddingCount, out decoded
 166        }
 167
 168        internal readonly struct Base64ByteValidatable : IBase64Validatable<byte>
 169        {
 170#if NET
 1171            private static readonly SearchValues<byte> s_validBase64Chars = SearchValues.Create(default(Base64EncoderByt
 172
 38562173            public int IndexOfAnyExcept(ReadOnlySpan<byte> span) => span.IndexOfAnyExcept(s_validBase64Chars);
 174#else
 175            public int DecodeValue(byte value) => default(Base64DecoderByte).DecodingMap[value];
 176#endif
 84944177            public bool IsWhiteSpace(byte value) => Base64Helper.IsWhiteSpace(value);
 7518178            public bool IsEncodingPad(byte value) => value == EncodingPad;
 179            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 180            public bool ValidateAndDecodeLength(byte lastChar, int length, int paddingCount, out int decodedLength)
 181            {
 440182                if (length % 4 == 0)
 183                {
 200184                    int decoded = default(Base64DecoderByte).DecodingMap[lastChar];
 200185                    if ((paddingCount == 1 && (decoded & 0x03) != 0) ||
 200186                        (paddingCount == 2 && (decoded & 0x0F) != 0))
 187                    {
 188                        // unused lower bits are not 0, reject input
 20189                        decodedLength = 0;
 20190                        return false;
 191                    }
 192
 193                    // Remove padding to get exact length.
 180194                    decodedLength = (int)((uint)length / 4 * 3) - paddingCount;
 180195                    return true;
 196                }
 197
 240198                decodedLength = 0;
 240199                return false;
 200            }
 201        }
 202    }
 203}
 204

Methods/Properties

DecodeFrom(TBase64Decoder,System.ReadOnlySpan`1<T>,System.Span`1<System.Byte>,System.Int32&,System.Int32&,System.Boolean,System.Boolean)
InvalidDataFallback(TBase64Decoder,System.ReadOnlySpan`1<T>,System.Span`1<System.Byte>,System.Int32&,System.Int32&,System.Boolean)
DecodeFromUtf8InPlace(TBase64Decoder,System.Span`1<System.Byte>,System.Int32&,System.Boolean)
DecodeWithWhiteSpaceBlockwise(TBase64Decoder,System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Byte>,System.Int32&,System.Int32&,System.Boolean)
DecodeWithWhiteSpaceBlockwise(TBase64Decoder,System.ReadOnlySpan`1<System.UInt16>,System.Span`1<System.Byte>,System.Int32&,System.Int32&,System.Boolean)
GetPaddingCount(TBase64Decoder,System.Byte&)
GetPaddingCount(TBase64Decoder,System.UInt16&)
DecodeWithWhiteSpaceFromUtf8InPlace(TBase64Decoder,System.Span`1<System.Byte>,System.Int32&,System.UInt32)
Avx512Decode(TBase64Decoder,T*&,System.Byte*&,T*,System.Int32,System.Int32,T*,System.Byte*)
Avx2Decode(TBase64Decoder,T*&,System.Byte*&,T*,System.Int32,System.Int32,T*,System.Byte*)
SimdShuffle(System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>)
Vector128Decode(TBase64Decoder,T*&,System.Byte*&,T*,System.Int32,System.Int32,T*,System.Byte*)
WriteThreeLowOrderBytes(System.Byte*,System.Int32)
IsWhiteSpace(System.Int32)
DecodingMap()
VbmiLookup0()
VbmiLookup1()
Avx2LutHigh()
Avx2LutLow()
Avx2LutShift()
MaskSlashOrUnderscore()
Vector128LutHigh()
Vector128LutLow()
Vector128LutShift()
AdvSimdLutOne3()
AdvSimdLutTwo3Uint1()
GetMaxDecodedLength(System.Int32)
IsInvalidLength(System.Int32)
IsValidPadding(System.UInt32)
SrcLength(System.Boolean,System.Int32)
TryDecode128Core(System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.SByte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>&)
TryDecode256Core(System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>&)
TryLoadVector512(System.Byte*,System.Byte*,System.Int32,System.Runtime.Intrinsics.Vector512`1<System.SByte>&)
TryLoadAvxVector256(System.Byte*,System.Byte*,System.Int32,System.Runtime.Intrinsics.Vector256`1<System.SByte>&)
TryLoadVector128(System.Byte*,System.Byte*,System.Int32,System.Runtime.Intrinsics.Vector128`1<System.Byte>&)
TryLoadArmVector128x4(System.Byte*,System.Byte*,System.Int32,System.Runtime.Intrinsics.Vector128`1<System.Byte>&,System.Runtime.Intrinsics.Vector128`1<System.Byte>&,System.Runtime.Intrinsics.Vector128`1<System.Byte>&,System.Runtime.Intrinsics.Vector128`1<System.Byte>&)
DecodeFourElements(System.Byte*,System.SByte&)
DecodeRemaining(System.Byte*,System.SByte&,System.Int64,System.UInt32&,System.UInt32&)
IndexOfAnyExceptWhiteSpace(System.ReadOnlySpan`1<System.Byte>)
DecodeWithWhiteSpaceBlockwiseWrapper(TBase64Decoder,System.ReadOnlySpan`1<System.Byte>,System.Span`1<System.Byte>,System.Int32&,System.Int32&,System.Boolean)
DecodingMap()
VbmiLookup0()
VbmiLookup1()
Avx2LutHigh()
Avx2LutLow()
Avx2LutShift()
MaskSlashOrUnderscore()
Vector128LutHigh()
Vector128LutLow()
Vector128LutShift()
AdvSimdLutOne3()
AdvSimdLutTwo3Uint1()
GetMaxDecodedLength(System.Int32)
IsInvalidLength(System.Int32)
IsValidPadding(System.UInt32)
SrcLength(System.Boolean,System.Int32)
TryDecode128Core(System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.SByte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>&)
TryDecode256Core(System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>,System.Runtime.Intrinsics.Vector256`1<System.SByte>&)
TryLoadVector512(System.UInt16*,System.UInt16*,System.Int32,System.Runtime.Intrinsics.Vector512`1<System.SByte>&)
TryLoadAvxVector256(System.UInt16*,System.UInt16*,System.Int32,System.Runtime.Intrinsics.Vector256`1<System.SByte>&)
TryLoadVector128(System.UInt16*,System.UInt16*,System.Int32,System.Runtime.Intrinsics.Vector128`1<System.Byte>&)
TryLoadArmVector128x4(System.UInt16*,System.UInt16*,System.Int32,System.Runtime.Intrinsics.Vector128`1<System.Byte>&,System.Runtime.Intrinsics.Vector128`1<System.Byte>&,System.Runtime.Intrinsics.Vector128`1<System.Byte>&,System.Runtime.Intrinsics.Vector128`1<System.Byte>&)
DecodeFourElements(System.UInt16*,System.SByte&)
DecodeRemaining(System.UInt16*,System.SByte&,System.Int64,System.UInt32&,System.UInt32&)
IndexOfAnyExceptWhiteSpace(System.ReadOnlySpan`1<System.UInt16>)
DecodeWithWhiteSpaceBlockwiseWrapper(TBase64Decoder,System.ReadOnlySpan`1<System.UInt16>,System.Span`1<System.Byte>,System.Int32&,System.Int32&,System.Boolean)
EncodeTo(TBase64Encoder,System.ReadOnlySpan`1<System.Byte>,System.Span`1<T>,System.Int32&,System.Int32&,System.Boolean)
Avx512Encode(TBase64Encoder,System.Byte*&,T*&,System.Byte*,System.Int32,System.Int32,System.Byte*,T*)
Avx2Encode(TBase64Encoder,System.Byte*&,T*&,System.Byte*,System.Int32,System.Int32,System.Byte*,T*)
Vector128Encode(TBase64Encoder,System.Byte*&,T*&,System.Byte*,System.Int32,System.Int32,System.Byte*,T*)
EncodeToUtf8InPlace(TBase64Encoder,System.Span`1<System.Byte>,System.Int32,System.Int32&)
Encode(System.Byte*,System.Byte&)
ConstructResult(System.UInt32,System.UInt32,System.UInt32,System.UInt32)
EncodeOneOptionallyPadTwo(System.Byte*,System.UInt16*,System.Byte&)
EncodeTwoOptionallyPadOne(System.Byte*,System.UInt16*,System.Byte&)
EncodingMap()
Avx2LutChar62()
Avx2LutChar63()
AdvSimdLut4()
Ssse3AdvSimdLutE3()
IncrementPadTwo()
IncrementPadOne()
GetMaxSrcLength(System.Int32,System.Int32)
GetInPlaceDestinationLength(System.Int32,System.Int32)
GetMaxEncodedLength(System.Int32)
EncodeOneOptionallyPadTwo(System.Byte*,System.Byte*,System.Byte&)
EncodeTwoOptionallyPadOne(System.Byte*,System.Byte*,System.Byte&)
StoreVector512ToDestination(System.Byte*,System.Byte*,System.Int32,System.Runtime.Intrinsics.Vector512`1<System.Byte>)
StoreVector256ToDestination(System.Byte*,System.Byte*,System.Int32,System.Runtime.Intrinsics.Vector256`1<System.Byte>)
StoreVector128ToDestination(System.Byte*,System.Byte*,System.Int32,System.Runtime.Intrinsics.Vector128`1<System.Byte>)
StoreArmVector128x4ToDestination(System.Byte*,System.Byte*,System.Int32,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>)
EncodeThreeAndWrite(System.Byte*,System.Byte*,System.Byte&)
EncodingMap()
Avx2LutChar62()
Avx2LutChar63()
AdvSimdLut4()
Ssse3AdvSimdLutE3()
IncrementPadTwo()
IncrementPadOne()
GetMaxSrcLength(System.Int32,System.Int32)
GetInPlaceDestinationLength(System.Int32,System.Int32)
GetMaxEncodedLength(System.Int32)
EncodeOneOptionallyPadTwo(System.Byte*,System.UInt16*,System.Byte&)
EncodeTwoOptionallyPadOne(System.Byte*,System.UInt16*,System.Byte&)
StoreVector512ToDestination(System.UInt16*,System.UInt16*,System.Int32,System.Runtime.Intrinsics.Vector512`1<System.Byte>)
StoreVector256ToDestination(System.UInt16*,System.UInt16*,System.Int32,System.Runtime.Intrinsics.Vector256`1<System.Byte>)
StoreVector128ToDestination(System.UInt16*,System.UInt16*,System.Int32,System.Runtime.Intrinsics.Vector128`1<System.Byte>)
StoreArmVector128x4ToDestination(System.UInt16*,System.UInt16*,System.Int32,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>,System.Runtime.Intrinsics.Vector128`1<System.Byte>)
EncodeThreeAndWrite(System.Byte*,System.UInt16*,System.Byte&)
AssertRead(System.Byte*,System.Byte*,System.Int32)
AssertWrite(System.Byte*,System.Byte*,System.Int32)
AssertRead(System.UInt16*,System.UInt16*,System.Int32)
AssertWrite(System.UInt16*,System.UInt16*,System.Int32)
ThrowUnreachableException()
IsValid(TBase64Validatable,System.ReadOnlySpan`1<T>,System.Int32&)
.cctor()
IndexOfAnyExcept(System.ReadOnlySpan`1<System.Char>)
IsWhiteSpace(System.Char)
IsEncodingPad(System.Char)
ValidateAndDecodeLength(System.Char,System.Int32,System.Int32,System.Int32&)
.cctor()
IndexOfAnyExcept(System.ReadOnlySpan`1<System.Byte>)
IsWhiteSpace(System.Byte)
IsEncodingPad(System.Byte)
ValidateAndDecodeLength(System.Byte,System.Int32,System.Int32,System.Int32&)