< Summary

Line coverage
63%
Covered lines: 671
Uncovered lines: 393
Coverable lines: 1064
Total lines: 3171
Line coverage: 63%
Branch coverage
66%
Covered branches: 272
Total branches: 406
Branch coverage: 66.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: DecodeFrom(...)78.2%787883.5%
File 1: InvalidDataFallback(TBase64Decoder,System.ReadOnlySpan`1<T>,System.Span`1<System.Byte>,System.Int32&,System.Int32&,System.Boolean)100%1212100%
File 1: DecodeFromUtf8InPlace(...)92.85%282896.77%
File 1: DecodeWithWhiteSpaceBlockwise(...)90%303095.65%
File 1: DecodeWithWhiteSpaceBlockwise(...)0%30300%
File 1: GetPaddingCount(...)83.33%66100%
File 1: GetPaddingCount(...)0%660%
File 1: DecodeWithWhiteSpaceFromUtf8InPlace(...)100%2424100%
File 1: Avx512Decode(...)0%660%
File 1: Avx2Decode(...)100%66100%
File 1: SimdShuffle(...)50%6675%
File 1: Vector128Decode(...)55.55%181893.33%
File 1: WriteThreeLowOrderBytes(...)100%11100%
File 1: IsWhiteSpace(...)50%22100%
File 1: GetMaxDecodedLength(...)100%110%
File 1: IsInvalidLength(...)100%110%
File 1: IsValidPadding(...)100%110%
File 1: SrcLength(...)100%110%
File 1: TryDecode128Core(...)0%220%
File 1: TryDecode256Core(...)0%220%
File 1: TryLoadVector512(...)100%110%
File 1: TryLoadAvxVector256(...)100%11100%
File 1: TryLoadVector128(...)100%11100%
File 1: TryLoadArmVector128x4(...)100%110%
File 1: DecodeFourElements(...)100%11100%
File 1: DecodeRemaining(...)100%66100%
File 1: IndexOfAnyExceptWhiteSpace(...)100%44100%
File 1: DecodeWithWhiteSpaceBlockwiseWrapper(...)100%110%
File 1: GetMaxDecodedLength(...)100%110%
File 1: IsInvalidLength(...)100%110%
File 1: IsValidPadding(...)100%110%
File 1: SrcLength(...)100%110%
File 1: TryDecode128Core(...)100%110%
File 1: TryDecode256Core(...)100%110%
File 1: TryLoadVector512(...)0%220%
File 1: TryLoadAvxVector256(...)50%2275%
File 1: TryLoadVector128(...)50%2275%
File 1: TryLoadArmVector128x4(...)0%220%
File 1: DecodeFourElements(...)50%2294.11%
File 1: DecodeRemaining(...)62.5%8891.3%
File 1: IndexOfAnyExceptWhiteSpace(...)0%440%
File 1: DecodeWithWhiteSpaceBlockwiseWrapper(...)100%110%
File 2: EncodeTo(...)85%404084.9%
File 2: Avx512Encode(...)0%440%
File 2: Avx2Encode(...)75%44100%
File 2: Vector128Encode(...)40%101093.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(...)0%440%
File 2: GetInPlaceDestinationLength(...)100%110%
File 2: GetMaxEncodedLength(...)100%110%
File 2: EncodeOneOptionallyPadTwo(...)100%110%
File 2: EncodeTwoOptionallyPadOne(...)100%110%
File 2: StoreVector512ToDestination(...)100%110%
File 2: StoreVector256ToDestination(...)100%110%
File 2: StoreVector128ToDestination(...)100%110%
File 2: StoreArmVector128x4ToDestination(...)100%110%
File 2: EncodeThreeAndWrite(...)100%110%
File 2: GetMaxSrcLength(...)100%110%
File 2: GetInPlaceDestinationLength(...)100%110%
File 2: GetMaxEncodedLength(...)100%110%
File 2: EncodeOneOptionallyPadTwo(...)100%110%
File 2: EncodeTwoOptionallyPadOne(...)100%110%
File 2: StoreVector512ToDestination(...)100%110%
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%110%
File 4: IndexOfAnyExcept(...)100%110%
File 4: IsWhiteSpace(...)100%110%
File 4: IsEncodingPad(...)100%110%
File 4: ValidateAndDecodeLength(...)0%10100%

File(s)

C:\h\w\9FD50923\w\C0A20A61\e\runtime-utils\Runner\runtime\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;
 8#if NET
 9using System.Runtime.Intrinsics.Arm;
 10using System.Runtime.Intrinsics.X86;
 11using System.Runtime.Intrinsics;
 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 DecodeFrom<TBase64Decoder, T>(TBase64Decoder decoder, ReadOnlySpan<T> sou
 21            out int bytesConsumed, out int bytesWritten, bool isFinalBlock, bool ignoreWhiteSpace)
 22            where TBase64Decoder : IBase64Decoder<T>
 23            where T : unmanaged
 24        {
 25            if (source.IsEmpty)
 26            {
 3327                bytesConsumed = 0;
 3328                bytesWritten = 0;
 3329                return OperationStatus.Done;
 30            }
 31
 35236632            fixed (T* srcBytes = &MemoryMarshal.GetReference(source))
 35236633            fixed (byte* destBytes = &MemoryMarshal.GetReference(bytes))
 34            {
 35236635                int srcLength = decoder.SrcLength(isFinalBlock, source.Length);
 35236636                int destLength = bytes.Length;
 35236637                int maxSrcLength = srcLength;
 35236638                int decodedLength = decoder.GetMaxDecodedLength(srcLength);
 39
 40                // max. 2 padding chars
 35236641                if (destLength < decodedLength - 2)
 42                {
 43                    // For overflow see comment below
 044                    maxSrcLength = destLength / 3 * 4;
 45                }
 46
 35236647                T* src = srcBytes;
 35236648                byte* dest = destBytes;
 35236649                T* srcEnd = srcBytes + (uint)srcLength;
 35236650                T* srcMax = srcBytes + (uint)maxSrcLength;
 51
 52#if NET
 35236653                if (maxSrcLength >= 24)
 54                {
 13602055                    T* end = srcMax - 88;
 13602056                    if (Vector512.IsHardwareAccelerated && Avx512Vbmi.IsSupported && (end >= src))
 57                    {
 058                        Avx512Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 59
 060                        if (src == srcEnd)
 61                        {
 62                            goto DoneExit;
 63                        }
 64                    }
 65
 13602066                    end = srcMax - 45;
 13602067                    if (Avx2.IsSupported && (end >= src))
 68                    {
 13206369                        Avx2Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 70
 13206371                        if (src == srcEnd)
 72                        {
 73                            goto DoneExit;
 74                        }
 75                    }
 76
 13602077                    end = srcMax - 66;
 78                    if (AdvSimd.Arm64.IsSupported && (end >= src))
 79                    {
 80                        AdvSimdDecode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 81
 82                        if (src == srcEnd)
 83                        {
 84                            goto DoneExit;
 85                        }
 86                    }
 87
 13602088                    end = srcMax - 24;
 13602089                    if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian && (end >= src))
 90                    {
 13420591                        Vector128Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 92
 13420593                        if (src == srcEnd)
 94                        {
 95                            goto DoneExit;
 96                        }
 97                    }
 98                }
 99#endif
 100
 101                // Last bytes could have padding characters, so process them separately and treat them as valid only if 
 102                // if isFinalBlock is false, padding characters are considered invalid
 352366103                int skipLastChunk = isFinalBlock ? 4 : 0;
 104
 352366105                if (destLength >= decodedLength)
 106                {
 352366107                    maxSrcLength = srcLength - skipLastChunk;
 108                }
 109                else
 110                {
 111                    // This should never overflow since destLength here is less than int.MaxValue / 4 * 3 (i.e. 16106127
 112                    // Therefore, (destLength / 3) * 4 will always be less than 2147483641
 0113                    Debug.Assert(destLength < (int.MaxValue / 4 * 3));
 114#if NET
 0115                    (maxSrcLength, int remainder) = int.DivRem(destLength, 3);
 0116                    maxSrcLength *= 4;
 117#else
 118                    maxSrcLength = (destLength / 3) * 4;
 119                    int remainder = (int)((uint)destLength % 3);
 120#endif
 0121                    if (isFinalBlock && remainder > 0)
 122                    {
 0123                        srcLength &= ~0x3; // In case of Base64UrlDecoder source can be not a multiple of 4, round down 
 124                    }
 125                }
 126
 352366127                ref sbyte decodingMap = ref MemoryMarshal.GetReference(decoder.DecodingMap);
 352366128                srcMax = srcBytes + maxSrcLength;
 129
 748093130                while (src < srcMax)
 131                {
 526579132                    int result = decoder.DecodeFourElements(src, ref decodingMap);
 133
 526579134                    if (result < 0)
 135                    {
 136                        goto InvalidDataExit;
 137                    }
 138
 395727139                    WriteThreeLowOrderBytes(dest, result);
 395727140                    src += 4;
 395727141                    dest += 3;
 142                }
 143
 221514144                if (maxSrcLength != srcLength - skipLastChunk)
 145                {
 146                    goto DestinationTooSmallExit;
 147                }
 148
 221514149                if (src == srcEnd)
 150                {
 213035151                    if (isFinalBlock)
 152                    {
 153                        goto InvalidDataExit;
 154                    }
 155
 213035156                    if (src == srcBytes + source.Length)
 157                    {
 213035158                        goto DoneExit;
 159                    }
 160
 161                    goto NeedMoreDataExit;
 162                }
 163
 164                // if isFinalBlock is false, we will never reach this point
 165                // Handle remaining bytes, for Base64 its always 4 bytes, for Base64Url up to 8 bytes left.
 166                // If more than 4 bytes remained it will end up in DestinationTooSmallExit or InvalidDataExit (might suc
 8479167                long remaining = srcEnd - src;
 8479168                Debug.Assert(typeof(TBase64Decoder) == typeof(Base64DecoderByte) ? remaining == 4 : remaining < 8);
 8479169                int i0 = decoder.DecodeRemaining(srcEnd, ref decodingMap, remaining, out uint t2, out uint t3);
 170
 8479171                byte* destMax = destBytes + (uint)destLength;
 172
 8479173                if (!decoder.IsValidPadding(t3))
 174                {
 4011175                    int i2 = Unsafe.Add(ref decodingMap, (IntPtr)t2);
 4011176                    int i3 = Unsafe.Add(ref decodingMap, (IntPtr)t3);
 177
 4011178                    i2 <<= 6;
 179
 4011180                    i0 |= i3;
 4011181                    i0 |= i2;
 182
 4011183                    if (i0 < 0)
 184                    {
 185                        goto InvalidDataExit;
 186                    }
 3860187                    if (dest + 3 > destMax)
 188                    {
 189                        goto DestinationTooSmallExit;
 190                    }
 191
 3860192                    WriteThreeLowOrderBytes(dest, i0);
 3860193                    dest += 3;
 3860194                    src += 4;
 195                }
 4468196                else if (!decoder.IsValidPadding(t2))
 197                {
 1857198                    int i2 = Unsafe.Add(ref decodingMap, (IntPtr)t2);
 199
 1857200                    i2 <<= 6;
 201
 1857202                    i0 |= i2;
 203
 1857204                    if ((i0 & 0x800000c0) != 0) // if negative or 2 unused bits are not 0.
 205                    {
 206                        goto InvalidDataExit;
 207                    }
 1559208                    if (dest + 2 > destMax)
 209                    {
 210                        goto DestinationTooSmallExit;
 211                    }
 212
 1559213                    dest[0] = (byte)(i0 >> 16);
 1559214                    dest[1] = (byte)(i0 >> 8);
 1559215                    dest += 2;
 1559216                    src += remaining;
 217                }
 218                else
 219                {
 2611220                    if ((i0 & 0x8000F000) != 0) // if negative or 4 unused bits are not 0.
 221                    {
 222                        goto InvalidDataExit;
 223                    }
 1755224                    if (dest + 1 > destMax)
 225                    {
 226                        goto DestinationTooSmallExit;
 227                    }
 228
 1755229                    dest[0] = (byte)(i0 >> 16);
 1755230                    dest += 1;
 1755231                    src += remaining;
 232                }
 233
 7174234                if (srcLength != source.Length)
 235                {
 236                    goto InvalidDataExit;
 237                }
 238
 239            DoneExit:
 220209240                bytesConsumed = (int)(src - srcBytes);
 220209241                bytesWritten = (int)(dest - destBytes);
 220209242                return OperationStatus.Done;
 243
 244            DestinationTooSmallExit:
 0245                if (srcLength != source.Length && isFinalBlock)
 246                {
 247                    goto InvalidDataExit; // if input is not a multiple of 4, and there is no more data, return invalid 
 248                }
 249
 0250                if (ignoreWhiteSpace)
 251                {
 252                    // Fall through to InvalidDataFallback which strips whitespace and re-evaluates destination size req
 253                    goto InvalidDataExit;
 254                }
 255
 0256                bytesConsumed = (int)(src - srcBytes);
 0257                bytesWritten = (int)(dest - destBytes);
 0258                return OperationStatus.DestinationTooSmall;
 259
 260            NeedMoreDataExit:
 0261                bytesConsumed = (int)(src - srcBytes);
 0262                bytesWritten = (int)(dest - destBytes);
 0263                return OperationStatus.NeedMoreData;
 264
 265            InvalidDataExit:
 132157266                bytesConsumed = (int)(src - srcBytes);
 132157267                bytesWritten = (int)(dest - destBytes);
 132157268                return ignoreWhiteSpace ?
 132157269                    InvalidDataFallback(decoder, source, bytes, ref bytesConsumed, ref bytesWritten, isFinalBlock) :
 132157270                    OperationStatus.InvalidData;
 271            }
 272
 273            static OperationStatus InvalidDataFallback(TBase64Decoder decoder, ReadOnlySpan<T> source, Span<byte> bytes,
 274            {
 5468275                source = source.Slice(bytesConsumed);
 5468276                bytes = bytes.Slice(bytesWritten);
 277
 278                OperationStatus status;
 279                do
 280                {
 131167281                    int localConsumed = decoder.IndexOfAnyExceptWhiteSpace(source);
 131167282                    if (localConsumed < 0)
 283                    {
 284                        // The remainder of the input is all whitespace. Mark it all as having been consumed,
 285                        // and mark the operation as being done.
 430286                        bytesConsumed += source.Length;
 430287                        status = OperationStatus.Done;
 430288                        break;
 289                    }
 290
 130737291                    if (localConsumed == 0)
 292                    {
 293                        // Non-whitespace was found at the beginning of the input. Since it wasn't consumed
 294                        // by the previous call to DecodeFromUtf8, it must be part of a Base64 sequence
 295                        // that was interrupted by whitespace or something else considered invalid.
 296                        // Fall back to block-wise decoding. This is very slow, but it's also very non-standard
 297                        // formatting of the input; whitespace is typically only found between blocks, such as
 298                        // when Convert.ToBase64String inserts a line break every 76 output characters.
 3174299                        return decoder.DecodeWithWhiteSpaceBlockwiseWrapper(decoder, source, bytes, ref bytesConsumed, r
 300                    }
 301
 302                    // Skip over the starting whitespace and continue.
 127563303                    bytesConsumed += localConsumed;
 127563304                    source = source.Slice(localConsumed);
 305
 306                    // Try again after consumed whitespace
 127563307                    status = DecodeFrom(decoder, source, bytes, out localConsumed, out int localWritten, isFinalBlock, i
 127563308                    bytesConsumed += localConsumed;
 127563309                    bytesWritten += localWritten;
 310
 127563311                    if (status is OperationStatus.Done or OperationStatus.NeedMoreData)
 312                    {
 313                        break;
 314                    }
 315
 316                    // The DecodeFrom helper will return DestinationTooSmall if the destination is too small,
 317                    // regardless of whether it's actually too small once you skip whitespace characters.
 318                    // In that case we loop again and fall back to block-wise decoding if we can't make progress.
 319
 125699320                    source = source.Slice(localConsumed);
 125699321                    bytes = bytes.Slice(localWritten);
 322                }
 125699323                while (!source.IsEmpty);
 324
 2294325                return status;
 326            }
 327        }
 328
 329        internal static unsafe OperationStatus DecodeFromUtf8InPlace<TBase64Decoder>(TBase64Decoder decoder, Span<byte> 
 330            where TBase64Decoder : IBase64Decoder<byte>
 331        {
 495845332            if (buffer.IsEmpty)
 333            {
 0334                bytesWritten = 0;
 0335                return OperationStatus.Done;
 336            }
 337
 495845338            fixed (byte* bufferBytes = &MemoryMarshal.GetReference(buffer))
 339            {
 495845340                uint bufferLength = (uint)buffer.Length;
 495845341                uint sourceIndex = 0;
 495845342                uint destIndex = 0;
 343
 495845344                if (decoder.IsInvalidLength(buffer.Length))
 345                {
 346                    goto InvalidExit;
 347                }
 348
 494990349                ref sbyte decodingMap = ref MemoryMarshal.GetReference(decoder.DecodingMap);
 350
 494990351                if (bufferLength > 4)
 352                {
 779655353                    while (sourceIndex < bufferLength - 4)
 354                    {
 776378355                        int result = decoder.DecodeFourElements(bufferBytes + sourceIndex, ref decodingMap);
 776378356                        if (result < 0)
 357                        {
 358                            goto InvalidExit;
 359                        }
 360
 774046361                        WriteThreeLowOrderBytes(bufferBytes + destIndex, result);
 774046362                        destIndex += 3;
 774046363                        sourceIndex += 4;
 364                    }
 365                }
 366
 367                uint t0;
 368                uint t1;
 369                uint t2;
 370                uint t3;
 371
 492658372                switch (bufferLength - sourceIndex)
 373                {
 374                    case 2:
 1239375                        t0 = bufferBytes[bufferLength - 2];
 1239376                        t1 = bufferBytes[bufferLength - 1];
 1239377                        t2 = EncodingPad;
 1239378                        t3 = EncodingPad;
 1239379                        break;
 380                    case 3:
 1132381                        t0 = bufferBytes[bufferLength - 3];
 1132382                        t1 = bufferBytes[bufferLength - 2];
 1132383                        t2 = bufferBytes[bufferLength - 1];
 1132384                        t3 = EncodingPad;
 1132385                        break;
 386                    case 4:
 490287387                        t0 = bufferBytes[bufferLength - 4];
 490287388                        t1 = bufferBytes[bufferLength - 3];
 490287389                        t2 = bufferBytes[bufferLength - 2];
 490287390                        t3 = bufferBytes[bufferLength - 1];
 391                        break;
 392                    default:
 393                        goto InvalidExit;
 394                }
 395
 492658396                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 492658397                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 398
 492658399                i0 <<= 18;
 492658400                i1 <<= 12;
 401
 492658402                i0 |= i1;
 403
 492658404                if (!decoder.IsValidPadding(t3))
 405                {
 489324406                    int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 489324407                    int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 408
 489324409                    i2 <<= 6;
 410
 489324411                    i0 |= i3;
 489324412                    i0 |= i2;
 413
 489324414                    if (i0 < 0)
 415                    {
 416                        goto InvalidExit;
 417                    }
 418
 488869419                    WriteThreeLowOrderBytes(bufferBytes + destIndex, i0);
 488869420                    destIndex += 3;
 421                }
 3334422                else if (!decoder.IsValidPadding(t2))
 423                {
 1575424                    int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 425
 1575426                    i2 <<= 6;
 427
 1575428                    i0 |= i2;
 429
 1575430                    if ((i0 & 0x800000c0) != 0) // if negative or 2 unused bits are not 0.
 431                    {
 432                        goto InvalidExit;
 433                    }
 434
 1341435                    bufferBytes[destIndex] = (byte)(i0 >> 16);
 1341436                    bufferBytes[destIndex + 1] = (byte)(i0 >> 8);
 1341437                    destIndex += 2;
 438                }
 439                else
 440                {
 1759441                    if ((i0 & 0x8000F000) != 0) // if negative or 4 unused bits are not 0.
 442                    {
 443                        goto InvalidExit;
 444                    }
 445
 1473446                    bufferBytes[destIndex] = (byte)(i0 >> 16);
 1473447                    destIndex += 1;
 448                }
 449
 491683450                bytesWritten = (int)destIndex;
 491683451                return OperationStatus.Done;
 452
 453            InvalidExit:
 4162454                bytesWritten = (int)destIndex;
 4162455                return ignoreWhiteSpace ?
 4162456                    DecodeWithWhiteSpaceFromUtf8InPlace<TBase64Decoder>(decoder, buffer, ref bytesWritten, sourceIndex) 
 4162457                    OperationStatus.InvalidData;
 458            }
 459        }
 460
 461        internal static OperationStatus DecodeWithWhiteSpaceBlockwise<TBase64Decoder>(TBase64Decoder decoder, ReadOnlySp
 462            where TBase64Decoder : IBase64Decoder<byte>
 463        {
 464            const int BlockSize = 4;
 3174465            Span<byte> buffer = stackalloc byte[BlockSize];
 3174466            OperationStatus status = OperationStatus.Done;
 467
 259728468            while (!source.IsEmpty)
 469            {
 470                // Skip over any leading whitespace
 259498471                if (IsWhiteSpace(source[0]))
 472                {
 46812473                    source = source.Slice(1);
 46812474                    bytesConsumed++;
 46812475                    continue;
 476                }
 477
 212686478                int encodedIdx = 0;
 212686479                int bufferIdx = 0;
 212686480                int skipped = 0;
 481
 2171706482                for (; encodedIdx < source.Length && (uint)bufferIdx < (uint)buffer.Length; ++encodedIdx)
 483                {
 979510484                    if (IsWhiteSpace(source[encodedIdx]))
 485                    {
 131022486                        skipped++;
 487                    }
 488                    else
 489                    {
 848488490                        buffer[bufferIdx] = source[encodedIdx];
 848488491                        bufferIdx++;
 492                    }
 493                }
 494
 212686495                source = source.Slice(encodedIdx);
 212686496                Debug.Assert(bufferIdx > 0);
 497
 498                bool hasAnotherBlock;
 499
 212686500                if (typeof(TBase64Decoder) == typeof(Base64DecoderByte))
 501                {
 0502                    hasAnotherBlock = source.Length >= BlockSize;
 503                }
 504                else
 505                {
 212686506                    hasAnotherBlock = source.Length > 1;
 507                }
 508
 212686509                bool localIsFinalBlock = !hasAnotherBlock;
 510
 511                // If this block contains padding and there's another block, then only whitespace may follow for being v
 212686512                if (hasAnotherBlock)
 513                {
 210332514                    int paddingCount = GetPaddingCount(decoder, ref buffer[BlockSize - 1]);
 210332515                    if (paddingCount > 0)
 516                    {
 239517                        hasAnotherBlock = false;
 239518                        localIsFinalBlock = true;
 519                    }
 520                }
 521
 212686522                if (localIsFinalBlock && !isFinalBlock)
 523                {
 0524                    localIsFinalBlock = false;
 525                }
 526
 212686527                status = DecodeFrom<TBase64Decoder, byte>(decoder, buffer.Slice(0, bufferIdx), bytes, out int localConsu
 528
 212686529                if (status != OperationStatus.Done)
 530                {
 990531                    Debug.Assert(localConsumed == 0 && localWritten == 0, "On failure, should not have consumed or writt
 990532                    return status;
 533                }
 534
 211696535                bytesConsumed += skipped;
 211696536                bytesConsumed += localConsumed;
 211696537                bytesWritten += localWritten;
 538
 539                // The remaining data must all be whitespace in order to be valid.
 211696540                if (!hasAnotherBlock)
 541                {
 14806542                    for (int i = 0; i < source.Length; ++i)
 543                    {
 5613544                        if (!IsWhiteSpace(source[i]))
 545                        {
 546                            // Revert previous dest increment, since an invalid state followed.
 164547                            bytesConsumed -= localConsumed;
 164548                            bytesWritten -= localWritten;
 549
 164550                            return OperationStatus.InvalidData;
 551                        }
 552
 5449553                        bytesConsumed++;
 554                    }
 555
 1790556                    break;
 557                }
 558
 209742559                bytes = bytes.Slice(localWritten);
 209742560                Debug.Assert(!source.IsEmpty);
 561            }
 562
 2020563            return status;
 564        }
 565
 566        internal static OperationStatus DecodeWithWhiteSpaceBlockwise<TBase64Decoder>(TBase64Decoder decoder, ReadOnlySp
 567            where TBase64Decoder : IBase64Decoder<ushort>
 568        {
 569            const int BlockSize = 4;
 0570            Span<ushort> buffer = stackalloc ushort[BlockSize];
 0571            OperationStatus status = OperationStatus.Done;
 572
 0573            while (!source.IsEmpty)
 574            {
 575                // Skip over any leading whitespace
 0576                if (IsWhiteSpace(source[0]))
 577                {
 0578                    source = source.Slice(1);
 0579                    bytesConsumed++;
 0580                    continue;
 581                }
 582
 0583                int encodedIdx = 0;
 0584                int bufferIdx = 0;
 0585                int skipped = 0;
 586
 0587                for (; encodedIdx < source.Length && (uint)bufferIdx < (uint)buffer.Length; ++encodedIdx)
 588                {
 0589                    if (IsWhiteSpace(source[encodedIdx]))
 590                    {
 0591                        skipped++;
 592                    }
 593                    else
 594                    {
 0595                        buffer[bufferIdx] = source[encodedIdx];
 0596                        bufferIdx++;
 597                    }
 598                }
 599
 0600                source = source.Slice(encodedIdx);
 0601                Debug.Assert(bufferIdx > 0);
 602
 603                bool hasAnotherBlock;
 604
 0605                if (decoder is Base64DecoderByte)
 606                {
 0607                    hasAnotherBlock = source.Length >= BlockSize;
 608                }
 609                else
 610                {
 0611                    hasAnotherBlock = source.Length > 1;
 612                }
 613
 0614                bool localIsFinalBlock = !hasAnotherBlock;
 615
 616                // If this block contains padding and there's another block, then only whitespace may follow for being v
 0617                if (hasAnotherBlock)
 618                {
 0619                    int paddingCount = GetPaddingCount(decoder, ref buffer[BlockSize - 1]);
 0620                    if (paddingCount > 0)
 621                    {
 0622                        hasAnotherBlock = false;
 0623                        localIsFinalBlock = true;
 624                    }
 625                }
 626
 0627                if (localIsFinalBlock && !isFinalBlock)
 628                {
 0629                    localIsFinalBlock = false;
 630                }
 631
 0632                status = DecodeFrom(decoder, buffer.Slice(0, bufferIdx), bytes, out int localConsumed, out int localWrit
 633
 0634                if (status != OperationStatus.Done)
 635                {
 0636                    Debug.Assert(localConsumed == 0 && localWritten == 0, "On failure, should not have consumed or writt
 0637                    return status;
 638                }
 639
 0640                bytesConsumed += skipped;
 0641                bytesConsumed += localConsumed;
 0642                bytesWritten += localWritten;
 643
 644                // The remaining data must all be whitespace in order to be valid.
 0645                if (!hasAnotherBlock)
 646                {
 0647                    for (int i = 0; i < source.Length; ++i)
 648                    {
 0649                        if (!IsWhiteSpace(source[i]))
 650                        {
 651                            // Revert previous dest increment, since an invalid state followed.
 0652                            bytesConsumed -= localConsumed;
 0653                            bytesWritten -= localWritten;
 654
 0655                            return OperationStatus.InvalidData;
 656                        }
 657                    }
 658
 0659                    bytesConsumed += source.Length;
 0660                    break;
 661                }
 662
 0663                bytes = bytes.Slice(localWritten);
 0664                Debug.Assert(!source.IsEmpty);
 665            }
 666
 0667            return status;
 668        }
 669
 670        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 671        private static int GetPaddingCount<TBase64Decoder>(TBase64Decoder decoder, ref byte ptrToLastElement)
 672            where TBase64Decoder : IBase64Decoder<byte>
 673        {
 210332674            int padding = 0;
 675
 210332676            if (decoder.IsValidPadding(ptrToLastElement))
 677            {
 233678                padding++;
 679            }
 680
 210332681            if (decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))
 682            {
 63683                padding++;
 684            }
 685
 210332686            return padding;
 687        }
 688
 689        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 690        private static int GetPaddingCount<TBase64Decoder>(TBase64Decoder decoder, ref ushort ptrToLastElement)
 691            where TBase64Decoder : IBase64Decoder<ushort>
 692        {
 0693            int padding = 0;
 694
 0695            if (decoder.IsValidPadding(ptrToLastElement))
 696            {
 0697                padding++;
 698            }
 699
 0700            if (decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))
 701            {
 0702                padding++;
 703            }
 704
 0705            return padding;
 706        }
 707
 708        private static OperationStatus DecodeWithWhiteSpaceFromUtf8InPlace<TBase64Decoder>(TBase64Decoder decoder, Span<
 709            where TBase64Decoder : IBase64Decoder<byte>
 710        {
 3311711            int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);
 3311712            Span<byte> buffer = stackalloc byte[BlockSize];
 713
 3311714            OperationStatus status = OperationStatus.Done;
 3311715            int localDestIndex = destIndex;
 3311716            bool hasPaddingBeenProcessed = false;
 3311717            int localBytesWritten = 0;
 718
 492100719            while (sourceIndex < (uint)source.Length)
 720            {
 489943721                int bufferIdx = 0;
 722
 2749698723                while (bufferIdx < BlockSize && sourceIndex < (uint)source.Length)
 724                {
 2259755725                    if (!IsWhiteSpace(source[(int)sourceIndex]))
 726                    {
 1955862727                        buffer[bufferIdx] = source[(int)sourceIndex];
 1955862728                        bufferIdx++;
 729                    }
 730
 2259755731                    sourceIndex++;
 732                }
 733
 489943734                if (bufferIdx == 0)
 735                {
 736                    continue;
 737                }
 738
 489496739                if (bufferIdx != 4)
 740                {
 741                    // Base64 require 4 bytes, for Base64Url it can be less than 4 bytes but not 1 byte.
 1113742                    if (decoder is Base64DecoderByte || bufferIdx == 1)
 743                    {
 262744                        status = OperationStatus.InvalidData;
 262745                        break;
 746                    }
 747                    else // For Base64Url fill empty slots in last block with padding
 748                    {
 2115749                        while (bufferIdx < BlockSize)  // Can happen only for last block
 750                        {
 1264751                            Debug.Assert(source.Length == sourceIndex);
 1264752                            buffer[bufferIdx++] = (byte)EncodingPad;
 753                        }
 754                    }
 755                }
 756
 489234757                if (hasPaddingBeenProcessed)
 758                {
 759                    // Padding has already been processed, a new valid block cannot be processed.
 760                    // Revert previous dest increment, since an invalid state followed.
 41761                    localDestIndex -= localBytesWritten;
 41762                    status = OperationStatus.InvalidData;
 41763                    break;
 764                }
 765
 489193766                status = DecodeFromUtf8InPlace<TBase64Decoder>(decoder, buffer, out localBytesWritten, ignoreWhiteSpace:
 489193767                localDestIndex += localBytesWritten;
 489193768                hasPaddingBeenProcessed = localBytesWritten < 3;
 769
 489193770                if (status != OperationStatus.Done)
 771                {
 772                    break;
 773                }
 774
 775                // Write result to source span in place.
 3905038776                for (int i = 0; i < localBytesWritten; i++)
 777                {
 1464177778                    source[localDestIndex - localBytesWritten + i] = buffer[i];
 779                }
 780            }
 781
 3311782            destIndex = localDestIndex;
 3311783            return status;
 784        }
 785
 786#if NET
 787        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 788        [CompExactlyDependsOn(typeof(Avx512BW))]
 789        [CompExactlyDependsOn(typeof(Avx512Vbmi))]
 790        private static unsafe void Avx512Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* de
 791            where TBase64Decoder : IBase64Decoder<T>
 792            where T : unmanaged
 793        {
 794            // Reference for VBMI implementation : https://github.com/WojciechMula/base64simd/tree/master/decode
 795            // If we have AVX512 support, pick off 64 bytes at a time for as long as we can,
 796            // but make sure that we quit before seeing any == markers at the end of the
 797            // string. Also, because we write 16 zeroes at the end of the output, ensure
 798            // that there are at least 22 valid bytes of input data remaining to close the
 799            // gap. 64 + 2 + 22 = 88 bytes.
 0800            T* src = srcBytes;
 0801            byte* dest = destBytes;
 802
 803            // The JIT won't hoist these "constants", so help it
 0804            Vector512<sbyte> vbmiLookup0 = Vector512.Create(decoder.VbmiLookup0).AsSByte();
 0805            Vector512<sbyte> vbmiLookup1 = Vector512.Create(decoder.VbmiLookup1).AsSByte();
 0806            Vector512<byte> vbmiPackedLanesControl = Vector512.Create(
 0807                0x06000102, 0x090a0405, 0x0c0d0e08, 0x16101112,
 0808                0x191a1415, 0x1c1d1e18, 0x26202122, 0x292a2425,
 0809                0x2c2d2e28, 0x36303132, 0x393a3435, 0x3c3d3e38,
 0810                0x00000000, 0x00000000, 0x00000000, 0x00000000).AsByte();
 811
 0812            Vector512<sbyte> mergeConstant0 = Vector512.Create(0x01400140).AsSByte();
 0813            Vector512<short> mergeConstant1 = Vector512.Create(0x00011000).AsInt16();
 814
 815            // This algorithm requires AVX512VBMI support.
 816            // Vbmi was first introduced in CannonLake and is available from IceLake on.
 817            do
 818            {
 0819                if (!decoder.TryLoadVector512(src, srcStart, sourceLength, out Vector512<sbyte> str))
 820                {
 821                    break;
 822                }
 823
 824                // Step 1: Translate encoded Base64 input to their original indices
 825                // This step also checks for invalid inputs and exits.
 826                // After this, we have indices which are verified to have upper 2 bits set to 0 in each byte.
 827                // origIndex      = [...|00dddddd|00cccccc|00bbbbbb|00aaaaaa]
 0828                Vector512<sbyte> origIndex = Avx512Vbmi.PermuteVar64x8x2(vbmiLookup0, str, vbmiLookup1);
 0829                Vector512<sbyte> errorVec = (origIndex.AsInt32() | str.AsInt32()).AsSByte();
 0830                if (errorVec.ExtractMostSignificantBits() != 0)
 831                {
 832                    break;
 833                }
 834
 835                // Step 2: Now we need to reshuffle bits to remove the 0 bits.
 836                // multiAdd1: [...|0000cccc|ccdddddd|0000aaaa|aabbbbbb]
 0837                Vector512<short> multiAdd1 = Avx512BW.MultiplyAddAdjacent(origIndex.AsByte(), mergeConstant0);
 838                // multiAdd1: [...|00000000|aaaaaabb|bbbbcccc|ccdddddd]
 0839                Vector512<int> multiAdd2 = Avx512BW.MultiplyAddAdjacent(multiAdd1, mergeConstant1);
 840
 841                // Step 3: Pack 48 bytes
 0842                str = Avx512Vbmi.PermuteVar64x8(multiAdd2.AsByte(), vbmiPackedLanesControl).AsSByte();
 843
 0844                AssertWrite<Vector512<sbyte>>(dest, destStart, destLength);
 0845                str.Store((sbyte*)dest);
 0846                src += 64;
 0847                dest += 48;
 848            }
 0849            while (src <= srcEnd);
 850
 0851            srcBytes = src;
 0852            destBytes = dest;
 0853        }
 854
 855        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 856        [CompExactlyDependsOn(typeof(Avx2))]
 857        private static unsafe void Avx2Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* dest
 858            where TBase64Decoder : IBase64Decoder<T>
 859            where T : unmanaged
 860        {
 861            // If we have AVX2 support, pick off 32 bytes at a time for as long as we can,
 862            // but make sure that we quit before seeing any == markers at the end of the
 863            // string. Also, because we write 8 zeroes at the end of the output, ensure
 864            // that there are at least 11 valid bytes of input data remaining to close the
 865            // gap. 32 + 2 + 11 = 45 bytes.
 866
 867            // See SSSE3-version below for an explanation of how the code works.
 868
 869            // The JIT won't hoist these "constants", so help it
 132063870            Vector256<sbyte> lutHi = Vector256.Create(decoder.Avx2LutHigh);
 871
 132063872            Vector256<sbyte> lutLo = Vector256.Create(decoder.Avx2LutLow);
 873
 132063874            Vector256<sbyte> lutShift = Vector256.Create(decoder.Avx2LutShift);
 875
 132063876            Vector256<sbyte> packBytesInLaneMask = Vector256.Create(
 132063877                2, 1, 0, 6,
 132063878                5, 4, 10, 9,
 132063879                8, 14, 13, 12,
 132063880                -1, -1, -1, -1,
 132063881                2, 1, 0, 6,
 132063882                5, 4, 10, 9,
 132063883                8, 14, 13, 12,
 132063884                -1, -1, -1, -1);
 885
 132063886            Vector256<int> packLanesControl = Vector256.Create(
 132063887                 0, 0, 0, 0,
 132063888                1, 0, 0, 0,
 132063889                2, 0, 0, 0,
 132063890                4, 0, 0, 0,
 132063891                5, 0, 0, 0,
 132063892                6, 0, 0, 0,
 132063893                -1, -1, -1, -1,
 132063894                -1, -1, -1, -1).AsInt32();
 895
 132063896            Vector256<sbyte> maskSlashOrUnderscore = Vector256.Create((sbyte)decoder.MaskSlashOrUnderscore);
 132063897            Vector256<sbyte> shiftForUnderscore = Vector256.Create((sbyte)33);
 132063898            Vector256<sbyte> mergeConstant0 = Vector256.Create(0x01400140).AsSByte();
 132063899            Vector256<short> mergeConstant1 = Vector256.Create(0x00011000).AsInt16();
 900
 132063901            T* src = srcBytes;
 132063902            byte* dest = destBytes;
 903
 904            //while (remaining >= 45)
 905            do
 906            {
 337107907                if (!decoder.TryLoadAvxVector256(src, srcStart, sourceLength, out Vector256<sbyte> str))
 908                {
 909                    break;
 910                }
 911
 337107912                Vector256<sbyte> hiNibbles = ((str.AsInt32()) >>> 4).AsSByte() & maskSlashOrUnderscore;
 913
 337107914                if (!decoder.TryDecode256Core(str, hiNibbles, maskSlashOrUnderscore, lutLo, lutHi, lutShift, shiftForUnd
 915                {
 916                    break;
 917                }
 918
 919                // in, lower lane, bits, upper case are most significant bits, lower case are least significant bits:
 920                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 921                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 922                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 923                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 924
 210793925                Vector256<short> merge_ab_and_bc = Avx2.MultiplyAddAdjacent(str.AsByte(), mergeConstant0);
 926                // 0000kkkk LLllllll 0000JJJJ JJjjKKKK
 927                // 0000hhhh IIiiiiii 0000GGGG GGggHHHH
 928                // 0000eeee FFffffff 0000DDDD DDddEEEE
 929                // 0000bbbb CCcccccc 0000AAAA AAaaBBBB
 930
 210793931                Vector256<int> output = Avx2.MultiplyAddAdjacent(merge_ab_and_bc, mergeConstant1);
 932                // 00000000 JJJJJJjj KKKKkkkk LLllllll
 933                // 00000000 GGGGGGgg HHHHhhhh IIiiiiii
 934                // 00000000 DDDDDDdd EEEEeeee FFffffff
 935                // 00000000 AAAAAAaa BBBBbbbb CCcccccc
 936
 937                // Pack bytes together in each lane:
 210793938                output = Avx2.Shuffle(output.AsSByte(), packBytesInLaneMask).AsInt32();
 939                // 00000000 00000000 00000000 00000000
 940                // LLllllll KKKKkkkk JJJJJJjj IIiiiiii
 941                // HHHHhhhh GGGGGGgg FFffffff EEEEeeee
 942                // DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa
 943
 944                // Pack lanes
 210793945                str = Avx2.PermuteVar8x32(output, packLanesControl).AsSByte();
 946
 210793947                AssertWrite<Vector256<sbyte>>(dest, destStart, destLength);
 210793948                Avx.Store(dest, str.AsByte());
 949
 210793950                src += 32;
 210793951                dest += 24;
 952            }
 210793953            while (src <= srcEnd);
 954
 132063955            srcBytes = src;
 132063956            destBytes = dest;
 132063957        }
 958
 959        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 960        [CompExactlyDependsOn(typeof(Ssse3))]
 961        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 962        internal static Vector128<byte> SimdShuffle(Vector128<byte> left, Vector128<byte> right, Vector128<byte> mask8F)
 963        {
 581234964            Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian);
 965
 581234966            if (Ssse3.IsSupported)
 967            {
 581234968                return Ssse3.Shuffle(left, right);
 969            }
 970            else
 971            {
 0972                return AdvSimd.Arm64.VectorTableLookup(left, right & mask8F);
 973            }
 974        }
 975
 976        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 977        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 978        private static unsafe void AdvSimdDecode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* d
 979            where TBase64Decoder : IBase64Decoder<T>
 980            where T : unmanaged
 981        {
 982            // C# implementation of https://github.com/aklomp/base64/blob/3a5add8652076612a8407627a42c768736a4263f/lib/a
 983            // If we have AdvSimd support, pick off 64 bytes at a time for as long as we can,
 984            // but make sure that we quit before seeing any == markers at the end of the
 985            // string. 64 + 2 = 66 bytes.
 986
 987            // In the decoding process, we want to map each byte, representing a Base64 value, to its 6-bit (0-63) repre
 988            // It uses the following mapping. Values outside the following groups are invalid and, we abort decoding whe
 989            //
 990            // #    From       To         Char
 991            // 1    [43]       [62]       +
 992            // 2    [47]       [63]       /
 993            // 3    [48..57]   [52..61]   0..9
 994            // 4    [65..90]   [0..25]    A..Z
 995            // 5    [97..122]  [26..51]   a..z
 996            //
 997            // To map an input value to its Base64 representation, we use look-up tables 'decLutOne' and 'decLutTwo'.
 998            // 'decLutOne' helps to map groups 1, 2 and 3 while 'decLutTwo' maps groups 4 and 5 in the above list.
 999            // After mapping, each value falls between 0-63. Consequently, the last six bits of each byte now hold a val
 1000            // We then compress four such bytes (with valid 4 * 6 = 24 bits) to three UTF8 bytes (3 * 8 = 24 bits).
 1001            // For faster decoding, we use SIMD operations that allow the processing of multiple bytes together.
 1002            // However, the compress operation on adjacent values of a vector could be slower. Thus, we de-interleave wh
 1003            // the input bytes that store adjacent bytes in separate vectors. This later simplifies the compress step wi
 1004            // of logical operations. This requires interleaving while storing the decoded result.
 1005
 1006            // Values in 'decLutOne' maps input values from 0 to 63.
 1007            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
 1008            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
 1009            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63
 1010            //    52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 255, 255, 255
 1011            var decLutOne = (Vector128<byte>.AllBitsSet,
 1012                             Vector128<byte>.AllBitsSet,
 1013                             Vector128.Create(decoder.AdvSimdLutOne3).AsByte(),
 1014                             Vector128.Create(0x37363534, 0x3B3A3938, 0xFFFF3D3C, 0xFFFFFFFF).AsByte());
 1015
 1016            // Values in 'decLutTwo' maps input values from 63 to 127.
 1017            //    0, 255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13
 1018            //   14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255
 1019            //  255, 255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39
 1020            //   40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255
 1021            var decLutTwo = (Vector128.Create(0x0100FF00, 0x05040302, 0x09080706, 0x0D0C0B0A).AsByte(),
 1022                             Vector128.Create(0x11100F0E, 0x15141312, 0x19181716, 0xFFFFFFFF).AsByte(),
 1023                             Vector128.Create(decoder.AdvSimdLutTwo3Uint1, 0x1F1E1D1C, 0x23222120, 0x27262524).AsByte(),
 1024                             Vector128.Create(0x2B2A2928, 0x2F2E2D2C, 0x33323130, 0xFFFFFFFF).AsByte());
 1025
 1026            T* src = srcBytes;
 1027            byte* dest = destBytes;
 1028            Vector128<byte> offset = Vector128.Create<byte>(63);
 1029
 1030            do
 1031            {
 1032                // Step 1: Load 64 bytes and de-interleave.
 1033                if (!decoder.TryLoadArmVector128x4(src, srcStart, sourceLength,
 1034                    out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> st
 1035                {
 1036                    break;
 1037                }
 1038
 1039                // Step 2: Map each valid input to its Base64 value.
 1040                // We use two look-ups to compute partial results and combine them later.
 1041
 1042                // Step 2.1: Detect valid Base64 values from the first three groups. Maps input as,
 1043                //  0 to  63 (Invalid) => 255
 1044                //  0 to  63 (Valid)   => Their Base64 equivalent
 1045                // 64 to 255           => 0
 1046
 1047                // Each input value acts as an index in the look-up table 'decLutOne'.
 1048                // e.g., for group 1: index 43 maps to 62 (Base64 '+').
 1049                // Group 4 and 5 values are out-of-range (>64), so they are mapped to zero.
 1050                // Other valid indices but invalid values are mapped to 255.
 1051                Vector128<byte> decOne1 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str1);
 1052                Vector128<byte> decOne2 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str2);
 1053                Vector128<byte> decOne3 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str3);
 1054                Vector128<byte> decOne4 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str4);
 1055
 1056                // Step 2.2: Detect valid Base64 values from groups 4 and 5. Maps input as,
 1057                //   0 to  63           => 0
 1058                //  64 to 122 (Valid)   => Their Base64 equivalent
 1059                //  64 to 122 (Invalid) => 255
 1060                // 123 to 255           => Remains unchanged
 1061
 1062                // Subtract/offset each input value by 63 so that it can be used as a valid offset.
 1063                // Subtract saturate makes values from the first three groups set to zero that are
 1064                // then mapped to zero in the subsequent look-up.
 1065                Vector128<byte> decTwo1 = AdvSimd.SubtractSaturate(str1, offset);
 1066                Vector128<byte> decTwo2 = AdvSimd.SubtractSaturate(str2, offset);
 1067                Vector128<byte> decTwo3 = AdvSimd.SubtractSaturate(str3, offset);
 1068                Vector128<byte> decTwo4 = AdvSimd.SubtractSaturate(str4, offset);
 1069
 1070                // We use VTBX to map values where out-of-range indices are unchanged.
 1071                decTwo1 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo1, decLutTwo, decTwo1);
 1072                decTwo2 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo2, decLutTwo, decTwo2);
 1073                decTwo3 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo3, decLutTwo, decTwo3);
 1074                decTwo4 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo4, decLutTwo, decTwo4);
 1075
 1076                // Step 3: Combine the partial result.
 1077                // Each look-up above maps valid values to their Base64 equivalent or zero.
 1078                // Thus the intermediate results 'decOne' and 'decTwo' could be OR-ed to get final values.
 1079                str1 = (decOne1 | decTwo1);
 1080                str2 = (decOne2 | decTwo2);
 1081                str3 = (decOne3 | decTwo3);
 1082                str4 = (decOne4 | decTwo4);
 1083
 1084                // Step 4: Detect an invalid input value.
 1085                // Invalid values < 122 are set to 255 while the ones above 122 are unchanged.
 1086                // Check for invalid input, any value larger than 63.
 1087                Vector128<byte> classified = (Vector128.GreaterThan(str1, offset)
 1088                                            | Vector128.GreaterThan(str2, offset)
 1089                                            | Vector128.GreaterThan(str3, offset)
 1090                                            | Vector128.GreaterThan(str4, offset));
 1091
 1092                // Check that all bits are zero.
 1093                if (classified != Vector128<byte>.Zero)
 1094                {
 1095                    break;
 1096                }
 1097
 1098                // Step 5: Compress four bytes into three.
 1099                Vector128<byte> res1 = ((str1 << 2) | (str2 >> 4));
 1100                Vector128<byte> res2 = ((str2 << 4) | (str3 >> 2));
 1101                Vector128<byte> res3 = ((str3 << 6) | str4);
 1102
 1103                // Step 6: Interleave and store decoded results.
 1104                AssertWrite<Vector128<byte>>(dest, destStart, destLength);
 1105                AdvSimd.Arm64.StoreVectorAndZip(dest, (res1, res2, res3));
 1106
 1107                src += 64;
 1108                dest += 48;
 1109            }
 1110            while (src <= srcEnd);
 1111
 1112            srcBytes = src;
 1113            destBytes = dest;
 1114        }
 1115
 1116        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1117        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1118        [CompExactlyDependsOn(typeof(Ssse3))]
 1119        private static unsafe void Vector128Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte*
 1120            where TBase64Decoder : IBase64Decoder<T>
 1121            where T : unmanaged
 1122        {
 1123            Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian);
 1124
 1125            // If we have Vector128 support, pick off 16 bytes at a time for as long as we can,
 1126            // but make sure that we quit before seeing any == markers at the end of the
 1127            // string. Also, because we write four zeroes at the end of the output, ensure
 1128            // that there are at least 6 valid bytes of input data remaining to close the
 1129            // gap. 16 + 2 + 6 = 24 bytes.
 1130
 1131            // The input consists of six character sets in the Base64 alphabet,
 1132            // which we need to map back to the 6-bit values they represent.
 1133            // There are three ranges, two singles, and then there's the rest.
 1134            //
 1135            //  #  From       To        Add  Characters
 1136            //  1  [43]       [62]      +19  +
 1137            //  2  [47]       [63]      +16  /
 1138            //  3  [48..57]   [52..61]   +4  0..9
 1139            //  4  [65..90]   [0..25]   -65  A..Z
 1140            //  5  [97..122]  [26..51]  -71  a..z
 1141            // (6) Everything else => invalid input
 1142
 1143            // We will use LUTS for character validation & offset computation
 1144            // Remember that 0x2X and 0x0X are the same index for _mm_shuffle_epi8,
 1145            // this allows to mask with 0x2F instead of 0x0F and thus save one constant declaration (register and/or mem
 1146
 1147            // For offsets:
 1148            // Perfect hash for lut = ((src>>4)&0x2F)+((src==0x2F)?0xFF:0x00)
 1149            // 0000 = garbage
 1150            // 0001 = /
 1151            // 0010 = +
 1152            // 0011 = 0-9
 1153            // 0100 = A-Z
 1154            // 0101 = A-Z
 1155            // 0110 = a-z
 1156            // 0111 = a-z
 1157            // 1000 >= garbage
 1158
 1159            // For validation, here's the table.
 1160            // A character is valid if and only if the AND of the 2 lookups equals 0:
 1161
 1162            // hi \ lo              0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
 1163            //      LUT             0x15 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x13 0x1A 0x1B 0x1B 0x1B 0x1A
 1164
 1165            // 0000 0X10 char        NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL   BS   HT   LF   VT   FF   CR   SO   SI
 1166            //           andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1167
 1168            // 0001 0x10 char        DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB  CAN   EM  SUB  ESC   FS   GS   RS   US
 1169            //           andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1170
 1171            // 0010 0x01 char               !    "    #    $    %    &    '    (    )    *    +    ,    -    .    /
 1172            //           andlut     0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00 0x01 0x01 0x01 0x00
 1173
 1174            // 0011 0x02 char          0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?
 1175            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x02 0x02 0x02 0x02 0x02
 1176
 1177            // 0100 0x04 char          @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    0
 1178            //           andlut     0x04 0x00 0x00 0x00 0X00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
 1179
 1180            // 0101 0x08 char          P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _
 1181            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08
 1182
 1183            // 0110 0x04 char          `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o
 1184            //           andlut     0x04 0x00 0x00 0x00 0X00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
 1185            // 0111 0X08 char          p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~
 1186            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08
 1187
 1188            // 1000 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1189            // 1001 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1190            // 1010 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1191            // 1011 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1192            // 1100 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1193            // 1101 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1194            // 1110 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1195            // 1111 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1196
 1197            // The JIT won't hoist these "constants", so help it
 1342051198            Vector128<byte> lutHi = Vector128.Create(decoder.Vector128LutHigh).AsByte();
 1342051199            Vector128<byte> lutLo = Vector128.Create(decoder.Vector128LutLow).AsByte();
 1342051200            Vector128<sbyte> lutShift = Vector128.Create(decoder.Vector128LutShift).AsSByte();
 1342051201            Vector128<sbyte> packBytesMask = Vector128.Create(0x06000102, 0x090A0405, 0x0C0D0E08, 0xffffffff).AsSByte();
 1342051202            Vector128<byte> mergeConstant0 = Vector128.Create(0x01400140).AsByte();
 1342051203            Vector128<short> mergeConstant1 = Vector128.Create(0x00011000).AsInt16();
 1342051204            Vector128<byte> one = Vector128<byte>.One;
 1342051205            Vector128<byte> mask2F = Vector128.Create(decoder.MaskSlashOrUnderscore);
 1342051206            Vector128<byte> mask8F = Vector128.Create((byte)0x8F);
 1342051207            Vector128<byte> shiftForUnderscore = Vector128.Create((byte)33);
 1342051208            T* src = srcBytes;
 1342051209            byte* dest = destBytes;
 1210
 1211            //while (remaining >= 24)
 1212            do
 1213            {
 2070231214                if (!decoder.TryLoadVector128(src, srcStart, sourceLength, out Vector128<byte> str))
 1215                {
 1216                    break;
 1217                }
 1218
 1219                // lookup
 2070231220                Vector128<byte> hiNibbles = Vector128.ShiftRightLogical(str.AsInt32(), 4).AsByte() & mask2F;
 1221
 2070231222                if (!decoder.TryDecode128Core(str, hiNibbles, mask2F, mask8F, lutLo, lutHi, lutShift, shiftForUnderscore
 1223                {
 1224                    break;
 1225                }
 1226
 1227                // in, bits, upper case are most significant bits, lower case are least significant bits
 1228                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 1229                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 1230                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 1231                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 1232
 1233                Vector128<short> merge_ab_and_bc;
 788061234                if (Ssse3.IsSupported)
 1235                {
 788061236                    merge_ab_and_bc = Ssse3.MultiplyAddAdjacent(str.AsByte(), mergeConstant0.AsSByte());
 1237                }
 1238                else if (AdvSimd.Arm64.IsSupported)
 1239                {
 1240                    Vector128<ushort> evens = AdvSimd.ShiftLeftLogicalWideningLower(AdvSimd.Arm64.UnzipEven(str, one).Ge
 1241                    Vector128<ushort> odds = AdvSimd.Arm64.TransposeOdd(str, Vector128<byte>.Zero).AsUInt16();
 1242                    merge_ab_and_bc = Vector128.Add(evens, odds).AsInt16();
 1243                }
 1244                else
 1245                {
 1246                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 01247                    ThrowUnreachableException();
 1248                    merge_ab_and_bc = default;
 1249                }
 1250                // 0000kkkk LLllllll 0000JJJJ JJjjKKKK
 1251                // 0000hhhh IIiiiiii 0000GGGG GGggHHHH
 1252                // 0000eeee FFffffff 0000DDDD DDddEEEE
 1253                // 0000bbbb CCcccccc 0000AAAA AAaaBBBB
 1254
 1255                Vector128<int> output;
 788061256                if (Ssse3.IsSupported)
 1257                {
 788061258                    output = Sse2.MultiplyAddAdjacent(merge_ab_and_bc, mergeConstant1);
 1259                }
 1260                else if (AdvSimd.Arm64.IsSupported)
 1261                {
 1262                    Vector128<int> ievens = AdvSimd.ShiftLeftLogicalWideningLower(AdvSimd.Arm64.UnzipEven(merge_ab_and_b
 1263                    Vector128<int> iodds = AdvSimd.Arm64.TransposeOdd(merge_ab_and_bc, Vector128<short>.Zero).AsInt32();
 1264                    output = Vector128.Add(ievens, iodds).AsInt32();
 1265                }
 1266                else
 1267                {
 1268                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 01269                    ThrowUnreachableException();
 1270                    output = default;
 1271                }
 1272                // 00000000 JJJJJJjj KKKKkkkk LLllllll
 1273                // 00000000 GGGGGGgg HHHHhhhh IIiiiiii
 1274                // 00000000 DDDDDDdd EEEEeeee FFffffff
 1275                // 00000000 AAAAAAaa BBBBbbbb CCcccccc
 1276
 1277                // Pack bytes together:
 788061278                str = SimdShuffle(output.AsByte(), packBytesMask.AsByte(), mask8F);
 1279                // 00000000 00000000 00000000 00000000
 1280                // LLllllll KKKKkkkk JJJJJJjj IIiiiiii
 1281                // HHHHhhhh GGGGGGgg FFffffff EEEEeeee
 1282                // DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa
 1283
 788061284                AssertWrite<Vector128<sbyte>>(dest, destStart, destLength);
 788061285                str.Store(dest);
 1286
 788061287                src += 16;
 788061288                dest += 12;
 1289            }
 788061290            while (src <= srcEnd);
 1291
 1342051292            srcBytes = src;
 1342051293            destBytes = dest;
 1342051294        }
 1295#endif
 1296
 1297        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1298        private static unsafe void WriteThreeLowOrderBytes(byte* destination, int value)
 1299        {
 16625021300            destination[0] = (byte)(value >> 16);
 16625021301            destination[1] = (byte)(value >> 8);
 16625021302            destination[2] = (byte)value;
 16625021303        }
 1304
 1305        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1306        internal static bool IsWhiteSpace(int value)
 1307        {
 44014751308            Debug.Assert(value >= 0 && value <= ushort.MaxValue);
 1309            uint charMinusLowUInt32;
 44014751310            return (int)((0xC8000100U << (short)(charMinusLowUInt32 = (ushort)(value - '\t'))) & (charMinusLowUInt32 - 3
 1311        }
 1312
 1313        internal readonly struct Base64DecoderByte : IBase64Decoder<byte>
 1314        {
 1315            // Pre-computing this table using a custom string(s_characters) and GenerateDecodingMapAndVerify (found in t
 1316            public ReadOnlySpan<sbyte> DecodingMap =>
 01317                [
 01318                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 01319                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 01320                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,         //62 is placed at index 43 (
 01321                    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,         //52-61 are placed at index 
 01322                    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
 01323                    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,         //0-25 are placed at index 6
 01324                    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
 01325                    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,         //26-51 are placed at index 
 01326                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,         // Bytes over 122 ('z') are 
 01327                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,         // Hence, padding the map wi
 01328                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 01329                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 01330                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 01331                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 01332                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 01333                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 01334                ];
 1335
 1336            public ReadOnlySpan<uint> VbmiLookup0 =>
 01337                [
 01338                    0x80808080, 0x80808080, 0x80808080, 0x80808080,
 01339                    0x80808080, 0x80808080, 0x80808080, 0x80808080,
 01340                    0x80808080, 0x80808080, 0x3e808080, 0x3f808080,
 01341                    0x37363534, 0x3b3a3938, 0x80803d3c, 0x80808080
 01342                ];
 1343
 1344            public ReadOnlySpan<uint> VbmiLookup1 =>
 01345                [
 01346                    0x02010080, 0x06050403, 0x0a090807, 0x0e0d0c0b,
 01347                    0x1211100f, 0x16151413, 0x80191817, 0x80808080,
 01348                    0x1c1b1a80, 0x201f1e1d, 0x24232221, 0x28272625,
 01349                    0x2c2b2a29, 0x302f2e2d, 0x80333231, 0x80808080
 01350                ];
 1351
 1352            public ReadOnlySpan<sbyte> Avx2LutHigh =>
 01353                [
 01354                    0x10, 0x10, 0x01, 0x02,
 01355                    0x04, 0x08, 0x04, 0x08,
 01356                    0x10, 0x10, 0x10, 0x10,
 01357                    0x10, 0x10, 0x10, 0x10,
 01358                    0x10, 0x10, 0x01, 0x02,
 01359                    0x04, 0x08, 0x04, 0x08,
 01360                    0x10, 0x10, 0x10, 0x10,
 01361                    0x10, 0x10, 0x10, 0x10
 01362                ];
 1363
 1364            public ReadOnlySpan<sbyte> Avx2LutLow =>
 01365                [
 01366                    0x15, 0x11, 0x11, 0x11,
 01367                    0x11, 0x11, 0x11, 0x11,
 01368                    0x11, 0x11, 0x13, 0x1A,
 01369                    0x1B, 0x1B, 0x1B, 0x1A,
 01370                    0x15, 0x11, 0x11, 0x11,
 01371                    0x11, 0x11, 0x11, 0x11,
 01372                    0x11, 0x11, 0x13, 0x1A,
 01373                    0x1B, 0x1B, 0x1B, 0x1A
 01374                ];
 1375
 1376            public ReadOnlySpan<sbyte> Avx2LutShift =>
 01377                [
 01378                    0, 16, 19, 4,
 01379                    -65, -65, -71, -71,
 01380                    0, 0, 0, 0,
 01381                    0, 0, 0, 0,
 01382                    0, 16, 19, 4,
 01383                    -65, -65, -71, -71,
 01384                    0, 0, 0, 0,
 01385                    0, 0, 0, 0
 01386                ];
 1387
 01388            public byte MaskSlashOrUnderscore => (byte)'/';
 1389
 01390            public ReadOnlySpan<int> Vector128LutHigh => [0x02011010, 0x08040804, 0x10101010, 0x10101010];
 1391
 01392            public ReadOnlySpan<int> Vector128LutLow => [0x11111115, 0x11111111, 0x1A131111, 0x1A1B1B1B];
 1393
 01394            public ReadOnlySpan<uint> Vector128LutShift => [0x04131000, 0xb9b9bfbf, 0x00000000, 0x00000000];
 1395
 01396            public ReadOnlySpan<uint> AdvSimdLutOne3 => [0xFFFFFFFF, 0xFFFFFFFF, 0x3EFFFFFF, 0x3FFFFFFF];
 1397
 01398            public uint AdvSimdLutTwo3Uint1 => 0x1B1AFFFF;
 1399
 01400            public int GetMaxDecodedLength(int utf8Length) => Base64.GetMaxDecodedFromUtf8Length(utf8Length);
 1401
 01402            public bool IsInvalidLength(int bufferLength) => bufferLength % 4 != 0; // only decode input if it is a mult
 1403
 01404            public bool IsValidPadding(uint padChar) => padChar == EncodingPad;
 1405
 01406            public int SrcLength(bool _, int utf8Length) => utf8Length & ~0x3;  // only decode input up to the closest m
 1407
 1408#if NET
 1409            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1410            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1411            [CompExactlyDependsOn(typeof(Ssse3))]
 1412            public bool TryDecode128Core(
 1413                Vector128<byte> str,
 1414                Vector128<byte> hiNibbles,
 1415                Vector128<byte> maskSlashOrUnderscore,
 1416                Vector128<byte> mask8F,
 1417                Vector128<byte> lutLow,
 1418                Vector128<byte> lutHigh,
 1419                Vector128<sbyte> lutShift,
 1420                Vector128<byte> _,
 1421                out Vector128<byte> result)
 1422            {
 01423                Vector128<byte> loNibbles = str & maskSlashOrUnderscore;
 01424                Vector128<byte> hi = SimdShuffle(lutHigh, hiNibbles, mask8F);
 01425                Vector128<byte> lo = SimdShuffle(lutLow, loNibbles, mask8F);
 1426
 1427                // Check for invalid input: if any "and" values from lo and hi are not zero,
 1428                // fall back on bytewise code to do error checking and reporting:
 01429                if ((lo & hi) != Vector128<byte>.Zero)
 1430                {
 01431                    result = default;
 01432                    return false;
 1433                }
 1434
 01435                Vector128<byte> eq2F = Vector128.Equals(str, maskSlashOrUnderscore);
 01436                Vector128<byte> shift = SimdShuffle(lutShift.AsByte(), (eq2F + hiNibbles), mask8F);
 1437
 1438                // Now simply add the delta values to the input:
 01439                result = str + shift;
 1440
 01441                return true;
 1442            }
 1443
 1444            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1445            [CompExactlyDependsOn(typeof(Avx2))]
 1446            public bool TryDecode256Core(
 1447                Vector256<sbyte> str,
 1448                Vector256<sbyte> hiNibbles,
 1449                Vector256<sbyte> maskSlashOrUnderscore,
 1450                Vector256<sbyte> lutLow,
 1451                Vector256<sbyte> lutHigh,
 1452                Vector256<sbyte> lutShift,
 1453                Vector256<sbyte> _,
 1454                out Vector256<sbyte> result)
 1455            {
 01456                Vector256<sbyte> loNibbles = str & maskSlashOrUnderscore;
 01457                Vector256<sbyte> hi = Avx2.Shuffle(lutHigh, hiNibbles);
 01458                Vector256<sbyte> lo = Avx2.Shuffle(lutLow, loNibbles);
 1459
 01460                if ((lo & hi) != Vector256<sbyte>.Zero)
 1461                {
 01462                    result = default;
 01463                    return false;
 1464                }
 1465
 01466                Vector256<sbyte> eq2F = Avx2.CompareEqual(str, maskSlashOrUnderscore);
 01467                Vector256<sbyte> shift = Avx2.Shuffle(lutShift, eq2F + hiNibbles);
 1468
 01469                result = str + shift;
 1470
 01471                return true;
 1472            }
 1473
 1474            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1475            public unsafe bool TryLoadVector512(byte* src, byte* srcStart, int sourceLength, out Vector512<sbyte> str)
 1476            {
 01477                AssertRead<Vector512<sbyte>>(src, srcStart, sourceLength);
 01478                str = Vector512.Load(src).AsSByte();
 01479                return true;
 1480            }
 1481
 1482            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1483            [CompExactlyDependsOn(typeof(Avx2))]
 1484            public unsafe bool TryLoadAvxVector256(byte* src, byte* srcStart, int sourceLength, out Vector256<sbyte> str
 1485            {
 1490091486                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 1490091487                str = Avx.LoadVector256(src).AsSByte();
 1490091488                return true;
 1489            }
 1490
 1491            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1492            public unsafe bool TryLoadVector128(byte* src, byte* srcStart, int sourceLength, out Vector128<byte> str)
 1493            {
 2015271494                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 2015271495                str = Vector128.LoadUnsafe(ref *src);
 2015271496                return true;
 1497            }
 1498
 1499            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1500            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1501            public unsafe bool TryLoadArmVector128x4(byte* src, byte* srcStart, int sourceLength,
 1502                out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> str4)
 1503            {
 01504                AssertRead<Vector128<byte>>(src, srcStart, sourceLength);
 01505                (str1, str2, str3, str4) = AdvSimd.Arm64.Load4xVector128AndUnzip(src);
 1506
 01507                return true;
 1508            }
 1509#endif // NET
 1510
 1511            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1512            public unsafe int DecodeFourElements(byte* source, ref sbyte decodingMap)
 1513            {
 1514                // The 'source' span expected to have at least 4 elements, and the 'decodingMap' consists 256 sbytes
 12818561515                uint t0 = source[0];
 12818561516                uint t1 = source[1];
 12818561517                uint t2 = source[2];
 12818561518                uint t3 = source[3];
 1519
 12818561520                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 12818561521                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 12818561522                int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 12818561523                int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 1524
 12818561525                i0 <<= 18;
 12818561526                i1 <<= 12;
 12818561527                i2 <<= 6;
 1528
 12818561529                i0 |= i3;
 12818561530                i1 |= i2;
 1531
 12818561532                i0 |= i1;
 12818561533                return i0;
 1534            }
 1535
 1536            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1537            public unsafe int DecodeRemaining(byte* srcEnd, ref sbyte decodingMap, long remaining, out uint t2, out uint
 1538            {
 1539                uint t0;
 1540                uint t1;
 51531541                t2 = EncodingPad;
 51531542                t3 = EncodingPad;
 1543                switch (remaining)
 1544                {
 1545                    case 2:
 10181546                        t0 = srcEnd[-2];
 10181547                        t1 = srcEnd[-1];
 10181548                        break;
 1549                    case 3:
 6421550                        t0 = srcEnd[-3];
 6421551                        t1 = srcEnd[-2];
 6421552                        t2 = srcEnd[-1];
 6421553                        break;
 1554                    case 4:
 31881555                        t0 = srcEnd[-4];
 31881556                        t1 = srcEnd[-3];
 31881557                        t2 = srcEnd[-2];
 31881558                        t3 = srcEnd[-1];
 31881559                        break;
 1560                    default:
 3051561                        return -1;
 1562                }
 1563
 48481564                int i0 = Unsafe.Add(ref decodingMap, (IntPtr)t0);
 48481565                int i1 = Unsafe.Add(ref decodingMap, (IntPtr)t1);
 1566
 48481567                i0 <<= 18;
 48481568                i1 <<= 12;
 1569
 48481570                i0 |= i1;
 48481571                return i0;
 1572            }
 1573
 1574            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1575            public int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<byte> span)
 1576            {
 9055061577                for (int i = 0; i < span.Length; i++)
 1578                {
 4523231579                    if (!IsWhiteSpace(span[i]))
 1580                    {
 1307371581                        return i;
 1582                    }
 1583                }
 1584
 4301585                return -1;
 1586            }
 1587
 1588            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1589            public OperationStatus DecodeWithWhiteSpaceBlockwiseWrapper<TBase64Decoder>(TBase64Decoder decoder, ReadOnly
 1590                Span<byte> bytes, ref int bytesConsumed, ref int bytesWritten, bool isFinalBlock = true)
 1591                where TBase64Decoder : IBase64Decoder<byte> =>
 01592                DecodeWithWhiteSpaceBlockwise(decoder, utf8, bytes, ref bytesConsumed, ref bytesWritten, isFinalBlock);
 1593        }
 1594
 1595        internal readonly struct Base64DecoderChar : IBase64Decoder<ushort>
 1596        {
 01597            public ReadOnlySpan<sbyte> DecodingMap => default(Base64DecoderByte).DecodingMap;
 1598
 01599            public ReadOnlySpan<uint> VbmiLookup0 => default(Base64DecoderByte).VbmiLookup0;
 1600
 01601            public ReadOnlySpan<uint> VbmiLookup1 => default(Base64DecoderByte).VbmiLookup1;
 1602
 01603            public ReadOnlySpan<sbyte> Avx2LutHigh => default(Base64DecoderByte).Avx2LutHigh;
 1604
 01605            public ReadOnlySpan<sbyte> Avx2LutLow => default(Base64DecoderByte).Avx2LutLow;
 1606
 01607            public ReadOnlySpan<sbyte> Avx2LutShift => default(Base64DecoderByte).Avx2LutShift;
 1608
 01609            public byte MaskSlashOrUnderscore => default(Base64DecoderByte).MaskSlashOrUnderscore;
 1610
 01611            public ReadOnlySpan<int> Vector128LutHigh => default(Base64DecoderByte).Vector128LutHigh;
 1612
 01613            public ReadOnlySpan<int> Vector128LutLow => default(Base64DecoderByte).Vector128LutLow;
 1614
 01615            public ReadOnlySpan<uint> Vector128LutShift => default(Base64DecoderByte).Vector128LutShift;
 1616
 01617            public ReadOnlySpan<uint> AdvSimdLutOne3 => default(Base64DecoderByte).AdvSimdLutOne3;
 1618
 01619            public uint AdvSimdLutTwo3Uint1 => default(Base64DecoderByte).AdvSimdLutTwo3Uint1;
 1620
 01621            public int GetMaxDecodedLength(int sourceLength) => Base64.GetMaxDecodedFromUtf8Length(sourceLength);
 1622
 01623            public bool IsInvalidLength(int bufferLength) => bufferLength % 4 != 0;
 1624
 01625            public bool IsValidPadding(uint padChar) => padChar == EncodingPad;
 1626
 01627            public int SrcLength(bool _, int sourceLength) => sourceLength & ~0x3;
 1628
 1629#if NET
 1630            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1631            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1632            [CompExactlyDependsOn(typeof(Ssse3))]
 1633            public bool TryDecode128Core(Vector128<byte> str, Vector128<byte> hiNibbles, Vector128<byte> maskSlashOrUnde
 1634                Vector128<byte> lutLow, Vector128<byte> lutHigh, Vector128<sbyte> lutShift, Vector128<byte> shiftForUnde
 01635                default(Base64DecoderByte).TryDecode128Core(str, hiNibbles, maskSlashOrUnderscore, mask8F, lutLow, lutHi
 1636
 1637            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1638            [CompExactlyDependsOn(typeof(Avx2))]
 1639            public bool TryDecode256Core(Vector256<sbyte> str, Vector256<sbyte> hiNibbles, Vector256<sbyte> maskSlashOrU
 1640                Vector256<sbyte> lutHigh, Vector256<sbyte> lutShift, Vector256<sbyte> shiftForUnderscore, out Vector256<
 01641                default(Base64DecoderByte).TryDecode256Core(str, hiNibbles, maskSlashOrUnderscore, lutLow, lutHigh, lutS
 1642
 1643            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1644            public unsafe bool TryLoadVector512(ushort* src, ushort* srcStart, int sourceLength, out Vector512<sbyte> st
 1645            {
 01646                AssertRead<Vector512<ushort>>(src, srcStart, sourceLength);
 01647                Vector512<ushort> utf16VectorLower = Vector512.Load(src);
 01648                Vector512<ushort> utf16VectorUpper = Vector512.Load(src + 32);
 01649                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1650                {
 01651                    str = default;
 01652                    return false;
 1653                }
 1654
 01655                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper).AsSByte();
 01656                return true;
 1657            }
 1658
 1659            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1660            [CompExactlyDependsOn(typeof(Avx2))]
 1661            public unsafe bool TryLoadAvxVector256(ushort* src, ushort* srcStart, int sourceLength, out Vector256<sbyte>
 1662            {
 1880981663                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 1880981664                Vector256<ushort> utf16VectorLower = Avx.LoadVector256(src);
 1880981665                Vector256<ushort> utf16VectorUpper = Avx.LoadVector256(src + 16);
 1666
 1880981667                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1668                {
 01669                    str = default;
 01670                    return false;
 1671                }
 1672
 1880981673                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper).AsSByte();
 1880981674                return true;
 1675            }
 1676
 1677            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1678            public unsafe bool TryLoadVector128(ushort* src, ushort* srcStart, int sourceLength, out Vector128<byte> str
 1679            {
 54961680                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 54961681                Vector128<ushort> utf16VectorLower = Vector128.LoadUnsafe(ref *src);
 54961682                Vector128<ushort> utf16VectorUpper = Vector128.LoadUnsafe(ref *src, 8);
 54961683                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1684                {
 01685                    str = default;
 01686                    return false;
 1687                }
 1688
 54961689                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper);
 54961690                return true;
 1691            }
 1692
 1693            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1694            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1695            public unsafe bool TryLoadArmVector128x4(ushort* src, ushort* srcStart, int sourceLength,
 1696                out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> str4)
 1697            {
 01698                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 01699                var (s11, s12, s21, s22) = AdvSimd.Arm64.Load4xVector128AndUnzip(src);
 01700                var (s31, s32, s41, s42) = AdvSimd.Arm64.Load4xVector128AndUnzip(src + 32);
 1701
 01702                if (Ascii.VectorContainsNonAsciiChar(s11 | s12 | s21 | s22 | s31 | s32 | s41 | s42))
 1703                {
 01704                    str1 = str2 = str3 = str4 = default;
 01705                    return false;
 1706                }
 1707
 01708                str1 = Ascii.ExtractAsciiVector(s11, s31);
 01709                str2 = Ascii.ExtractAsciiVector(s12, s32);
 01710                str3 = Ascii.ExtractAsciiVector(s21, s41);
 01711                str4 = Ascii.ExtractAsciiVector(s22, s42);
 1712
 01713                return true;
 1714            }
 1715#endif // NET
 1716
 1717            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1718            public unsafe int DecodeFourElements(ushort* source, ref sbyte decodingMap)
 1719            {
 1720                // The 'source' span expected to have at least 4 elements, and the 'decodingMap' consists 256 sbytes
 211011721                uint t0 = source[0];
 211011722                uint t1 = source[1];
 211011723                uint t2 = source[2];
 211011724                uint t3 = source[3];
 1725
 211011726                if (((t0 | t1 | t2 | t3) & 0xffffff00) != 0)
 1727                {
 01728                    return -1; // One or more chars falls outside the 00..ff range, invalid Base64 character.
 1729                }
 1730
 211011731                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 211011732                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 211011733                int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 211011734                int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 1735
 211011736                i0 <<= 18;
 211011737                i1 <<= 12;
 211011738                i2 <<= 6;
 1739
 211011740                i0 |= i3;
 211011741                i1 |= i2;
 1742
 211011743                i0 |= i1;
 211011744                return i0;
 1745            }
 1746
 1747            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1748            public unsafe int DecodeRemaining(ushort* srcEnd, ref sbyte decodingMap, long remaining, out uint t2, out ui
 1749            {
 1750                uint t0;
 1751                uint t1;
 33261752                t2 = EncodingPad;
 33261753                t3 = EncodingPad;
 1754                switch (remaining)
 1755                {
 1756                    case 2:
 11851757                        t0 = srcEnd[-2];
 11851758                        t1 = srcEnd[-1];
 11851759                        break;
 1760                    case 3:
 10521761                        t0 = srcEnd[-3];
 10521762                        t1 = srcEnd[-2];
 10521763                        t2 = srcEnd[-1];
 10521764                        break;
 1765                    case 4:
 10891766                        t0 = srcEnd[-4];
 10891767                        t1 = srcEnd[-3];
 10891768                        t2 = srcEnd[-2];
 10891769                        t3 = srcEnd[-1];
 10891770                        break;
 1771                    default:
 01772                        return -1;
 1773                }
 1774
 33261775                if (((t0 | t1 | t2 | t3) & 0xffffff00) != 0)
 1776                {
 01777                    return -1;
 1778                }
 1779
 33261780                int i0 = Unsafe.Add(ref decodingMap, (IntPtr)t0);
 33261781                int i1 = Unsafe.Add(ref decodingMap, (IntPtr)t1);
 1782
 33261783                i0 <<= 18;
 33261784                i1 <<= 12;
 1785
 33261786                i0 |= i1;
 33261787                return i0;
 1788            }
 1789
 1790            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1791            public int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<ushort> span)
 1792            {
 01793                for (int i = 0; i < span.Length; i++)
 1794                {
 01795                    if (!IsWhiteSpace(span[i]))
 1796                    {
 01797                        return i;
 1798                    }
 1799                }
 1800
 01801                return -1;
 1802            }
 1803
 1804            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1805            public OperationStatus DecodeWithWhiteSpaceBlockwiseWrapper<TBase64Decoder>(TBase64Decoder decoder, ReadOnly
 1806                Span<byte> bytes, ref int bytesConsumed, ref int bytesWritten, bool isFinalBlock = true) where TBase64De
 01807                DecodeWithWhiteSpaceBlockwise(default(Base64DecoderChar), source, bytes, ref bytesConsumed, ref bytesWri
 1808        }
 1809    }
 1810}

C:\h\w\9FD50923\w\C0A20A61\e\runtime-utils\Runner\runtime\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.Runtime.CompilerServices;
 5using System.Runtime.InteropServices;
 6#if NET
 7using System.Runtime.Intrinsics;
 8using System.Runtime.Intrinsics.Arm;
 9using System.Runtime.Intrinsics.X86;
 10#endif
 11
 12namespace System.Buffers.Text
 13{
 14    // AVX2 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arch/avx
 15    // Vector128 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arc
 16    internal static partial class Base64Helper
 17    {
 18        internal static unsafe OperationStatus EncodeTo<TBase64Encoder, T>(TBase64Encoder encoder, ReadOnlySpan<byte> so
 19            Span<T> destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true)
 20            where TBase64Encoder : IBase64Encoder<T>
 21            where T : unmanaged
 22        {
 23            if (source.IsEmpty)
 24            {
 025                bytesConsumed = 0;
 026                bytesWritten = 0;
 027                return OperationStatus.Done;
 28            }
 29
 997830            fixed (byte* srcBytes = &MemoryMarshal.GetReference(source))
 997831            fixed (T* destBytes = &MemoryMarshal.GetReference(destination))
 32            {
 997833                int srcLength = source.Length;
 997834                int destLength = destination.Length;
 997835                int maxSrcLength = encoder.GetMaxSrcLength(srcLength, destLength);
 36
 997837                byte* src = srcBytes;
 997838                T* dest = destBytes;
 997839                byte* srcEnd = srcBytes + (uint)srcLength;
 997840                byte* srcMax = srcBytes + (uint)maxSrcLength;
 41
 42#if NET
 997843                if (maxSrcLength >= 16)
 44                {
 886245                    byte* end = srcMax - 64;
 886246                    if (Vector512.IsHardwareAccelerated && Avx512Vbmi.IsSupported && (end >= src))
 47                    {
 048                        Avx512Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 49
 050                        if (src == srcEnd)
 51                            goto DoneExit;
 52                    }
 53
 886254                    end = srcMax - 32;
 886255                    if (Avx2.IsSupported && (end >= src))
 56                    {
 814857                        Avx2Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 58
 814859                        if (src == srcEnd)
 60                            goto DoneExit;
 61                    }
 62
 886263                    end = srcMax - 48;
 64                    if (AdvSimd.Arm64.IsSupported && (end >= src))
 65                    {
 66                        AdvSimdEncode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 67
 68                        if (src == srcEnd)
 69                            goto DoneExit;
 70                    }
 71
 886272                    end = srcMax - 16;
 886273                    if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian && (end >= src))
 74                    {
 465675                        Vector128Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 76
 465677                        if (src == srcEnd)
 78                            goto DoneExit;
 79                    }
 80                }
 81#endif
 997882                ref byte encodingMap = ref MemoryMarshal.GetReference(encoder.EncodingMap);
 83
 997884                srcMax -= 2;
 3671185                while (src < srcMax)
 86                {
 2673387                    encoder.EncodeThreeAndWrite(src, dest, ref encodingMap);
 2673388                    src += 3;
 2673389                    dest += 4;
 90                }
 91
 997892                if (srcMax + 2 != srcEnd)
 93                    goto DestinationTooSmallExit;
 94
 997895                if (!isFinalBlock)
 96                {
 332697                    if (src == srcEnd)
 108998                        goto DoneExit;
 99
 100                    goto NeedMoreData;
 101                }
 102
 6652103                if (src + 1 == srcEnd)
 104                {
 2370105                    encoder.EncodeOneOptionallyPadTwo(src, dest, ref encodingMap);
 2370106                    src += 1;
 2370107                    dest += encoder.IncrementPadTwo;
 108                }
 4282109                else if (src + 2 == srcEnd)
 110                {
 2104111                    encoder.EncodeTwoOptionallyPadOne(src, dest, ref encodingMap);
 2104112                    src += 2;
 2104113                    dest += encoder.IncrementPadOne;
 114                }
 115
 116            DoneExit:
 7741117                bytesConsumed = (int)(src - srcBytes);
 7741118                bytesWritten = (int)(dest - destBytes);
 7741119                return OperationStatus.Done;
 120
 121            DestinationTooSmallExit:
 0122                bytesConsumed = (int)(src - srcBytes);
 0123                bytesWritten = (int)(dest - destBytes);
 0124                return OperationStatus.DestinationTooSmall;
 125
 126            NeedMoreData:
 2237127                bytesConsumed = (int)(src - srcBytes);
 2237128                bytesWritten = (int)(dest - destBytes);
 2237129                return OperationStatus.NeedMoreData;
 130            }
 131        }
 132
 133#if NET
 134        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 135        [CompExactlyDependsOn(typeof(Avx512BW))]
 136        [CompExactlyDependsOn(typeof(Avx512Vbmi))]
 137        private static unsafe void Avx512Encode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T* de
 138            where TBase64Encoder : IBase64Encoder<T>
 139            where T : unmanaged
 140        {
 141            // Reference for VBMI implementation : https://github.com/WojciechMula/base64simd/tree/master/encode
 142            // If we have AVX512 support, pick off 48 bytes at a time for as long as we can.
 143            // But because we read 64 bytes at a time, ensure we have enough room to do a
 144            // full 64-byte read without segfaulting.
 145
 0146            byte* src = srcBytes;
 0147            T* dest = destBytes;
 148
 149            // The JIT won't hoist these "constants", so help it
 0150            Vector512<sbyte> shuffleVecVbmi = Vector512.Create(
 0151                0x01020001, 0x04050304, 0x07080607, 0x0a0b090a,
 0152                0x0d0e0c0d, 0x10110f10, 0x13141213, 0x16171516,
 0153                0x191a1819, 0x1c1d1b1c, 0x1f201e1f, 0x22232122,
 0154                0x25262425, 0x28292728, 0x2b2c2a2b, 0x2e2f2d2e).AsSByte();
 0155            Vector512<sbyte> vbmiLookup = Vector512.Create(encoder.EncodingMap).AsSByte();
 156
 0157            Vector512<ushort> maskAC = Vector512.Create((uint)0x0fc0fc00).AsUInt16();
 0158            Vector512<uint> maskBB = Vector512.Create((uint)0x3f003f00);
 0159            Vector512<ushort> shiftAC = Vector512.Create((uint)0x0006000a).AsUInt16();
 0160            Vector512<ushort> shiftBB = Vector512.Create((uint)0x00080004).AsUInt16();
 161
 0162            AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 163
 164            // This algorithm requires AVX512VBMI support.
 165            // Vbmi was first introduced in CannonLake and is available from IceLake on.
 166
 167            // str = [...|PONM|LKJI|HGFE|DCBA]
 0168            Vector512<sbyte> str = Vector512.Load(src).AsSByte();
 169
 0170            while (true)
 171            {
 172                // Step 1 : Split 48 bytes into 64 bytes with each byte using 6-bits from input
 173                // str = [...|KLJK|HIGH|EFDE|BCAB]
 0174                str = Avx512Vbmi.PermuteVar64x8(str, shuffleVecVbmi);
 175
 176                // TO-DO- This can be achieved faster with multishift
 177                // Consider the first 4 bytes - BCAB
 178                // temp1    = [...|0000cccc|cc000000|aaaaaa00|00000000]
 0179                Vector512<ushort> temp1 = (str.AsUInt16() & maskAC);
 180
 181                // temp2    = [...|00000000|00cccccc|00000000|00aaaaaa]
 0182                Vector512<ushort> temp2 = Avx512BW.ShiftRightLogicalVariable(temp1, shiftAC).AsUInt16();
 183
 184                // temp3    = [...|ccdddddd|00000000|aabbbbbb|cccc0000]
 0185                Vector512<ushort> temp3 = Avx512BW.ShiftLeftLogicalVariable(str.AsUInt16(), shiftBB).AsUInt16();
 186
 187                // str      = [...|00dddddd|00cccccc|00bbbbbb|00aaaaaa]
 0188                str = Vector512.ConditionalSelect(maskBB, temp3.AsUInt32(), temp2.AsUInt32()).AsSByte();
 189
 190                // Step 2: Now we have the indices calculated. Next step is to use these indices to translate.
 0191                str = Avx512Vbmi.PermuteVar64x8(vbmiLookup, str);
 192
 0193                encoder.StoreVector512ToDestination(dest, destStart, destLength, str.AsByte());
 194
 0195                src += 48;
 0196                dest += 64;
 197
 0198                if (src > srcEnd)
 199                    break;
 200
 0201                AssertRead<Vector512<sbyte>>(src, srcStart, sourceLength);
 0202                str = Vector512.Load(src).AsSByte();
 203            }
 204
 0205            srcBytes = src;
 0206            destBytes = dest;
 0207        }
 208
 209        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 210        [CompExactlyDependsOn(typeof(Avx2))]
 211        private static unsafe void Avx2Encode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T* dest
 212            where TBase64Encoder : IBase64Encoder<T>
 213            where T : unmanaged
 214        {
 215            // If we have AVX2 support, pick off 24 bytes at a time for as long as we can.
 216            // But because we read 32 bytes at a time, ensure we have enough room to do a
 217            // full 32-byte read without segfaulting.
 218
 219            // translation from SSSE3 into AVX2 of procedure
 220            // This one works with shifted (4 bytes) input in order to
 221            // be able to work efficiently in the 2 128-bit lanes
 222
 223            // srcBytes, bytes MSB to LSB:
 224            // 0 0 0 0 x w v u t s r q p o n m
 225            // l k j i h g f e d c b a 0 0 0 0
 226
 227            // The JIT won't hoist these "constants", so help it
 8148228            Vector256<sbyte> shuffleVec = Vector256.Create(
 8148229                5, 4, 6, 5,
 8148230                8, 7, 9, 8,
 8148231                11, 10, 12, 11,
 8148232                14, 13, 15, 14,
 8148233                1, 0, 2, 1,
 8148234                4, 3, 5, 4,
 8148235                7, 6, 8, 7,
 8148236                10, 9, 11, 10);
 237
 8148238            Vector256<sbyte> lut = Vector256.Create(
 8148239                65, 71, -4, -4,
 8148240                -4, -4, -4, -4,
 8148241                -4, -4, -4, -4,
 8148242                encoder.Avx2LutChar62, encoder.Avx2LutChar63, 0, 0,
 8148243                65, 71, -4, -4,
 8148244                -4, -4, -4, -4,
 8148245                -4, -4, -4, -4,
 8148246                encoder.Avx2LutChar62, encoder.Avx2LutChar63, 0, 0);
 247
 8148248            Vector256<sbyte> maskAC = Vector256.Create(0x0fc0fc00).AsSByte();
 8148249            Vector256<sbyte> maskBB = Vector256.Create(0x003f03f0).AsSByte();
 8148250            Vector256<ushort> shiftAC = Vector256.Create(0x04000040).AsUInt16();
 8148251            Vector256<short> shiftBB = Vector256.Create(0x01000010).AsInt16();
 8148252            Vector256<byte> const51 = Vector256.Create((byte)51);
 8148253            Vector256<sbyte> const25 = Vector256.Create((sbyte)25);
 254
 8148255            byte* src = srcBytes;
 8148256            T* dest = destBytes;
 257
 258            // first load is done at c-0 not to get a segfault
 8148259            AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 8148260            Vector256<sbyte> str = Avx.LoadVector256(src).AsSByte();
 261
 262            // shift by 4 bytes, as required by Reshuffle
 8148263            str = Avx2.PermuteVar8x32(str.AsInt32(), Vector256.Create(
 8148264                0, 0, 0, 0,
 8148265                0, 0, 0, 0,
 8148266                1, 0, 0, 0,
 8148267                2, 0, 0, 0,
 8148268                3, 0, 0, 0,
 8148269                4, 0, 0, 0,
 8148270                5, 0, 0, 0,
 8148271                6, 0, 0, 0).AsInt32()).AsSByte();
 272
 273            // Next loads are done at src-4, as required by Reshuffle, so shift it once
 8148274            src -= 4;
 275
 276546276            while (true)
 277            {
 278                // Reshuffle
 284694279                str = Avx2.Shuffle(str, shuffleVec);
 280                // str, bytes MSB to LSB:
 281                // w x v w
 282                // t u s t
 283                // q r p q
 284                // n o m n
 285                // k l j k
 286                // h i g h
 287                // e f d e
 288                // b c a b
 289
 284694290                Vector256<sbyte> t0 = str & maskAC;
 291                // bits, upper case are most significant bits, lower case are least significant bits.
 292                // 0000wwww XX000000 VVVVVV00 00000000
 293                // 0000tttt UU000000 SSSSSS00 00000000
 294                // 0000qqqq RR000000 PPPPPP00 00000000
 295                // 0000nnnn OO000000 MMMMMM00 00000000
 296                // 0000kkkk LL000000 JJJJJJ00 00000000
 297                // 0000hhhh II000000 GGGGGG00 00000000
 298                // 0000eeee FF000000 DDDDDD00 00000000
 299                // 0000bbbb CC000000 AAAAAA00 00000000
 300
 284694301                Vector256<sbyte> t2 = str & maskBB;
 302                // 00000000 00xxxxxx 000000vv WWWW0000
 303                // 00000000 00uuuuuu 000000ss TTTT0000
 304                // 00000000 00rrrrrr 000000pp QQQQ0000
 305                // 00000000 00oooooo 000000mm NNNN0000
 306                // 00000000 00llllll 000000jj KKKK0000
 307                // 00000000 00iiiiii 000000gg HHHH0000
 308                // 00000000 00ffffff 000000dd EEEE0000
 309                // 00000000 00cccccc 000000aa BBBB0000
 310
 284694311                Vector256<ushort> t1 = Avx2.MultiplyHigh(t0.AsUInt16(), shiftAC);
 312                // 00000000 00wwwwXX 00000000 00VVVVVV
 313                // 00000000 00ttttUU 00000000 00SSSSSS
 314                // 00000000 00qqqqRR 00000000 00PPPPPP
 315                // 00000000 00nnnnOO 00000000 00MMMMMM
 316                // 00000000 00kkkkLL 00000000 00JJJJJJ
 317                // 00000000 00hhhhII 00000000 00GGGGGG
 318                // 00000000 00eeeeFF 00000000 00DDDDDD
 319                // 00000000 00bbbbCC 00000000 00AAAAAA
 320
 284694321                Vector256<short> t3 = t2.AsInt16() * shiftBB;
 322                // 00xxxxxx 00000000 00vvWWWW 00000000
 323                // 00uuuuuu 00000000 00ssTTTT 00000000
 324                // 00rrrrrr 00000000 00ppQQQQ 00000000
 325                // 00oooooo 00000000 00mmNNNN 00000000
 326                // 00llllll 00000000 00jjKKKK 00000000
 327                // 00iiiiii 00000000 00ggHHHH 00000000
 328                // 00ffffff 00000000 00ddEEEE 00000000
 329                // 00cccccc 00000000 00aaBBBB 00000000
 330
 284694331                str = t1.AsSByte() | t3.AsSByte();
 332                // 00xxxxxx 00wwwwXX 00vvWWWW 00VVVVVV
 333                // 00uuuuuu 00ttttUU 00ssTTTT 00SSSSSS
 334                // 00rrrrrr 00qqqqRR 00ppQQQQ 00PPPPPP
 335                // 00oooooo 00nnnnOO 00mmNNNN 00MMMMMM
 336                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 337                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 338                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 339                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 340
 341                // Translation
 342                // LUT contains Absolute offset for all ranges:
 343                // Translate values 0..63 to the Base64 alphabet. There are five sets:
 344                // #  From      To         Abs    Index  Characters
 345                // 0  [0..25]   [65..90]   +65        0  ABCDEFGHIJKLMNOPQRSTUVWXYZ
 346                // 1  [26..51]  [97..122]  +71        1  abcdefghijklmnopqrstuvwxyz
 347                // 2  [52..61]  [48..57]    -4  [2..11]  0123456789
 348                // 3  [62]      [43]       -19       12  +
 349                // 4  [63]      [47]       -16       13  /
 350
 351                // Create LUT indices from input:
 352                // the index for range #0 is right, others are 1 less than expected:
 284694353                Vector256<byte> indices = Avx2.SubtractSaturate(str.AsByte(), const51);
 354
 355                // mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0:
 284694356                Vector256<sbyte> mask = Avx2.CompareGreaterThan(str, const25);
 357
 358                // subtract -1, so add 1 to indices for range #[1..4], All indices are now correct:
 284694359                Vector256<sbyte> tmp = indices.AsSByte() - mask;
 360
 361                // Add offsets to input values:
 284694362                str += Avx2.Shuffle(lut, tmp);
 363
 284694364                encoder.StoreVector256ToDestination(dest, destStart, destLength, str.AsByte());
 365
 284694366                src += 24;
 284694367                dest += 32;
 368
 284694369                if (src > srcEnd)
 370                    break;
 371
 372                // Load at src-4, as required by Reshuffle (already shifted by -4)
 276546373                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 276546374                str = Avx.LoadVector256(src).AsSByte();
 375            }
 376
 8148377            srcBytes = src + 4;
 8148378            destBytes = dest;
 8148379        }
 380
 381        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 382        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 383        private static unsafe void AdvSimdEncode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T* d
 384            where TBase64Encoder : IBase64Encoder<T>
 385            where T : unmanaged
 386        {
 387            // C# implementation of https://github.com/aklomp/base64/blob/3a5add8652076612a8407627a42c768736a4263f/lib/a
 388            Vector128<byte> str1;
 389            Vector128<byte> str2;
 390            Vector128<byte> str3;
 391            Vector128<byte> res1;
 392            Vector128<byte> res2;
 393            Vector128<byte> res3;
 394            Vector128<byte> res4;
 395            Vector128<byte> tblEnc1 = Vector128.Create("ABCDEFGHIJKLMNOP"u8).AsByte();
 396            Vector128<byte> tblEnc2 = Vector128.Create("QRSTUVWXYZabcdef"u8).AsByte();
 397            Vector128<byte> tblEnc3 = Vector128.Create("ghijklmnopqrstuv"u8).AsByte();
 398            Vector128<byte> tblEnc4 = Vector128.Create(encoder.AdvSimdLut4).AsByte();
 399            byte* src = srcBytes;
 400            T* dest = destBytes;
 401
 402            // If we have Neon support, pick off 48 bytes at a time for as long as we can.
 403            do
 404            {
 405                // Load 48 bytes and deinterleave:
 406                AssertRead<Vector128<byte>>(src, srcStart, sourceLength);
 407                (str1, str2, str3) = AdvSimd.Arm64.Load3xVector128AndUnzip(src);
 408
 409                // Divide bits of three input bytes over four output bytes:
 410                res1 = str1 >>> 2;
 411                res2 = str2 >>> 4;
 412                res3 = str3 >>> 6;
 413                res2 = AdvSimd.ShiftLeftAndInsert(res2, str1, 4);
 414                res3 = AdvSimd.ShiftLeftAndInsert(res3, str2, 2);
 415
 416                // Clear top two bits:
 417                res2 &= AdvSimd.DuplicateToVector128((byte)0x3F);
 418                res3 &= AdvSimd.DuplicateToVector128((byte)0x3F);
 419                res4 = str3 & AdvSimd.DuplicateToVector128((byte)0x3F);
 420
 421                // The bits have now been shifted to the right locations;
 422                // translate their values 0..63 to the Base64 alphabet.
 423                // Use a 64-byte table lookup:
 424                res1 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res1);
 425                res2 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res2);
 426                res3 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res3);
 427                res4 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res4);
 428
 429                // Interleave and store result:
 430                encoder.StoreArmVector128x4ToDestination(dest, destStart, destLength, res1, res2, res3, res4);
 431
 432                src += 48;
 433                dest += 64;
 434            } while (src <= srcEnd);
 435
 436            srcBytes = src;
 437            destBytes = dest;
 438        }
 439
 440        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 441        [CompExactlyDependsOn(typeof(Ssse3))]
 442        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 443        private static unsafe void Vector128Encode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T*
 444            where TBase64Encoder : IBase64Encoder<T>
 445            where T : unmanaged
 446        {
 447            // If we have SSSE3 support, pick off 12 bytes at a time for as long as we can.
 448            // But because we read 16 bytes at a time, ensure we have enough room to do a
 449            // full 16-byte read without segfaulting.
 450
 451            // srcBytes, bytes MSB to LSB:
 452            // 0 0 0 0 l k j i h g f e d c b a
 453
 454            // The JIT won't hoist these "constants", so help it
 455            Vector128<byte> shuffleVec = Vector128.Create(0x01020001, 0x04050304, 0x07080607, 0x0A0B090A).AsByte();
 4656456            Vector128<byte> lut = Vector128.Create(0xFCFC4741, 0xFCFCFCFC, 0xFCFCFCFC, encoder.Ssse3AdvSimdLutE3).AsByte
 4656457            Vector128<byte> maskAC = Vector128.Create(0x0fc0fc00).AsByte();
 4656458            Vector128<byte> maskBB = Vector128.Create(0x003f03f0).AsByte();
 4656459            Vector128<ushort> shiftAC = Vector128.Create(0x04000040).AsUInt16();
 4656460            Vector128<short> shiftBB = Vector128.Create(0x01000010).AsInt16();
 4656461            Vector128<byte> const51 = Vector128.Create((byte)51);
 4656462            Vector128<sbyte> const25 = Vector128.Create((sbyte)25);
 4656463            Vector128<byte> mask8F = Vector128.Create((byte)0x8F);
 464
 4656465            byte* src = srcBytes;
 4656466            T* dest = destBytes;
 467
 468            //while (remaining >= 16)
 469            do
 470            {
 4788471                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 4788472                Vector128<byte> str = Vector128.LoadUnsafe(ref *src);
 473
 474                // Reshuffle
 4788475                str = SimdShuffle(str, shuffleVec, mask8F);
 476                // str, bytes MSB to LSB:
 477                // k l j k
 478                // h i g h
 479                // e f d e
 480                // b c a b
 481
 4788482                Vector128<byte> t0 = str & maskAC;
 483                // bits, upper case are most significant bits, lower case are least significant bits
 484                // 0000kkkk LL000000 JJJJJJ00 00000000
 485                // 0000hhhh II000000 GGGGGG00 00000000
 486                // 0000eeee FF000000 DDDDDD00 00000000
 487                // 0000bbbb CC000000 AAAAAA00 00000000
 488
 4788489                Vector128<byte> t2 = str & maskBB;
 490                // 00000000 00llllll 000000jj KKKK0000
 491                // 00000000 00iiiiii 000000gg HHHH0000
 492                // 00000000 00ffffff 000000dd EEEE0000
 493                // 00000000 00cccccc 000000aa BBBB0000
 494
 495                Vector128<ushort> t1;
 4788496                if (Ssse3.IsSupported)
 497                {
 4788498                    t1 = Sse2.MultiplyHigh(t0.AsUInt16(), shiftAC);
 499                }
 500                else if (AdvSimd.Arm64.IsSupported)
 501                {
 502                    Vector128<ushort> odd = Vector128.ShiftRightLogical(AdvSimd.Arm64.UnzipOdd(t0.AsUInt16(), t0.AsUInt1
 503                    Vector128<ushort> even = Vector128.ShiftRightLogical(AdvSimd.Arm64.UnzipEven(t0.AsUInt16(), t0.AsUIn
 504                    t1 = AdvSimd.Arm64.ZipLow(even, odd);
 505                }
 506                else
 507                {
 508                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 0509                    ThrowUnreachableException();
 510                    t1 = default;
 511                }
 512                // 00000000 00kkkkLL 00000000 00JJJJJJ
 513                // 00000000 00hhhhII 00000000 00GGGGGG
 514                // 00000000 00eeeeFF 00000000 00DDDDDD
 515                // 00000000 00bbbbCC 00000000 00AAAAAA
 516
 4788517                Vector128<short> t3 = t2.AsInt16() * shiftBB;
 518                // 00llllll 00000000 00jjKKKK 00000000
 519                // 00iiiiii 00000000 00ggHHHH 00000000
 520                // 00ffffff 00000000 00ddEEEE 00000000
 521                // 00cccccc 00000000 00aaBBBB 00000000
 522
 4788523                str = t1.AsByte() | t3.AsByte();
 524                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 525                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 526                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 527                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 528
 529                // Translation
 530                // LUT contains Absolute offset for all ranges:
 531                // Translate values 0..63 to the Base64 alphabet. There are five sets:
 532                // #  From      To         Abs    Index  Characters
 533                // 0  [0..25]   [65..90]   +65        0  ABCDEFGHIJKLMNOPQRSTUVWXYZ
 534                // 1  [26..51]  [97..122]  +71        1  abcdefghijklmnopqrstuvwxyz
 535                // 2  [52..61]  [48..57]    -4  [2..11]  0123456789
 536                // 3  [62]      [43]       -19       12  +
 537                // 4  [63]      [47]       -16       13  /
 538
 539                // Create LUT indices from input:
 540                // the index for range #0 is right, others are 1 less than expected:
 541                Vector128<byte> indices;
 4788542                if (Ssse3.IsSupported)
 543                {
 4788544                    indices = Sse2.SubtractSaturate(str.AsByte(), const51);
 545                }
 546                else if (AdvSimd.IsSupported)
 547                {
 548                    indices = AdvSimd.SubtractSaturate(str.AsByte(), const51);
 549                }
 550                else
 551                {
 552                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 0553                    ThrowUnreachableException();
 554                    indices = default;
 555                }
 556
 557                // mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0:
 4788558                Vector128<sbyte> mask = Vector128.GreaterThan(str.AsSByte(), const25);
 559
 560                // subtract -1, so add 1 to indices for range #[1..4], All indices are now correct:
 4788561                Vector128<sbyte> tmp = indices.AsSByte() - mask;
 562
 563                // Add offsets to input values:
 4788564                str += SimdShuffle(lut, tmp.AsByte(), mask8F);
 565
 4788566                encoder.StoreVector128ToDestination(dest, destStart, destLength, str);
 567
 4788568                src += 12;
 4788569                dest += 16;
 570            }
 4788571            while (src <= srcEnd);
 572
 4656573            srcBytes = src;
 4656574            destBytes = dest;
 4656575        }
 576#endif
 577
 578        internal static unsafe OperationStatus EncodeToUtf8InPlace<TBase64Encoder>(TBase64Encoder encoder, Span<byte> bu
 579            where TBase64Encoder : IBase64Encoder<byte>
 580        {
 3326581            if (buffer.IsEmpty)
 582            {
 0583                bytesWritten = 0;
 0584                return OperationStatus.Done;
 585            }
 586
 3326587            fixed (byte* bufferBytes = &MemoryMarshal.GetReference(buffer))
 588            {
 3326589                int encodedLength = encoder.GetMaxEncodedLength(dataLength);
 3326590                if (buffer.Length < encodedLength)
 591                {
 0592                    bytesWritten = 0;
 0593                    return OperationStatus.DestinationTooSmall;
 594                }
 595
 3326596                int leftover = (int)((uint)dataLength % 3); // how many bytes after packs of 3
 597
 3326598                uint destinationIndex = encoder.GetInPlaceDestinationLength(encodedLength, leftover);
 3326599                uint sourceIndex = (uint)(dataLength - leftover);
 3326600                ref byte encodingMap = ref MemoryMarshal.GetReference(encoder.EncodingMap);
 601
 602                // encode last pack to avoid conditional in the main loop
 3326603                if (leftover != 0)
 604                {
 2237605                    if (leftover == 1)
 606                    {
 1185607                        encoder.EncodeOneOptionallyPadTwo(bufferBytes + sourceIndex, bufferBytes + destinationIndex, ref
 608                    }
 609                    else
 610                    {
 1052611                        encoder.EncodeTwoOptionallyPadOne(bufferBytes + sourceIndex, bufferBytes + destinationIndex, ref
 612                    }
 613
 2237614                    destinationIndex -= 4;
 615                }
 616
 3326617                sourceIndex -= 3;
 777805618                while ((int)sourceIndex >= 0)
 619                {
 774479620                    uint result = Encode(bufferBytes + sourceIndex, ref encodingMap);
 774479621                    Unsafe.WriteUnaligned(bufferBytes + destinationIndex, result);
 774479622                    destinationIndex -= 4;
 774479623                    sourceIndex -= 3;
 624                }
 625
 3326626                bytesWritten = encodedLength;
 3326627                return OperationStatus.Done;
 628            }
 629        }
 630
 631        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 632        private static unsafe uint Encode(byte* threeBytes, ref byte encodingMap)
 633        {
 774479634            uint t0 = threeBytes[0];
 774479635            uint t1 = threeBytes[1];
 774479636            uint t2 = threeBytes[2];
 637
 774479638            uint i = (t0 << 16) | (t1 << 8) | t2;
 639
 774479640            uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 774479641            uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 774479642            uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 774479643            uint i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F));
 644
 774479645            return ConstructResult(i0, i1, i2, i3);
 646        }
 647
 648        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 649        private static uint ConstructResult(uint i0, uint i1, uint i2, uint i3)
 650        {
 651            if (BitConverter.IsLittleEndian)
 652            {
 774479653                return i0 | (i1 << 8) | (i2 << 16) | (i3 << 24);
 654            }
 655            else
 656            {
 657                return (i0 << 24) | (i1 << 16) | (i2 << 8) | i3;
 658            }
 659        }
 660
 661        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 662        public static unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, ushort* dest, ref byte encodingMap)
 663        {
 664            uint t0 = oneByte[0];
 665
 2370666            uint i = t0 << 8;
 667
 2370668            uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10));
 2370669            uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F));
 670
 671            uint result;
 672
 2370673            if (BitConverter.IsLittleEndian)
 674            {
 2370675                result = (i0 | (i1 << 16));
 676            }
 677            else
 678            {
 679                result = ((i0 << 16) | i1);
 680            }
 681
 2370682            Unsafe.WriteUnaligned(dest, result);
 2370683        }
 684
 685        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 686        public static unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, ushort* dest, ref byte encodingMap)
 687        {
 2104688            uint t0 = twoBytes[0];
 2104689            uint t1 = twoBytes[1];
 690
 2104691            uint i = (t0 << 16) | (t1 << 8);
 692
 2104693            ushort i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 2104694            ushort i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 2104695            ushort i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 696
 2104697            dest[0] = i0;
 2104698            dest[1] = i1;
 2104699            dest[2] = i2;
 2104700        }
 701
 702        internal const uint EncodingPad = '='; // '=', for padding
 703
 704        internal const int MaximumEncodeLength = (int.MaxValue / 4) * 3; // 1610612733
 705
 706        internal readonly struct Base64EncoderByte : IBase64Encoder<byte>
 707        {
 0708            public ReadOnlySpan<byte> EncodingMap => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"u
 709
 0710            public sbyte Avx2LutChar62 => -19;  // char '+' diff
 711
 0712            public sbyte Avx2LutChar63 => -16;   // char '/' diff
 713
 0714            public ReadOnlySpan<byte> AdvSimdLut4 => "wxyz0123456789+/"u8;
 715
 0716            public uint Ssse3AdvSimdLutE3 => 0x0000F0ED;
 717
 0718            public int IncrementPadTwo => 4;
 719
 0720            public int IncrementPadOne => 4;
 721
 722            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 723            public int GetMaxSrcLength(int srcLength, int destLength) =>
 0724                srcLength <= MaximumEncodeLength && destLength >= Base64.GetMaxEncodedToUtf8Length(srcLength) ?
 0725                srcLength : (destLength >> 2) * 3;
 726
 0727            public uint GetInPlaceDestinationLength(int encodedLength, int _) => (uint)(encodedLength - 4);
 728
 0729            public int GetMaxEncodedLength(int srcLength) => Base64.GetMaxEncodedToUtf8Length(srcLength);
 730
 731            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 732            public unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, byte* dest, ref byte encodingMap)
 733            {
 0734                uint t0 = oneByte[0];
 735
 0736                uint i = t0 << 8;
 737
 0738                uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10));
 0739                uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F));
 740
 0741                uint result = ConstructResult(i0, i1, EncodingPad, EncodingPad);
 0742                Unsafe.WriteUnaligned(dest, result);
 0743            }
 744
 745            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 746            public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, byte* dest, ref byte encodingMap)
 747            {
 0748                uint t0 = twoBytes[0];
 0749                uint t1 = twoBytes[1];
 750
 0751                uint i = (t0 << 16) | (t1 << 8);
 752
 0753                uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 0754                uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 0755                uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 756
 0757                uint result = ConstructResult(i0, i1, i2, EncodingPad);
 0758                Unsafe.WriteUnaligned(dest, result);
 0759            }
 760
 761#if NET
 762            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 763            public unsafe void StoreVector512ToDestination(byte* dest, byte* destStart, int destLength, Vector512<byte> 
 764            {
 0765                AssertWrite<Vector512<sbyte>>(dest, destStart, destLength);
 0766                str.Store(dest);
 0767            }
 768
 769            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 770            [CompExactlyDependsOn(typeof(Avx2))]
 771            public unsafe void StoreVector256ToDestination(byte* dest, byte* destStart, int destLength, Vector256<byte> 
 772            {
 0773                AssertWrite<Vector256<sbyte>>(dest, destStart, destLength);
 0774                Avx.Store(dest, str.AsByte());
 0775            }
 776
 777            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 778            public unsafe void StoreVector128ToDestination(byte* dest, byte* destStart, int destLength, Vector128<byte> 
 779            {
 0780                AssertWrite<Vector128<sbyte>>(dest, destStart, destLength);
 0781                str.Store(dest);
 0782            }
 783
 784            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 785            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 786            public unsafe void StoreArmVector128x4ToDestination(byte* dest, byte* destStart, int destLength,
 787                Vector128<byte> res1, Vector128<byte> res2, Vector128<byte> res3, Vector128<byte> res4)
 788            {
 0789                AssertWrite<Vector128<byte>>(dest, destStart, destLength);
 0790                AdvSimd.Arm64.StoreVectorAndZip(dest, (res1, res2, res3, res4));
 0791            }
 792#endif // NET
 793
 794            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 795            public unsafe void EncodeThreeAndWrite(byte* threeBytes, byte* destination, ref byte encodingMap)
 796            {
 0797                uint result = Encode(threeBytes, ref encodingMap);
 0798                Unsafe.WriteUnaligned(destination, result);
 0799            }
 800        }
 801
 802        internal readonly struct Base64EncoderChar : IBase64Encoder<ushort>
 803        {
 0804            public ReadOnlySpan<byte> EncodingMap => default(Base64EncoderByte).EncodingMap;
 805
 0806            public sbyte Avx2LutChar62 => default(Base64EncoderByte).Avx2LutChar62;
 807
 0808            public sbyte Avx2LutChar63 => default(Base64EncoderByte).Avx2LutChar63;
 809
 0810            public ReadOnlySpan<byte> AdvSimdLut4 => default(Base64EncoderByte).AdvSimdLut4;
 811
 0812            public uint Ssse3AdvSimdLutE3 => default(Base64EncoderByte).Ssse3AdvSimdLutE3;
 813
 0814            public int IncrementPadTwo => default(Base64EncoderByte).IncrementPadTwo;
 815
 0816            public int IncrementPadOne => default(Base64EncoderByte).IncrementPadOne;
 817
 818            public int GetMaxSrcLength(int srcLength, int destLength) =>
 0819                default(Base64EncoderByte).GetMaxSrcLength(srcLength, destLength);
 820
 0821            public uint GetInPlaceDestinationLength(int encodedLength, int _) => 0; // not used for char encoding
 822
 0823            public int GetMaxEncodedLength(int _) => 0;  // not used for char encoding
 824
 825            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 826            public unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, ushort* dest, ref byte encodingMap)
 827            {
 0828                Base64Helper.EncodeOneOptionallyPadTwo(oneByte, dest, ref encodingMap);
 0829                dest[2] = (ushort)EncodingPad;
 0830                dest[3] = (ushort)EncodingPad;
 0831            }
 832
 833            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 834            public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, ushort* dest, ref byte encodingMap)
 835            {
 0836                Base64Helper.EncodeTwoOptionallyPadOne(twoBytes, dest, ref encodingMap);
 0837                dest[3] = (ushort)EncodingPad;
 0838            }
 839
 840#if NET
 841            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 842            public unsafe void StoreVector512ToDestination(ushort* dest, ushort* destStart, int destLength, Vector512<by
 843            {
 0844                AssertWrite<Vector512<short>>(dest, destStart, destLength);
 0845                (Vector512<ushort> utf16LowVector, Vector512<ushort> utf16HighVector) = Vector512.Widen(str);
 0846                utf16LowVector.Store(dest);
 0847                utf16HighVector.Store(dest + 32);
 0848            }
 849
 850            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 851            public unsafe void StoreVector256ToDestination(ushort* dest, ushort* destStart, int destLength, Vector256<by
 852            {
 284694853                AssertWrite<Vector256<short>>(dest, destStart, destLength);
 284694854                (Vector256<ushort> utf16LowVector, Vector256<ushort> utf16HighVector) = Vector256.Widen(str);
 284694855                utf16LowVector.Store(dest);
 284694856                utf16HighVector.Store(dest + 16);
 284694857            }
 858
 859            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 860            public unsafe void StoreVector128ToDestination(ushort* dest, ushort* destStart, int destLength, Vector128<by
 861            {
 4788862                AssertWrite<Vector128<short>>(dest, destStart, destLength);
 4788863                (Vector128<ushort> utf16LowVector, Vector128<ushort> utf16HighVector) = Vector128.Widen(str);
 4788864                utf16LowVector.Store(dest);
 4788865                utf16HighVector.Store(dest + 8);
 4788866            }
 867
 868            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 869            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 870            public unsafe void StoreArmVector128x4ToDestination(ushort* dest, ushort* destStart, int destLength,
 871                Vector128<byte> res1, Vector128<byte> res2, Vector128<byte> res3, Vector128<byte> res4)
 872            {
 0873                AssertWrite<Vector128<short>>(dest, destStart, destLength);
 0874                (Vector128<ushort> utf16LowVector1, Vector128<ushort> utf16HighVector1) = Vector128.Widen(res1);
 0875                (Vector128<ushort> utf16LowVector2, Vector128<ushort> utf16HighVector2) = Vector128.Widen(res2);
 0876                (Vector128<ushort> utf16LowVector3, Vector128<ushort> utf16HighVector3) = Vector128.Widen(res3);
 0877                (Vector128<ushort> utf16LowVector4, Vector128<ushort> utf16HighVector4) = Vector128.Widen(res4);
 0878                AdvSimd.Arm64.StoreVectorAndZip(dest, (utf16LowVector1, utf16LowVector2, utf16LowVector3, utf16LowVector
 0879                AdvSimd.Arm64.StoreVectorAndZip(dest + 32, (utf16HighVector1, utf16HighVector2, utf16HighVector3, utf16H
 0880            }
 881#endif // NET
 882
 883            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 884            public unsafe void EncodeThreeAndWrite(byte* threeBytes, ushort* destination, ref byte encodingMap)
 885            {
 886                uint t0 = threeBytes[0];
 26733887                uint t1 = threeBytes[1];
 26733888                uint t2 = threeBytes[2];
 889
 26733890                uint i = (t0 << 16) | (t1 << 8) | t2;
 891
 26733892                ulong i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 26733893                ulong i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 26733894                ulong i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 26733895                ulong i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F));
 896
 897                ulong result;
 26733898                if (BitConverter.IsLittleEndian)
 899                {
 26733900                    result = i0 | (i1 << 16) | (i2 << 32) | (i3 << 48);
 901                }
 902                else
 903                {
 904                    result = (i0 << 48) | (i1 << 32) | (i2 << 16) | i3;
 905                }
 906
 26733907                Unsafe.WriteUnaligned(destination, result);
 26733908            }
 909        }
 910    }
 911}

C:\h\w\9FD50923\w\C0A20A61\e\runtime-utils\Runner\runtime\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        {
 64001818            int vectorElements = Unsafe.SizeOf<TVector>();
 64001819            byte* readEnd = src + vectorElements;
 64001820            byte* srcEnd = srcStart + srcLength;
 21
 64001822            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            }
 64001827        }
 28
 29        [Conditional("DEBUG")]
 30        internal static unsafe void AssertWrite<TVector>(byte* dest, byte* destStart, int destLength)
 31        {
 28959932            int vectorElements = Unsafe.SizeOf<TVector>();
 28959933            byte* writeEnd = dest + vectorElements;
 28959934            byte* destEnd = destStart + destLength;
 35
 28959936            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            }
 28959941        }
 42
 43        [Conditional("DEBUG")]
 44        internal static unsafe void AssertRead<TVector>(ushort* src, ushort* srcStart, int srcLength)
 45        {
 19359446            int vectorElements = Unsafe.SizeOf<TVector>();
 19359447            ushort* readEnd = src + vectorElements;
 19359448            ushort* srcEnd = srcStart + srcLength;
 49
 19359450            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            }
 19359455        }
 56
 57        [Conditional("DEBUG")]
 58        internal static unsafe void AssertWrite<TVector>(ushort* dest, ushort* destStart, int destLength)
 59        {
 28948260            int vectorElements = Unsafe.SizeOf<TVector>();
 28948261            ushort* writeEnd = dest + vectorElements;
 28948262            ushort* destEnd = destStart + destLength;
 63
 28948264            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            }
 28948269        }
 70
 71#if NET8_0
 72        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 73        internal static bool VectorContainsNonAsciiChar(Vector128<ushort> utf16Vector)
 74        {
 75            // prefer architecture specific intrinsic as they offer better perf
 76            if (Sse2.IsSupported)
 77            {
 78                if (Sse41.IsSupported)
 79                {
 80                    Vector128<ushort> asciiMaskForTestZ = Vector128.Create((ushort)0xFF80);
 81                    // If a non-ASCII bit is set in any WORD of the vector, we have seen non-ASCII data.
 82                    return !Sse41.TestZ(utf16Vector.AsInt16(), asciiMaskForTestZ.AsInt16());
 83                }
 84                else
 85                {
 86                    Vector128<ushort> asciiMaskForAddSaturate = Vector128.Create((ushort)0x7F80);
 87                    // The operation below forces the 0x8000 bit of each WORD to be set iff the WORD element
 88                    // has value >= 0x0800 (non-ASCII). Then we'll treat the vector as a BYTE vector in order
 89                    // to extract the mask. Reminder: the 0x0080 bit of each WORD should be ignored.
 90                    return (Sse2.MoveMask(Sse2.AddSaturate(utf16Vector, asciiMaskForAddSaturate).AsByte()) & 0b_1010_101
 91                }
 92            }
 93            else if (AdvSimd.Arm64.IsSupported)
 94            {
 95                // First we pick four chars, a larger one from all four pairs of adjecent chars in the vector.
 96                // If any of those four chars has a non-ASCII bit set, we have seen non-ASCII data.
 97                Vector128<ushort> maxChars = AdvSimd.Arm64.MaxPairwise(utf16Vector, utf16Vector);
 98                return (maxChars.AsUInt64().ToScalar() & 0xFF80FF80FF80FF80) != 0;
 99            }
 100            else
 101            {
 102                const ushort asciiMask = ushort.MaxValue - 127; // 0xFF80
 103                Vector128<ushort> zeroIsAscii = utf16Vector & Vector128.Create(asciiMask);
 104                // If a non-ASCII bit is set in any WORD of the vector, we have seen non-ASCII data.
 105                return zeroIsAscii != Vector128<ushort>.Zero;
 106            }
 107        }
 108
 109        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 110        internal static Vector128<byte> ExtractAsciiVector(Vector128<ushort> vectorFirst, Vector128<ushort> vectorSecond
 111        {
 112            // Narrows two vectors of words [ w7 w6 w5 w4 w3 w2 w1 w0 ] and [ w7' w6' w5' w4' w3' w2' w1' w0' ]
 113            // to a vector of bytes [ b7 ... b0 b7' ... b0'].
 114
 115            // prefer architecture specific intrinsic as they don't perform additional AND like Vector128.Narrow does
 116            if (Sse2.IsSupported)
 117            {
 118                return Sse2.PackUnsignedSaturate(vectorFirst.AsInt16(), vectorSecond.AsInt16());
 119            }
 120            else if (AdvSimd.Arm64.IsSupported)
 121            {
 122                return AdvSimd.Arm64.UnzipEven(vectorFirst.AsByte(), vectorSecond.AsByte());
 123            }
 124            else if (PackedSimd.IsSupported)
 125            {
 126                return PackedSimd.ConvertNarrowingSaturateUnsigned(vectorFirst.AsInt16(), vectorSecond.AsInt16());
 127            }
 128            else
 129            {
 130                return Vector128.Narrow(vectorFirst, vectorSecond);
 131            }
 132        }
 133
 134        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 135        internal static bool VectorContainsNonAsciiChar(Vector256<ushort> utf16Vector)
 136        {
 137            if (Avx.IsSupported)
 138            {
 139                Vector256<ushort> asciiMaskForTestZ = Vector256.Create((ushort)0xFF80);
 140                return !Avx.TestZ(utf16Vector.AsInt16(), asciiMaskForTestZ.AsInt16());
 141            }
 142            else
 143            {
 144                const ushort asciiMask = ushort.MaxValue - 127; // 0xFF80
 145                Vector256<ushort> zeroIsAscii = utf16Vector & Vector256.Create(asciiMask);
 146                // If a non-ASCII bit is set in any WORD of the vector, we have seen non-ASCII data.
 147                return zeroIsAscii != Vector256<ushort>.Zero;
 148            }
 149        }
 150
 151        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 152        internal static bool VectorContainsNonAsciiChar(Vector512<ushort> utf16Vector)
 153        {
 154            const ushort asciiMask = ushort.MaxValue - 127; // 0xFF80
 155            Vector512<ushort> zeroIsAscii = utf16Vector & Vector512.Create(asciiMask);
 156            // If a non-ASCII bit is set in any WORD of the vector, we have seen non-ASCII data.
 157            return zeroIsAscii != Vector512<ushort>.Zero;
 158        }
 159#endif
 160
 161        [DoesNotReturn]
 162        internal static void ThrowUnreachableException()
 163        {
 164#if NET
 0165            throw new UnreachableException();
 166#else
 167            throw new Exception("Unreachable");
 168#endif
 169        }
 170
 171        internal interface IBase64Encoder<T> where T : unmanaged
 172        {
 173            ReadOnlySpan<byte> EncodingMap { get; }
 174            sbyte Avx2LutChar62 { get; }
 175            sbyte Avx2LutChar63 { get; }
 176            ReadOnlySpan<byte> AdvSimdLut4 { get; }
 177            uint Ssse3AdvSimdLutE3 { get; }
 178            int GetMaxSrcLength(int srcLength, int destLength);
 179            int GetMaxEncodedLength(int srcLength);
 180            uint GetInPlaceDestinationLength(int encodedLength, int leftOver);
 181            unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, T* dest, ref byte encodingMap);
 182            unsafe void EncodeTwoOptionallyPadOne(byte* oneByte, T* dest, ref byte encodingMap);
 183            unsafe void EncodeThreeAndWrite(byte* threeBytes, T* destination, ref byte encodingMap);
 184            int IncrementPadTwo { get; }
 185            int IncrementPadOne { get; }
 186#if NET
 187            unsafe void StoreVector512ToDestination(T* dest, T* destStart, int destLength, Vector512<byte> str);
 188            unsafe void StoreVector256ToDestination(T* dest, T* destStart, int destLength, Vector256<byte> str);
 189            unsafe void StoreVector128ToDestination(T* dest, T* destStart, int destLength, Vector128<byte> str);
 190            unsafe void StoreArmVector128x4ToDestination(T* dest, T* destStart, int destLength, Vector128<byte> res1,
 191                Vector128<byte> res2, Vector128<byte> res3, Vector128<byte> res4);
 192#endif // NET
 193        }
 194
 195        internal interface IBase64Decoder<T> where T : unmanaged
 196        {
 197            ReadOnlySpan<sbyte> DecodingMap { get; }
 198            ReadOnlySpan<uint> VbmiLookup0 { get; }
 199            ReadOnlySpan<uint> VbmiLookup1 { get; }
 200            ReadOnlySpan<sbyte> Avx2LutHigh { get; }
 201            ReadOnlySpan<sbyte> Avx2LutLow { get; }
 202            ReadOnlySpan<sbyte> Avx2LutShift { get; }
 203            byte MaskSlashOrUnderscore { get; }
 204            ReadOnlySpan<int> Vector128LutHigh { get; }
 205            ReadOnlySpan<int> Vector128LutLow { get; }
 206            ReadOnlySpan<uint> Vector128LutShift { get; }
 207            ReadOnlySpan<uint> AdvSimdLutOne3 { get; }
 208            uint AdvSimdLutTwo3Uint1 { get; }
 209            int SrcLength(bool isFinalBlock, int sourceLength);
 210            int GetMaxDecodedLength(int sourceLength);
 211            bool IsInvalidLength(int bufferLength);
 212            bool IsValidPadding(uint padChar);
 213#if NET
 214            bool TryDecode128Core(
 215                Vector128<byte> str,
 216                Vector128<byte> hiNibbles,
 217                Vector128<byte> maskSlashOrUnderscore,
 218                Vector128<byte> mask8F,
 219                Vector128<byte> lutLow,
 220                Vector128<byte> lutHigh,
 221                Vector128<sbyte> lutShift,
 222                Vector128<byte> shiftForUnderscore,
 223                out Vector128<byte> result);
 224            bool TryDecode256Core(
 225                Vector256<sbyte> str,
 226                Vector256<sbyte> hiNibbles,
 227                Vector256<sbyte> maskSlashOrUnderscore,
 228                Vector256<sbyte> lutLow,
 229                Vector256<sbyte> lutHigh,
 230                Vector256<sbyte> lutShift,
 231                Vector256<sbyte> shiftForUnderscore,
 232                out Vector256<sbyte> result);
 233            unsafe bool TryLoadVector512(T* src, T* srcStart, int sourceLength, out Vector512<sbyte> str);
 234            unsafe bool TryLoadAvxVector256(T* src, T* srcStart, int sourceLength, out Vector256<sbyte> str);
 235            unsafe bool TryLoadVector128(T* src, T* srcStart, int sourceLength, out Vector128<byte> str);
 236            unsafe bool TryLoadArmVector128x4(T* src, T* srcStart, int sourceLength,
 237                out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> str4);
 238#endif // NET
 239            unsafe int DecodeFourElements(T* source, ref sbyte decodingMap);
 240            unsafe int DecodeRemaining(T* srcEnd, ref sbyte decodingMap, long remaining, out uint t2, out uint t3);
 241            int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<T> span);
 242            OperationStatus DecodeWithWhiteSpaceBlockwiseWrapper<TTBase64Decoder>(TTBase64Decoder decoder, ReadOnlySpan<
 243                Span<byte> bytes, ref int bytesConsumed, ref int bytesWritten, bool isFinalBlock = true)
 244                where TTBase64Decoder : IBase64Decoder<T>;
 245        }
 246    }
 247}

C:\h\w\9FD50923\w\C0A20A61\e\runtime-utils\Runner\runtime\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        {
 665214            int length = 0, paddingCount = 0;
 332615            T lastChar = default;
 16
 332617            if (!base64Text.IsEmpty)
 18            {
 19#if NET
 14427520                while (!base64Text.IsEmpty)
 21                {
 14378522                    int index = validatable.IndexOfAnyExcept(base64Text);
 14378523                    if ((uint)index >= (uint)base64Text.Length)
 24                    {
 202325                        length += base64Text.Length;
 202326                        lastChar = base64Text[base64Text.Length - 1];
 202327                        break;
 28                    }
 29
 14176230                    length += index;
 14176231                    if (index != 0)
 32                    {
 13873533                        lastChar = base64Text[index - 1];
 34                    }
 35
 14176236                    T charToValidate = base64Text[index];
 14176237                    base64Text = base64Text.Slice(index + 1);
 38
 14176239                    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.
 29799343                        while (!base64Text.IsEmpty && validatable.IsWhiteSpace(base64Text[0]))
 44                        {
 15704445                            base64Text = base64Text.Slice(1);
 46                        }
 14045947                        continue;
 48                    }
 49
 81350                    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.
 27057                    paddingCount = 1;
 1168058                    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
 563494                        if (validatable.IsEncodingPad(charToValidateInPadding))
 95                        {
 96                            // There can be at most 2 padding chars.
 12397                            if (paddingCount >= 2)
 98                            {
 99                                goto Fail;
 100                            }
 101
 103102                            paddingCount++;
 103                        }
 5511104                        else if (!validatable.IsWhiteSpace(charToValidateInPadding))
 105                        {
 106                            // Invalid char was found.
 107                            goto Fail;
 108                        }
 109                    }
 110
 142111                    length += paddingCount;
 142112                    break;
 113                }
 114
 2655115                if (!validatable.ValidateAndDecodeLength(lastChar, length, paddingCount, out decodedLength))
 116                {
 117                    goto Fail;
 118                }
 119
 2172120                return true;
 121            }
 122
 0123            decodedLength = 0;
 0124            return true;
 125
 126        Fail:
 1154127            decodedLength = 0;
 1154128            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
 0171            private static readonly SearchValues<byte> s_validBase64Chars = SearchValues.Create(default(Base64EncoderByt
 172
 0173            public int IndexOfAnyExcept(ReadOnlySpan<byte> span) => span.IndexOfAnyExcept(s_validBase64Chars);
 174#else
 175            public int DecodeValue(byte value) => default(Base64DecoderByte).DecodingMap[value];
 176#endif
 0177            public bool IsWhiteSpace(byte value) => Base64Helper.IsWhiteSpace(value);
 0178            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            {
 0182                if (length % 4 == 0)
 183                {
 0184                    int decoded = default(Base64DecoderByte).DecodingMap[lastChar];
 0185                    if ((paddingCount == 1 && (decoded & 0x03) != 0) ||
 0186                        (paddingCount == 2 && (decoded & 0x0F) != 0))
 187                    {
 188                        // unused lower bits are not 0, reject input
 0189                        decodedLength = 0;
 0190                        return false;
 191                    }
 192
 193                    // Remove padding to get exact length.
 0194                    decodedLength = (int)((uint)length / 4 * 3) - paddingCount;
 0195                    return true;
 196                }
 197
 0198                decodedLength = 0;
 0199                return false;
 200            }
 201        }
 202    }
 203}

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&)