< 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\A9B70965\w\B51109B6\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
 35256432            fixed (T* srcBytes = &MemoryMarshal.GetReference(source))
 35256433            fixed (byte* destBytes = &MemoryMarshal.GetReference(bytes))
 34            {
 35256435                int srcLength = decoder.SrcLength(isFinalBlock, source.Length);
 35256436                int destLength = bytes.Length;
 35256437                int maxSrcLength = srcLength;
 35256438                int decodedLength = decoder.GetMaxDecodedLength(srcLength);
 39
 40                // max. 2 padding chars
 35256441                if (destLength < decodedLength - 2)
 42                {
 43                    // For overflow see comment below
 044                    maxSrcLength = destLength / 3 * 4;
 45                }
 46
 35256447                T* src = srcBytes;
 35256448                byte* dest = destBytes;
 35256449                T* srcEnd = srcBytes + (uint)srcLength;
 35256450                T* srcMax = srcBytes + (uint)maxSrcLength;
 51
 52#if NET
 35256453                if (maxSrcLength >= 24)
 54                {
 13634755                    T* end = srcMax - 88;
 13634756                    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
 13634766                    end = srcMax - 45;
 13634767                    if (Avx2.IsSupported && (end >= src))
 68                    {
 13237469                        Avx2Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 70
 13237471                        if (src == srcEnd)
 72                        {
 73                            goto DoneExit;
 74                        }
 75                    }
 76
 13634777                    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
 13634788                    end = srcMax - 24;
 13634789                    if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian && (end >= src))
 90                    {
 13453791                        Vector128Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 92
 13453793                        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
 352564103                int skipLastChunk = isFinalBlock ? 4 : 0;
 104
 352564105                if (destLength >= decodedLength)
 106                {
 352564107                    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
 352564127                ref sbyte decodingMap = ref MemoryMarshal.GetReference(decoder.DecodingMap);
 352564128                srcMax = srcBytes + maxSrcLength;
 129
 748587130                while (src < srcMax)
 131                {
 527208132                    int result = decoder.DecodeFourElements(src, ref decodingMap);
 133
 527208134                    if (result < 0)
 135                    {
 136                        goto InvalidDataExit;
 137                    }
 138
 396023139                    WriteThreeLowOrderBytes(dest, result);
 396023140                    src += 4;
 396023141                    dest += 3;
 142                }
 143
 221379144                if (maxSrcLength != srcLength - skipLastChunk)
 145                {
 146                    goto DestinationTooSmallExit;
 147                }
 148
 221379149                if (src == srcEnd)
 150                {
 212904151                    if (isFinalBlock)
 152                    {
 153                        goto InvalidDataExit;
 154                    }
 155
 212904156                    if (src == srcBytes + source.Length)
 157                    {
 212904158                        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
 8475167                long remaining = srcEnd - src;
 8475168                Debug.Assert(typeof(TBase64Decoder) == typeof(Base64DecoderByte) ? remaining == 4 : remaining < 8);
 8475169                int i0 = decoder.DecodeRemaining(srcEnd, ref decodingMap, remaining, out uint t2, out uint t3);
 170
 8475171                byte* destMax = destBytes + (uint)destLength;
 172
 8475173                if (!decoder.IsValidPadding(t3))
 174                {
 4014175                    int i2 = Unsafe.Add(ref decodingMap, (IntPtr)t2);
 4014176                    int i3 = Unsafe.Add(ref decodingMap, (IntPtr)t3);
 177
 4014178                    i2 <<= 6;
 179
 4014180                    i0 |= i3;
 4014181                    i0 |= i2;
 182
 4014183                    if (i0 < 0)
 184                    {
 185                        goto InvalidDataExit;
 186                    }
 3866187                    if (dest + 3 > destMax)
 188                    {
 189                        goto DestinationTooSmallExit;
 190                    }
 191
 3866192                    WriteThreeLowOrderBytes(dest, i0);
 3866193                    dest += 3;
 3866194                    src += 4;
 195                }
 4461196                else if (!decoder.IsValidPadding(t2))
 197                {
 1855198                    int i2 = Unsafe.Add(ref decodingMap, (IntPtr)t2);
 199
 1855200                    i2 <<= 6;
 201
 1855202                    i0 |= i2;
 203
 1855204                    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                {
 2606220                    if ((i0 & 0x8000F000) != 0) // if negative or 4 unused bits are not 0.
 221                    {
 222                        goto InvalidDataExit;
 223                    }
 1752224                    if (dest + 1 > destMax)
 225                    {
 226                        goto DestinationTooSmallExit;
 227                    }
 228
 1752229                    dest[0] = (byte)(i0 >> 16);
 1752230                    dest += 1;
 1752231                    src += remaining;
 232                }
 233
 7177234                if (srcLength != source.Length)
 235                {
 236                    goto InvalidDataExit;
 237                }
 238
 239            DoneExit:
 220081240                bytesConsumed = (int)(src - srcBytes);
 220081241                bytesWritten = (int)(dest - destBytes);
 220081242                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:
 132483266                bytesConsumed = (int)(src - srcBytes);
 132483267                bytesWritten = (int)(dest - destBytes);
 132483268                return ignoreWhiteSpace ?
 132483269                    InvalidDataFallback(decoder, source, bytes, ref bytesConsumed, ref bytesWritten, isFinalBlock) :
 132483270                    OperationStatus.InvalidData;
 271            }
 272
 273            static OperationStatus InvalidDataFallback(TBase64Decoder decoder, ReadOnlySpan<T> source, Span<byte> bytes,
 274            {
 5467275                source = source.Slice(bytesConsumed);
 5467276                bytes = bytes.Slice(bytesWritten);
 277
 278                OperationStatus status;
 279                do
 280                {
 131498281                    int localConsumed = decoder.IndexOfAnyExceptWhiteSpace(source);
 131498282                    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.
 428286                        bytesConsumed += source.Length;
 428287                        status = OperationStatus.Done;
 428288                        break;
 289                    }
 290
 131070291                    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.
 3177299                        return decoder.DecodeWithWhiteSpaceBlockwiseWrapper(decoder, source, bytes, ref bytesConsumed, r
 300                    }
 301
 302                    // Skip over the starting whitespace and continue.
 127893303                    bytesConsumed += localConsumed;
 127893304                    source = source.Slice(localConsumed);
 305
 306                    // Try again after consumed whitespace
 127893307                    status = DecodeFrom(decoder, source, bytes, out localConsumed, out int localWritten, isFinalBlock, i
 127893308                    bytesConsumed += localConsumed;
 127893309                    bytesWritten += localWritten;
 310
 127893311                    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
 126031320                    source = source.Slice(localConsumed);
 126031321                    bytes = bytes.Slice(localWritten);
 322                }
 126031323                while (!source.IsEmpty);
 324
 2290325                return status;
 326            }
 327        }
 328
 329        internal static unsafe OperationStatus DecodeFromUtf8InPlace<TBase64Decoder>(TBase64Decoder decoder, Span<byte> 
 330            where TBase64Decoder : IBase64Decoder<byte>
 331        {
 495843332            if (buffer.IsEmpty)
 333            {
 0334                bytesWritten = 0;
 0335                return OperationStatus.Done;
 336            }
 337
 495843338            fixed (byte* bufferBytes = &MemoryMarshal.GetReference(buffer))
 339            {
 495843340                uint bufferLength = (uint)buffer.Length;
 495843341                uint sourceIndex = 0;
 495843342                uint destIndex = 0;
 343
 495843344                if (decoder.IsInvalidLength(buffer.Length))
 345                {
 346                    goto InvalidExit;
 347                }
 348
 494991349                ref sbyte decodingMap = ref MemoryMarshal.GetReference(decoder.DecodingMap);
 350
 494991351                if (bufferLength > 4)
 352                {
 779718353                    while (sourceIndex < bufferLength - 4)
 354                    {
 776444355                        int result = decoder.DecodeFourElements(bufferBytes + sourceIndex, ref decodingMap);
 776444356                        if (result < 0)
 357                        {
 358                            goto InvalidExit;
 359                        }
 360
 774112361                        WriteThreeLowOrderBytes(bufferBytes + destIndex, result);
 774112362                        destIndex += 3;
 774112363                        sourceIndex += 4;
 364                    }
 365                }
 366
 367                uint t0;
 368                uint t1;
 369                uint t2;
 370                uint t3;
 371
 492659372                switch (bufferLength - sourceIndex)
 373                {
 374                    case 2:
 1238375                        t0 = bufferBytes[bufferLength - 2];
 1238376                        t1 = bufferBytes[bufferLength - 1];
 1238377                        t2 = EncodingPad;
 1238378                        t3 = EncodingPad;
 1238379                        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:
 490289387                        t0 = bufferBytes[bufferLength - 4];
 490289388                        t1 = bufferBytes[bufferLength - 3];
 490289389                        t2 = bufferBytes[bufferLength - 2];
 490289390                        t3 = bufferBytes[bufferLength - 1];
 391                        break;
 392                    default:
 393                        goto InvalidExit;
 394                }
 395
 492659396                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 492659397                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 398
 492659399                i0 <<= 18;
 492659400                i1 <<= 12;
 401
 492659402                i0 |= i1;
 403
 492659404                if (!decoder.IsValidPadding(t3))
 405                {
 489329406                    int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 489329407                    int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 408
 489329409                    i2 <<= 6;
 410
 489329411                    i0 |= i3;
 489329412                    i0 |= i2;
 413
 489329414                    if (i0 < 0)
 415                    {
 416                        goto InvalidExit;
 417                    }
 418
 488877419                    WriteThreeLowOrderBytes(bufferBytes + destIndex, i0);
 488877420                    destIndex += 3;
 421                }
 3330422                else if (!decoder.IsValidPadding(t2))
 423                {
 1573424                    int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 425
 1573426                    i2 <<= 6;
 427
 1573428                    i0 |= i2;
 429
 1573430                    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                {
 1757441                    if ((i0 & 0x8000F000) != 0) // if negative or 4 unused bits are not 0.
 442                    {
 443                        goto InvalidExit;
 444                    }
 445
 1471446                    bufferBytes[destIndex] = (byte)(i0 >> 16);
 1471447                    destIndex += 1;
 448                }
 449
 491689450                bytesWritten = (int)destIndex;
 491689451                return OperationStatus.Done;
 452
 453            InvalidExit:
 4154454                bytesWritten = (int)destIndex;
 4154455                return ignoreWhiteSpace ?
 4154456                    DecodeWithWhiteSpaceFromUtf8InPlace<TBase64Decoder>(decoder, buffer, ref bytesWritten, sourceIndex) 
 4154457                    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;
 3177465            Span<byte> buffer = stackalloc byte[BlockSize];
 3177466            OperationStatus status = OperationStatus.Done;
 467
 259590468            while (!source.IsEmpty)
 469            {
 470                // Skip over any leading whitespace
 259360471                if (IsWhiteSpace(source[0]))
 472                {
 46799473                    source = source.Slice(1);
 46799474                    bytesConsumed++;
 46799475                    continue;
 476                }
 477
 212561478                int encodedIdx = 0;
 212561479                int bufferIdx = 0;
 212561480                int skipped = 0;
 481
 2170479482                for (; encodedIdx < source.Length && (uint)bufferIdx < (uint)buffer.Length; ++encodedIdx)
 483                {
 978959484                    if (IsWhiteSpace(source[encodedIdx]))
 485                    {
 130965486                        skipped++;
 487                    }
 488                    else
 489                    {
 847994490                        buffer[bufferIdx] = source[encodedIdx];
 847994491                        bufferIdx++;
 492                    }
 493                }
 494
 212561495                source = source.Slice(encodedIdx);
 212561496                Debug.Assert(bufferIdx > 0);
 497
 498                bool hasAnotherBlock;
 499
 212561500                if (typeof(TBase64Decoder) == typeof(Base64DecoderByte))
 501                {
 0502                    hasAnotherBlock = source.Length >= BlockSize;
 503                }
 504                else
 505                {
 212561506                    hasAnotherBlock = source.Length > 1;
 507                }
 508
 212561509                bool localIsFinalBlock = !hasAnotherBlock;
 510
 511                // If this block contains padding and there's another block, then only whitespace may follow for being v
 212561512                if (hasAnotherBlock)
 513                {
 210204514                    int paddingCount = GetPaddingCount(decoder, ref buffer[BlockSize - 1]);
 210204515                    if (paddingCount > 0)
 516                    {
 239517                        hasAnotherBlock = false;
 239518                        localIsFinalBlock = true;
 519                    }
 520                }
 521
 212561522                if (localIsFinalBlock && !isFinalBlock)
 523                {
 0524                    localIsFinalBlock = false;
 525                }
 526
 212561527                status = DecodeFrom<TBase64Decoder, byte>(decoder, buffer.Slice(0, bufferIdx), bytes, out int localConsu
 528
 212561529                if (status != OperationStatus.Done)
 530                {
 985531                    Debug.Assert(localConsumed == 0 && localWritten == 0, "On failure, should not have consumed or writt
 985532                    return status;
 533                }
 534
 211576535                bytesConsumed += skipped;
 211576536                bytesConsumed += localConsumed;
 211576537                bytesWritten += localWritten;
 538
 539                // The remaining data must all be whitespace in order to be valid.
 211576540                if (!hasAnotherBlock)
 541                {
 14822542                    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
 1798556                    break;
 557                }
 558
 209614559                bytes = bytes.Slice(localWritten);
 209614560                Debug.Assert(!source.IsEmpty);
 561            }
 562
 2028563            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        {
 210204674            int padding = 0;
 675
 210204676            if (decoder.IsValidPadding(ptrToLastElement))
 677            {
 233678                padding++;
 679            }
 680
 210204681            if (decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))
 682            {
 63683                padding++;
 684            }
 685
 210204686            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        {
 3308711            int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);
 3308712            Span<byte> buffer = stackalloc byte[BlockSize];
 713
 3308714            OperationStatus status = OperationStatus.Done;
 3308715            int localDestIndex = destIndex;
 3308716            bool hasPaddingBeenProcessed = false;
 3308717            int localBytesWritten = 0;
 718
 492105719            while (sourceIndex < (uint)source.Length)
 720            {
 489946721                int bufferIdx = 0;
 722
 2749894723                while (bufferIdx < BlockSize && sourceIndex < (uint)source.Length)
 724                {
 2259948725                    if (!IsWhiteSpace(source[(int)sourceIndex]))
 726                    {
 1955882727                        buffer[bufferIdx] = source[(int)sourceIndex];
 1955882728                        bufferIdx++;
 729                    }
 730
 2259948731                    sourceIndex++;
 732                }
 733
 489946734                if (bufferIdx == 0)
 735                {
 736                    continue;
 737                }
 738
 489500739                if (bufferIdx != 4)
 740                {
 741                    // Base64 require 4 bytes, for Base64Url it can be less than 4 bytes but not 1 byte.
 1110742                    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                    {
 2108749                        while (bufferIdx < BlockSize)  // Can happen only for last block
 750                        {
 1260751                            Debug.Assert(source.Length == sourceIndex);
 1260752                            buffer[bufferIdx++] = (byte)EncodingPad;
 753                        }
 754                    }
 755                }
 756
 489238757                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
 489197766                status = DecodeFromUtf8InPlace<TBase64Decoder>(decoder, buffer, out localBytesWritten, ignoreWhiteSpace:
 489197767                localDestIndex += localBytesWritten;
 489197768                hasPaddingBeenProcessed = localBytesWritten < 3;
 769
 489197770                if (status != OperationStatus.Done)
 771                {
 772                    break;
 773                }
 774
 775                // Write result to source span in place.
 3905114776                for (int i = 0; i < localBytesWritten; i++)
 777                {
 1464206778                    source[localDestIndex - localBytesWritten + i] = buffer[i];
 779                }
 780            }
 781
 3308782            destIndex = localDestIndex;
 3308783            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
 132374870            Vector256<sbyte> lutHi = Vector256.Create(decoder.Avx2LutHigh);
 871
 132374872            Vector256<sbyte> lutLo = Vector256.Create(decoder.Avx2LutLow);
 873
 132374874            Vector256<sbyte> lutShift = Vector256.Create(decoder.Avx2LutShift);
 875
 132374876            Vector256<sbyte> packBytesInLaneMask = Vector256.Create(
 132374877                2, 1, 0, 6,
 132374878                5, 4, 10, 9,
 132374879                8, 14, 13, 12,
 132374880                -1, -1, -1, -1,
 132374881                2, 1, 0, 6,
 132374882                5, 4, 10, 9,
 132374883                8, 14, 13, 12,
 132374884                -1, -1, -1, -1);
 885
 132374886            Vector256<int> packLanesControl = Vector256.Create(
 132374887                 0, 0, 0, 0,
 132374888                1, 0, 0, 0,
 132374889                2, 0, 0, 0,
 132374890                4, 0, 0, 0,
 132374891                5, 0, 0, 0,
 132374892                6, 0, 0, 0,
 132374893                -1, -1, -1, -1,
 132374894                -1, -1, -1, -1).AsInt32();
 895
 132374896            Vector256<sbyte> maskSlashOrUnderscore = Vector256.Create((sbyte)decoder.MaskSlashOrUnderscore);
 132374897            Vector256<sbyte> shiftForUnderscore = Vector256.Create((sbyte)33);
 132374898            Vector256<sbyte> mergeConstant0 = Vector256.Create(0x01400140).AsSByte();
 132374899            Vector256<short> mergeConstant1 = Vector256.Create(0x00011000).AsInt16();
 900
 132374901            T* src = srcBytes;
 132374902            byte* dest = destBytes;
 903
 904            //while (remaining >= 45)
 905            do
 906            {
 337401907                if (!decoder.TryLoadAvxVector256(src, srcStart, sourceLength, out Vector256<sbyte> str))
 908                {
 909                    break;
 910                }
 911
 337401912                Vector256<sbyte> hiNibbles = ((str.AsInt32()) >>> 4).AsSByte() & maskSlashOrUnderscore;
 913
 337401914                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
 210768925                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
 210768931                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:
 210768938                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
 210768945                str = Avx2.PermuteVar8x32(output, packLanesControl).AsSByte();
 946
 210768947                AssertWrite<Vector256<sbyte>>(dest, destStart, destLength);
 210768948                Avx.Store(dest, str.AsByte());
 949
 210768950                src += 32;
 210768951                dest += 24;
 952            }
 210768953            while (src <= srcEnd);
 954
 132374955            srcBytes = src;
 132374956            destBytes = dest;
 132374957        }
 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        {
 582044964            Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian);
 965
 582044966            if (Ssse3.IsSupported)
 967            {
 582044968                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
 1345371198            Vector128<byte> lutHi = Vector128.Create(decoder.Vector128LutHigh).AsByte();
 1345371199            Vector128<byte> lutLo = Vector128.Create(decoder.Vector128LutLow).AsByte();
 1345371200            Vector128<sbyte> lutShift = Vector128.Create(decoder.Vector128LutShift).AsSByte();
 1345371201            Vector128<sbyte> packBytesMask = Vector128.Create(0x06000102, 0x090A0405, 0x0C0D0E08, 0xffffffff).AsSByte();
 1345371202            Vector128<byte> mergeConstant0 = Vector128.Create(0x01400140).AsByte();
 1345371203            Vector128<short> mergeConstant1 = Vector128.Create(0x00011000).AsInt16();
 1345371204            Vector128<byte> one = Vector128<byte>.One;
 1345371205            Vector128<byte> mask2F = Vector128.Create(decoder.MaskSlashOrUnderscore);
 1345371206            Vector128<byte> mask8F = Vector128.Create((byte)0x8F);
 1345371207            Vector128<byte> shiftForUnderscore = Vector128.Create((byte)33);
 1345371208            T* src = srcBytes;
 1345371209            byte* dest = destBytes;
 1210
 1211            //while (remaining >= 24)
 1212            do
 1213            {
 2073941214                if (!decoder.TryLoadVector128(src, srcStart, sourceLength, out Vector128<byte> str))
 1215                {
 1216                    break;
 1217                }
 1218
 1219                // lookup
 2073941220                Vector128<byte> hiNibbles = Vector128.ShiftRightLogical(str.AsInt32(), 4).AsByte() & mask2F;
 1221
 2073941222                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;
 788401234                if (Ssse3.IsSupported)
 1235                {
 788401236                    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;
 788401256                if (Ssse3.IsSupported)
 1257                {
 788401258                    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:
 788401278                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
 788401284                AssertWrite<Vector128<sbyte>>(dest, destStart, destLength);
 788401285                str.Store(dest);
 1286
 788401287                src += 16;
 788401288                dest += 12;
 1289            }
 788401290            while (src <= srcEnd);
 1291
 1345371292            srcBytes = src;
 1345371293            destBytes = dest;
 1345371294        }
 1295#endif
 1296
 1297        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1298        private static unsafe void WriteThreeLowOrderBytes(byte* destination, int value)
 1299        {
 16628781300            destination[0] = (byte)(value >> 16);
 16628781301            destination[1] = (byte)(value >> 8);
 16628781302            destination[2] = (byte)value;
 16628781303        }
 1304
 1305        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1306        internal static bool IsWhiteSpace(int value)
 1307        {
 44021471308            Debug.Assert(value >= 0 && value <= ushort.MaxValue);
 1309            uint charMinusLowUInt32;
 44021471310            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            {
 1492801486                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 1492801487                str = Avx.LoadVector256(src).AsSByte();
 1492801488                return true;
 1489            }
 1490
 1491            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1492            public unsafe bool TryLoadVector128(byte* src, byte* srcStart, int sourceLength, out Vector128<byte> str)
 1493            {
 2019021494                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 2019021495                str = Vector128.LoadUnsafe(ref *src);
 2019021496                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
 12825891515                uint t0 = source[0];
 12825891516                uint t1 = source[1];
 12825891517                uint t2 = source[2];
 12825891518                uint t3 = source[3];
 1519
 12825891520                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 12825891521                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 12825891522                int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 12825891523                int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 1524
 12825891525                i0 <<= 18;
 12825891526                i1 <<= 12;
 12825891527                i2 <<= 6;
 1528
 12825891529                i0 |= i3;
 12825891530                i1 |= i2;
 1531
 12825891532                i0 |= i1;
 12825891533                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;
 51521541                t2 = EncodingPad;
 51521542                t3 = EncodingPad;
 1543                switch (remaining)
 1544                {
 1545                    case 2:
 10161546                        t0 = srcEnd[-2];
 10161547                        t1 = srcEnd[-1];
 10161548                        break;
 1549                    case 3:
 6401550                        t0 = srcEnd[-3];
 6401551                        t1 = srcEnd[-2];
 6401552                        t2 = srcEnd[-1];
 6401553                        break;
 1554                    case 4:
 31931555                        t0 = srcEnd[-4];
 31931556                        t1 = srcEnd[-3];
 31931557                        t2 = srcEnd[-2];
 31931558                        t3 = srcEnd[-1];
 31931559                        break;
 1560                    default:
 3031561                        return -1;
 1562                }
 1563
 48491564                int i0 = Unsafe.Add(ref decodingMap, (IntPtr)t0);
 48491565                int i1 = Unsafe.Add(ref decodingMap, (IntPtr)t1);
 1566
 48491567                i0 <<= 18;
 48491568                i1 <<= 12;
 1569
 48491570                i0 |= i1;
 48491571                return i0;
 1572            }
 1573
 1574            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1575            public int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<byte> span)
 1576            {
 9072641577                for (int i = 0; i < span.Length; i++)
 1578                {
 4532041579                    if (!IsWhiteSpace(span[i]))
 1580                    {
 1310701581                        return i;
 1582                    }
 1583                }
 1584
 4281585                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            {
 1881211663                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 1881211664                Vector256<ushort> utf16VectorLower = Avx.LoadVector256(src);
 1881211665                Vector256<ushort> utf16VectorUpper = Avx.LoadVector256(src + 16);
 1666
 1881211667                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1668                {
 01669                    str = default;
 01670                    return false;
 1671                }
 1672
 1881211673                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper).AsSByte();
 1881211674                return true;
 1675            }
 1676
 1677            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1678            public unsafe bool TryLoadVector128(ushort* src, ushort* srcStart, int sourceLength, out Vector128<byte> str
 1679            {
 54921680                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 54921681                Vector128<ushort> utf16VectorLower = Vector128.LoadUnsafe(ref *src);
 54921682                Vector128<ushort> utf16VectorUpper = Vector128.LoadUnsafe(ref *src, 8);
 54921683                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1684                {
 01685                    str = default;
 01686                    return false;
 1687                }
 1688
 54921689                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper);
 54921690                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
 210631721                uint t0 = source[0];
 210631722                uint t1 = source[1];
 210631723                uint t2 = source[2];
 210631724                uint t3 = source[3];
 1725
 210631726                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
 210631731                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 210631732                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 210631733                int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 210631734                int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 1735
 210631736                i0 <<= 18;
 210631737                i1 <<= 12;
 210631738                i2 <<= 6;
 1739
 210631740                i0 |= i3;
 210631741                i1 |= i2;
 1742
 210631743                i0 |= i1;
 210631744                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;
 33231752                t2 = EncodingPad;
 33231753                t3 = EncodingPad;
 1754                switch (remaining)
 1755                {
 1756                    case 2:
 11841757                        t0 = srcEnd[-2];
 11841758                        t1 = srcEnd[-1];
 11841759                        break;
 1760                    case 3:
 10521761                        t0 = srcEnd[-3];
 10521762                        t1 = srcEnd[-2];
 10521763                        t2 = srcEnd[-1];
 10521764                        break;
 1765                    case 4:
 10871766                        t0 = srcEnd[-4];
 10871767                        t1 = srcEnd[-3];
 10871768                        t2 = srcEnd[-2];
 10871769                        t3 = srcEnd[-1];
 10871770                        break;
 1771                    default:
 01772                        return -1;
 1773                }
 1774
 33231775                if (((t0 | t1 | t2 | t3) & 0xffffff00) != 0)
 1776                {
 01777                    return -1;
 1778                }
 1779
 33231780                int i0 = Unsafe.Add(ref decodingMap, (IntPtr)t0);
 33231781                int i1 = Unsafe.Add(ref decodingMap, (IntPtr)t1);
 1782
 33231783                i0 <<= 18;
 33231784                i1 <<= 12;
 1785
 33231786                i0 |= i1;
 33231787                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\A9B70965\w\B51109B6\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
 996930            fixed (byte* srcBytes = &MemoryMarshal.GetReference(source))
 996931            fixed (T* destBytes = &MemoryMarshal.GetReference(destination))
 32            {
 996933                int srcLength = source.Length;
 996934                int destLength = destination.Length;
 996935                int maxSrcLength = encoder.GetMaxSrcLength(srcLength, destLength);
 36
 996937                byte* src = srcBytes;
 996938                T* dest = destBytes;
 996939                byte* srcEnd = srcBytes + (uint)srcLength;
 996940                byte* srcMax = srcBytes + (uint)maxSrcLength;
 41
 42#if NET
 996943                if (maxSrcLength >= 16)
 44                {
 885345                    byte* end = srcMax - 64;
 885346                    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
 885354                    end = srcMax - 32;
 885355                    if (Avx2.IsSupported && (end >= src))
 56                    {
 813957                        Avx2Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 58
 813959                        if (src == srcEnd)
 60                            goto DoneExit;
 61                    }
 62
 885363                    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
 885372                    end = srcMax - 16;
 885373                    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
 996982                ref byte encodingMap = ref MemoryMarshal.GetReference(encoder.EncodingMap);
 83
 996984                srcMax -= 2;
 3665485                while (src < srcMax)
 86                {
 2668587                    encoder.EncodeThreeAndWrite(src, dest, ref encodingMap);
 2668588                    src += 3;
 2668589                    dest += 4;
 90                }
 91
 996992                if (srcMax + 2 != srcEnd)
 93                    goto DestinationTooSmallExit;
 94
 996995                if (!isFinalBlock)
 96                {
 332397                    if (src == srcEnd)
 108798                        goto DoneExit;
 99
 100                    goto NeedMoreData;
 101                }
 102
 6646103                if (src + 1 == srcEnd)
 104                {
 2368105                    encoder.EncodeOneOptionallyPadTwo(src, dest, ref encodingMap);
 2368106                    src += 1;
 2368107                    dest += encoder.IncrementPadTwo;
 108                }
 4278109                else if (src + 2 == srcEnd)
 110                {
 2104111                    encoder.EncodeTwoOptionallyPadOne(src, dest, ref encodingMap);
 2104112                    src += 2;
 2104113                    dest += encoder.IncrementPadOne;
 114                }
 115
 116            DoneExit:
 7733117                bytesConsumed = (int)(src - srcBytes);
 7733118                bytesWritten = (int)(dest - destBytes);
 7733119                return OperationStatus.Done;
 120
 121            DestinationTooSmallExit:
 0122                bytesConsumed = (int)(src - srcBytes);
 0123                bytesWritten = (int)(dest - destBytes);
 0124                return OperationStatus.DestinationTooSmall;
 125
 126            NeedMoreData:
 2236127                bytesConsumed = (int)(src - srcBytes);
 2236128                bytesWritten = (int)(dest - destBytes);
 2236129                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
 8139228            Vector256<sbyte> shuffleVec = Vector256.Create(
 8139229                5, 4, 6, 5,
 8139230                8, 7, 9, 8,
 8139231                11, 10, 12, 11,
 8139232                14, 13, 15, 14,
 8139233                1, 0, 2, 1,
 8139234                4, 3, 5, 4,
 8139235                7, 6, 8, 7,
 8139236                10, 9, 11, 10);
 237
 8139238            Vector256<sbyte> lut = Vector256.Create(
 8139239                65, 71, -4, -4,
 8139240                -4, -4, -4, -4,
 8139241                -4, -4, -4, -4,
 8139242                encoder.Avx2LutChar62, encoder.Avx2LutChar63, 0, 0,
 8139243                65, 71, -4, -4,
 8139244                -4, -4, -4, -4,
 8139245                -4, -4, -4, -4,
 8139246                encoder.Avx2LutChar62, encoder.Avx2LutChar63, 0, 0);
 247
 8139248            Vector256<sbyte> maskAC = Vector256.Create(0x0fc0fc00).AsSByte();
 8139249            Vector256<sbyte> maskBB = Vector256.Create(0x003f03f0).AsSByte();
 8139250            Vector256<ushort> shiftAC = Vector256.Create(0x04000040).AsUInt16();
 8139251            Vector256<short> shiftBB = Vector256.Create(0x01000010).AsInt16();
 8139252            Vector256<byte> const51 = Vector256.Create((byte)51);
 8139253            Vector256<sbyte> const25 = Vector256.Create((sbyte)25);
 254
 8139255            byte* src = srcBytes;
 8139256            T* dest = destBytes;
 257
 258            // first load is done at c-0 not to get a segfault
 8139259            AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 8139260            Vector256<sbyte> str = Avx.LoadVector256(src).AsSByte();
 261
 262            // shift by 4 bytes, as required by Reshuffle
 8139263            str = Avx2.PermuteVar8x32(str.AsInt32(), Vector256.Create(
 8139264                0, 0, 0, 0,
 8139265                0, 0, 0, 0,
 8139266                1, 0, 0, 0,
 8139267                2, 0, 0, 0,
 8139268                3, 0, 0, 0,
 8139269                4, 0, 0, 0,
 8139270                5, 0, 0, 0,
 8139271                6, 0, 0, 0).AsInt32()).AsSByte();
 272
 273            // Next loads are done at src-4, as required by Reshuffle, so shift it once
 8139274            src -= 4;
 275
 276585276            while (true)
 277            {
 278                // Reshuffle
 284724279                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
 284724290                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
 284724301                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
 284724311                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
 284724321                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
 284724331                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:
 284724353                Vector256<byte> indices = Avx2.SubtractSaturate(str.AsByte(), const51);
 354
 355                // mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0:
 284724356                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:
 284724359                Vector256<sbyte> tmp = indices.AsSByte() - mask;
 360
 361                // Add offsets to input values:
 284724362                str += Avx2.Shuffle(lut, tmp);
 363
 284724364                encoder.StoreVector256ToDestination(dest, destStart, destLength, str.AsByte());
 365
 284724366                src += 24;
 284724367                dest += 32;
 368
 284724369                if (src > srcEnd)
 370                    break;
 371
 372                // Load at src-4, as required by Reshuffle (already shifted by -4)
 276585373                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 276585374                str = Avx.LoadVector256(src).AsSByte();
 375            }
 376
 8139377            srcBytes = src + 4;
 8139378            destBytes = dest;
 8139379        }
 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        {
 3323581            if (buffer.IsEmpty)
 582            {
 0583                bytesWritten = 0;
 0584                return OperationStatus.Done;
 585            }
 586
 3323587            fixed (byte* bufferBytes = &MemoryMarshal.GetReference(buffer))
 588            {
 3323589                int encodedLength = encoder.GetMaxEncodedLength(dataLength);
 3323590                if (buffer.Length < encodedLength)
 591                {
 0592                    bytesWritten = 0;
 0593                    return OperationStatus.DestinationTooSmall;
 594                }
 595
 3323596                int leftover = (int)((uint)dataLength % 3); // how many bytes after packs of 3
 597
 3323598                uint destinationIndex = encoder.GetInPlaceDestinationLength(encodedLength, leftover);
 3323599                uint sourceIndex = (uint)(dataLength - leftover);
 3323600                ref byte encodingMap = ref MemoryMarshal.GetReference(encoder.EncodingMap);
 601
 602                // encode last pack to avoid conditional in the main loop
 3323603                if (leftover != 0)
 604                {
 2236605                    if (leftover == 1)
 606                    {
 1184607                        encoder.EncodeOneOptionallyPadTwo(bufferBytes + sourceIndex, bufferBytes + destinationIndex, ref
 608                    }
 609                    else
 610                    {
 1052611                        encoder.EncodeTwoOptionallyPadOne(bufferBytes + sourceIndex, bufferBytes + destinationIndex, ref
 612                    }
 613
 2236614                    destinationIndex -= 4;
 615                }
 616
 3323617                sourceIndex -= 3;
 777866618                while ((int)sourceIndex >= 0)
 619                {
 774543620                    uint result = Encode(bufferBytes + sourceIndex, ref encodingMap);
 774543621                    Unsafe.WriteUnaligned(bufferBytes + destinationIndex, result);
 774543622                    destinationIndex -= 4;
 774543623                    sourceIndex -= 3;
 624                }
 625
 3323626                bytesWritten = encodedLength;
 3323627                return OperationStatus.Done;
 628            }
 629        }
 630
 631        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 632        private static unsafe uint Encode(byte* threeBytes, ref byte encodingMap)
 633        {
 774543634            uint t0 = threeBytes[0];
 774543635            uint t1 = threeBytes[1];
 774543636            uint t2 = threeBytes[2];
 637
 774543638            uint i = (t0 << 16) | (t1 << 8) | t2;
 639
 774543640            uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 774543641            uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 774543642            uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 774543643            uint i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F));
 644
 774543645            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            {
 774543653                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
 2368666            uint i = t0 << 8;
 667
 2368668            uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10));
 2368669            uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F));
 670
 671            uint result;
 672
 2368673            if (BitConverter.IsLittleEndian)
 674            {
 2368675                result = (i0 | (i1 << 16));
 676            }
 677            else
 678            {
 679                result = ((i0 << 16) | i1);
 680            }
 681
 2368682            Unsafe.WriteUnaligned(dest, result);
 2368683        }
 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            {
 284724853                AssertWrite<Vector256<short>>(dest, destStart, destLength);
 284724854                (Vector256<ushort> utf16LowVector, Vector256<ushort> utf16HighVector) = Vector256.Widen(str);
 284724855                utf16LowVector.Store(dest);
 284724856                utf16HighVector.Store(dest + 16);
 284724857            }
 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];
 26685887                uint t1 = threeBytes[1];
 26685888                uint t2 = threeBytes[2];
 889
 26685890                uint i = (t0 << 16) | (t1 << 8) | t2;
 891
 26685892                ulong i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 26685893                ulong i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 26685894                ulong i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 26685895                ulong i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F));
 896
 897                ulong result;
 26685898                if (BitConverter.IsLittleEndian)
 899                {
 26685900                    result = i0 | (i1 << 16) | (i2 << 32) | (i3 << 48);
 901                }
 902                else
 903                {
 904                    result = (i0 << 48) | (i1 << 32) | (i2 << 16) | i3;
 905                }
 906
 26685907                Unsafe.WriteUnaligned(destination, result);
 26685908            }
 909        }
 910    }
 911}

C:\h\w\A9B70965\w\B51109B6\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        {
 64069418            int vectorElements = Unsafe.SizeOf<TVector>();
 64069419            byte* readEnd = src + vectorElements;
 64069420            byte* srcEnd = srcStart + srcLength;
 21
 64069422            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            }
 64069427        }
 28
 29        [Conditional("DEBUG")]
 30        internal static unsafe void AssertWrite<TVector>(byte* dest, byte* destStart, int destLength)
 31        {
 28960832            int vectorElements = Unsafe.SizeOf<TVector>();
 28960833            byte* writeEnd = dest + vectorElements;
 28960834            byte* destEnd = destStart + destLength;
 35
 28960836            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            }
 28960841        }
 42
 43        [Conditional("DEBUG")]
 44        internal static unsafe void AssertRead<TVector>(ushort* src, ushort* srcStart, int srcLength)
 45        {
 19361346            int vectorElements = Unsafe.SizeOf<TVector>();
 19361347            ushort* readEnd = src + vectorElements;
 19361348            ushort* srcEnd = srcStart + srcLength;
 49
 19361350            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            }
 19361355        }
 56
 57        [Conditional("DEBUG")]
 58        internal static unsafe void AssertWrite<TVector>(ushort* dest, ushort* destStart, int destLength)
 59        {
 28951260            int vectorElements = Unsafe.SizeOf<TVector>();
 28951261            ushort* writeEnd = dest + vectorElements;
 28951262            ushort* destEnd = destStart + destLength;
 63
 28951264            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            }
 28951269        }
 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\A9B70965\w\B51109B6\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        {
 664614            int length = 0, paddingCount = 0;
 332315            T lastChar = default;
 16
 332317            if (!base64Text.IsEmpty)
 18            {
 19#if NET
 14439020                while (!base64Text.IsEmpty)
 21                {
 14390122                    int index = validatable.IndexOfAnyExcept(base64Text);
 14390123                    if ((uint)index >= (uint)base64Text.Length)
 24                    {
 202625                        length += base64Text.Length;
 202626                        lastChar = base64Text[base64Text.Length - 1];
 202627                        break;
 28                    }
 29
 14187530                    length += index;
 14187531                    if (index != 0)
 32                    {
 13885133                        lastChar = base64Text[index - 1];
 34                    }
 35
 14187536                    T charToValidate = base64Text[index];
 14187537                    base64Text = base64Text.Slice(index + 1);
 38
 14187539                    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.
 29816643                        while (!base64Text.IsEmpty && validatable.IsWhiteSpace(base64Text[0]))
 44                        {
 15709945                            base64Text = base64Text.Slice(1);
 46                        }
 14057847                        continue;
 48                    }
 49
 80850                    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
 2657115                if (!validatable.ValidateAndDecodeLength(lastChar, length, paddingCount, out decodedLength))
 116                {
 117                    goto Fail;
 118                }
 119
 2174120                return true;
 121            }
 122
 0123            decodedLength = 0;
 0124            return true;
 125
 126        Fail:
 1149127            decodedLength = 0;
 1149128            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&)