< Summary

Line coverage
80%
Covered lines: 845
Uncovered lines: 207
Coverable lines: 1052
Total lines: 3157
Line coverage: 80.3%
Branch coverage
78%
Covered branches: 316
Total branches: 402
Branch coverage: 78.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
File 1: DecodeFrom(...)79.48%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(...)85.71%282880.64%
File 1: DecodeWithWhiteSpaceBlockwise(...)92.85%282895%
File 1: DecodeWithWhiteSpaceBlockwise(...)92.85%282895%
File 1: GetPaddingCount(...)83.33%66100%
File 1: GetPaddingCount(...)83.33%66100%
File 1: DecodeWithWhiteSpaceFromUtf8InPlace(...)87.5%242490.9%
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%11100%
File 1: IsInvalidLength(...)100%11100%
File 1: IsValidPadding(...)100%11100%
File 1: SrcLength(...)100%11100%
File 1: TryDecode128Core(...)100%22100%
File 1: TryDecode256Core(...)100%22100%
File 1: TryLoadVector512(...)100%110%
File 1: TryLoadAvxVector256(...)100%11100%
File 1: TryLoadVector128(...)100%11100%
File 1: TryLoadArmVector128x4(...)100%110%
File 1: DecodeFourElements(...)100%11100%
File 1: DecodeRemaining(...)33.33%6661.9%
File 1: IndexOfAnyExceptWhiteSpace(...)100%44100%
File 1: DecodeWithWhiteSpaceBlockwiseWrapper(...)100%11100%
File 1: GetMaxDecodedLength(...)100%11100%
File 1: IsInvalidLength(...)100%110%
File 1: IsValidPadding(...)100%11100%
File 1: SrcLength(...)100%11100%
File 1: TryDecode128Core(...)100%11100%
File 1: TryDecode256Core(...)100%11100%
File 1: TryLoadVector512(...)0%220%
File 1: TryLoadAvxVector256(...)100%22100%
File 1: TryLoadVector128(...)100%22100%
File 1: TryLoadArmVector128x4(...)0%220%
File 1: DecodeFourElements(...)100%22100%
File 1: DecodeRemaining(...)50%8865.21%
File 1: IndexOfAnyExceptWhiteSpace(...)100%44100%
File 1: DecodeWithWhiteSpaceBlockwiseWrapper(...)100%11100%
File 2: EncodeTo(...)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(...)50%44100%
File 2: GetInPlaceDestinationLength(...)100%11100%
File 2: GetMaxEncodedLength(...)100%11100%
File 2: EncodeOneOptionallyPadTwo(...)100%11100%
File 2: EncodeTwoOptionallyPadOne(...)100%11100%
File 2: StoreVector512ToDestination(...)100%110%
File 2: StoreVector256ToDestination(...)100%11100%
File 2: StoreVector128ToDestination(...)100%11100%
File 2: StoreArmVector128x4ToDestination(...)100%110%
File 2: EncodeThreeAndWrite(...)100%11100%
File 2: GetMaxSrcLength(...)100%11100%
File 2: GetInPlaceDestinationLength(...)100%110%
File 2: GetMaxEncodedLength(...)100%110%
File 2: EncodeOneOptionallyPadTwo(...)100%11100%
File 2: EncodeTwoOptionallyPadOne(...)100%11100%
File 2: StoreVector512ToDestination(...)100%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%11100%
File 4: IndexOfAnyExcept(...)100%11100%
File 4: IsWhiteSpace(...)100%11100%
File 4: IsEncodingPad(...)100%11100%
File 4: ValidateAndDecodeLength(...)100%1010100%

File(s)

C:\h\w\B7B10A05\w\A2F5091A\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            {
 9227                bytesConsumed = 0;
 9228                bytesWritten = 0;
 9229                return OperationStatus.Done;
 30            }
 31
 121005032            fixed (T* srcBytes = &MemoryMarshal.GetReference(source))
 121005033            fixed (byte* destBytes = &MemoryMarshal.GetReference(bytes))
 34            {
 121005035                int srcLength = decoder.SrcLength(isFinalBlock, source.Length);
 121005036                int destLength = bytes.Length;
 121005037                int maxSrcLength = srcLength;
 121005038                int decodedLength = decoder.GetMaxDecodedLength(srcLength);
 39
 40                // max. 2 padding chars
 121005041                if (destLength < decodedLength - 2)
 42                {
 43                    // For overflow see comment below
 044                    maxSrcLength = destLength / 3 * 4;
 45                }
 46
 121005047                T* src = srcBytes;
 121005048                byte* dest = destBytes;
 121005049                T* srcEnd = srcBytes + (uint)srcLength;
 121005050                T* srcMax = srcBytes + (uint)maxSrcLength;
 51
 52#if NET
 121005053                if (maxSrcLength >= 24)
 54                {
 62873255                    T* end = srcMax - 88;
 62873256                    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
 62873266                    end = srcMax - 45;
 62873267                    if (Avx2.IsSupported && (end >= src))
 68                    {
 60465869                        Avx2Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 70
 60465871                        if (src == srcEnd)
 72                        {
 73                            goto DoneExit;
 74                        }
 75                    }
 76
 62873277                    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
 62873288                    end = srcMax - 24;
 62873289                    if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian && (end >= src))
 90                    {
 59293691                        Vector128Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 92
 59293693                        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
 1210050103                int skipLastChunk = isFinalBlock ? 4 : 0;
 104
 1210050105                if (destLength >= decodedLength)
 106                {
 1210050107                    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
 1210050127                ref sbyte decodingMap = ref MemoryMarshal.GetReference(decoder.DecodingMap);
 1210050128                srcMax = srcBytes + maxSrcLength;
 129
 3134404130                while (src < srcMax)
 131                {
 2423510132                    int result = decoder.DecodeFourElements(src, ref decodingMap);
 133
 2423510134                    if (result < 0)
 135                    {
 136                        goto InvalidDataExit;
 137                    }
 138
 1924354139                    WriteThreeLowOrderBytes(dest, result);
 1924354140                    src += 4;
 1924354141                    dest += 3;
 142                }
 143
 710894144                if (maxSrcLength != srcLength - skipLastChunk)
 145                {
 146                    goto DestinationTooSmallExit;
 147                }
 148
 710894149                if (src == srcEnd)
 150                {
 558576151                    if (isFinalBlock)
 152                    {
 153                        goto InvalidDataExit;
 154                    }
 155
 554582156                    if (src == srcBytes + source.Length)
 157                    {
 554582158                        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
 152318167                long remaining = srcEnd - src;
 152318168                Debug.Assert(typeof(TBase64Decoder) == typeof(Base64DecoderByte) ? remaining == 4 : remaining < 8);
 152318169                int i0 = decoder.DecodeRemaining(srcEnd, ref decodingMap, remaining, out uint t2, out uint t3);
 170
 152318171                byte* destMax = destBytes + (uint)destLength;
 172
 152318173                if (!decoder.IsValidPadding(t3))
 174                {
 58156175                    int i2 = Unsafe.Add(ref decodingMap, (IntPtr)t2);
 58156176                    int i3 = Unsafe.Add(ref decodingMap, (IntPtr)t3);
 177
 58156178                    i2 <<= 6;
 179
 58156180                    i0 |= i3;
 58156181                    i0 |= i2;
 182
 58156183                    if (i0 < 0)
 184                    {
 185                        goto InvalidDataExit;
 186                    }
 54164187                    if (dest + 3 > destMax)
 188                    {
 189                        goto DestinationTooSmallExit;
 190                    }
 191
 54164192                    WriteThreeLowOrderBytes(dest, i0);
 54164193                    dest += 3;
 54164194                    src += 4;
 195                }
 94162196                else if (!decoder.IsValidPadding(t2))
 197                {
 45094198                    int i2 = Unsafe.Add(ref decodingMap, (IntPtr)t2);
 199
 45094200                    i2 <<= 6;
 201
 45094202                    i0 |= i2;
 203
 45094204                    if ((i0 & 0x800000c0) != 0) // if negative or 2 unused bits are not 0.
 205                    {
 206                        goto InvalidDataExit;
 207                    }
 44688208                    if (dest + 2 > destMax)
 209                    {
 210                        goto DestinationTooSmallExit;
 211                    }
 212
 44688213                    dest[0] = (byte)(i0 >> 16);
 44688214                    dest[1] = (byte)(i0 >> 8);
 44688215                    dest += 2;
 44688216                    src += remaining;
 217                }
 218                else
 219                {
 49068220                    if ((i0 & 0x8000F000) != 0) // if negative or 4 unused bits are not 0.
 221                    {
 222                        goto InvalidDataExit;
 223                    }
 48754224                    if (dest + 1 > destMax)
 225                    {
 226                        goto DestinationTooSmallExit;
 227                    }
 228
 48754229                    dest[0] = (byte)(i0 >> 16);
 48754230                    dest += 1;
 48754231                    src += remaining;
 232                }
 233
 147606234                if (srcLength != source.Length)
 235                {
 236                    goto InvalidDataExit;
 237                }
 238
 239            DoneExit:
 701636240                bytesConsumed = (int)(src - srcBytes);
 701636241                bytesWritten = (int)(dest - destBytes);
 701636242                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:
 508414266                bytesConsumed = (int)(src - srcBytes);
 508414267                bytesWritten = (int)(dest - destBytes);
 508414268                return ignoreWhiteSpace ?
 508414269                    InvalidDataFallback(decoder, source, bytes, ref bytesConsumed, ref bytesWritten, isFinalBlock) :
 508414270                    OperationStatus.InvalidData;
 271            }
 272
 273            static OperationStatus InvalidDataFallback(TBase64Decoder decoder, ReadOnlySpan<T> source, Span<byte> bytes,
 274            {
 42810275                source = source.Slice(bytesConsumed);
 42810276                bytes = bytes.Slice(bytesWritten);
 277
 278                OperationStatus status;
 279                do
 280                {
 488932281                    int localConsumed = decoder.IndexOfAnyExceptWhiteSpace(source);
 488932282                    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.
 162286                        bytesConsumed += source.Length;
 162287                        status = OperationStatus.Done;
 162288                        break;
 289                    }
 290
 488770291                    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.
 23482299                        return decoder.DecodeWithWhiteSpaceBlockwiseWrapper(decoder, source, bytes, ref bytesConsumed, r
 300                    }
 301
 302                    // Skip over the starting whitespace and continue.
 465288303                    bytesConsumed += localConsumed;
 465288304                    source = source.Slice(localConsumed);
 305
 306                    // Try again after consumed whitespace
 465288307                    status = DecodeFrom(decoder, source, bytes, out localConsumed, out int localWritten, isFinalBlock, i
 465288308                    bytesConsumed += localConsumed;
 465288309                    bytesWritten += localWritten;
 310
 465288311                    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
 446122320                    source = source.Slice(localConsumed);
 446122321                    bytes = bytes.Slice(localWritten);
 322                }
 446122323                while (!source.IsEmpty);
 324
 19328325                return status;
 326            }
 327        }
 328
 329        internal static unsafe OperationStatus DecodeFromUtf8InPlace<TBase64Decoder>(TBase64Decoder decoder, Span<byte> 
 330            where TBase64Decoder : IBase64Decoder<byte>
 331        {
 1053086332            if (buffer.IsEmpty)
 333            {
 0334                bytesWritten = 0;
 0335                return OperationStatus.Done;
 336            }
 337
 1053086338            fixed (byte* bufferBytes = &MemoryMarshal.GetReference(buffer))
 339            {
 1053086340                uint bufferLength = (uint)buffer.Length;
 1053086341                uint sourceIndex = 0;
 1053086342                uint destIndex = 0;
 343
 1053086344                if (decoder.IsInvalidLength(buffer.Length))
 345                {
 346                    goto InvalidExit;
 347                }
 348
 1044534349                ref sbyte decodingMap = ref MemoryMarshal.GetReference(decoder.DecodingMap);
 350
 1044534351                if (bufferLength > 4)
 352                {
 2156630353                    while (sourceIndex < bufferLength - 4)
 354                    {
 2144686355                        int result = decoder.DecodeFourElements(bufferBytes + sourceIndex, ref decodingMap);
 2144686356                        if (result < 0)
 357                        {
 358                            goto InvalidExit;
 359                        }
 360
 2141542361                        WriteThreeLowOrderBytes(bufferBytes + destIndex, result);
 2141542362                        destIndex += 3;
 2141542363                        sourceIndex += 4;
 364                    }
 365                }
 366
 367                uint t0;
 368                uint t1;
 369                uint t2;
 370                uint t3;
 371
 1041390372                switch (bufferLength - sourceIndex)
 373                {
 374                    case 2:
 0375                        t0 = bufferBytes[bufferLength - 2];
 0376                        t1 = bufferBytes[bufferLength - 1];
 0377                        t2 = EncodingPad;
 0378                        t3 = EncodingPad;
 0379                        break;
 380                    case 3:
 0381                        t0 = bufferBytes[bufferLength - 3];
 0382                        t1 = bufferBytes[bufferLength - 2];
 0383                        t2 = bufferBytes[bufferLength - 1];
 0384                        t3 = EncodingPad;
 0385                        break;
 386                    case 4:
 1041390387                        t0 = bufferBytes[bufferLength - 4];
 1041390388                        t1 = bufferBytes[bufferLength - 3];
 1041390389                        t2 = bufferBytes[bufferLength - 2];
 1041390390                        t3 = bufferBytes[bufferLength - 1];
 391                        break;
 392                    default:
 393                        goto InvalidExit;
 394                }
 395
 1041390396                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 1041390397                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 398
 1041390399                i0 <<= 18;
 1041390400                i1 <<= 12;
 401
 1041390402                i0 |= i1;
 403
 1041390404                if (!decoder.IsValidPadding(t3))
 405                {
 1032722406                    int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 1032722407                    int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 408
 1032722409                    i2 <<= 6;
 410
 1032722411                    i0 |= i3;
 1032722412                    i0 |= i2;
 413
 1032722414                    if (i0 < 0)
 415                    {
 416                        goto InvalidExit;
 417                    }
 418
 1027716419                    WriteThreeLowOrderBytes(bufferBytes + destIndex, i0);
 1027716420                    destIndex += 3;
 421                }
 8668422                else if (!decoder.IsValidPadding(t2))
 423                {
 4310424                    int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 425
 4310426                    i2 <<= 6;
 427
 4310428                    i0 |= i2;
 429
 4310430                    if ((i0 & 0x800000c0) != 0) // if negative or 2 unused bits are not 0.
 431                    {
 432                        goto InvalidExit;
 433                    }
 434
 4054435                    bufferBytes[destIndex] = (byte)(i0 >> 16);
 4054436                    bufferBytes[destIndex + 1] = (byte)(i0 >> 8);
 4054437                    destIndex += 2;
 438                }
 439                else
 440                {
 4358441                    if ((i0 & 0x8000F000) != 0) // if negative or 4 unused bits are not 0.
 442                    {
 443                        goto InvalidExit;
 444                    }
 445
 4154446                    bufferBytes[destIndex] = (byte)(i0 >> 16);
 4154447                    destIndex += 1;
 448                }
 449
 1035924450                bytesWritten = (int)destIndex;
 1035924451                return OperationStatus.Done;
 452
 453            InvalidExit:
 17162454                bytesWritten = (int)destIndex;
 17162455                return ignoreWhiteSpace ?
 17162456                    DecodeWithWhiteSpaceFromUtf8InPlace<TBase64Decoder>(decoder, buffer, ref bytesWritten, sourceIndex) 
 17162457                    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;
 11588465            Span<byte> buffer = stackalloc byte[BlockSize];
 11588466            OperationStatus status = OperationStatus.Done;
 467
 529554468            while (!source.IsEmpty)
 469            {
 529444470                int encodedIdx = 0;
 529444471                int bufferIdx = 0;
 529444472                int skipped = 0;
 473
 5356280474                for (; encodedIdx < source.Length && (uint)bufferIdx < (uint)buffer.Length; ++encodedIdx)
 475                {
 2413418476                    if (IsWhiteSpace(source[encodedIdx]))
 477                    {
 300192478                        skipped++;
 479                    }
 480                    else
 481                    {
 2113226482                        buffer[bufferIdx] = source[encodedIdx];
 2113226483                        bufferIdx++;
 484                    }
 485                }
 486
 529444487                source = source.Slice(encodedIdx);
 529444488                bytesConsumed += skipped;
 489
 529444490                if (bufferIdx == 0)
 491                {
 492                    continue;
 493                }
 494
 495                bool hasAnotherBlock;
 496
 529334497                if (typeof(TBase64Decoder) == typeof(Base64DecoderByte))
 498                {
 529334499                    hasAnotherBlock = source.Length >= BlockSize;
 500                }
 501                else
 502                {
 0503                    hasAnotherBlock = source.Length > 1;
 504                }
 505
 529334506                bool localIsFinalBlock = !hasAnotherBlock;
 507
 508                // If this block contains padding and there's another block, then only whitespace may follow for being v
 529334509                if (hasAnotherBlock)
 510                {
 522826511                    int paddingCount = GetPaddingCount(decoder, ref buffer[BlockSize - 1]);
 522826512                    if (paddingCount > 0)
 513                    {
 690514                        hasAnotherBlock = false;
 690515                        localIsFinalBlock = true;
 516                    }
 517                }
 518
 529334519                if (localIsFinalBlock && !isFinalBlock)
 520                {
 0521                    localIsFinalBlock = false;
 522                }
 523
 529334524                status = DecodeFrom<TBase64Decoder, byte>(decoder, buffer.Slice(0, bufferIdx), bytes, out int localConsu
 529334525                bytesConsumed += localConsumed;
 529334526                bytesWritten += localWritten;
 527
 529334528                if (status != OperationStatus.Done)
 529                {
 7656530                    return status;
 531                }
 532
 533                // The remaining data must all be whitespace in order to be valid.
 521678534                if (!hasAnotherBlock)
 535                {
 43144536                    for (int i = 0; i < source.Length; ++i)
 537                    {
 20042538                        if (!IsWhiteSpace(source[i]))
 539                        {
 540                            // Revert previous dest increment, since an invalid state followed.
 2292541                            bytesConsumed -= localConsumed;
 2292542                            bytesWritten -= localWritten;
 543
 2292544                            return OperationStatus.InvalidData;
 545                        }
 546
 17750547                        bytesConsumed++;
 548                    }
 549
 1530550                    break;
 551                }
 552
 517856553                bytes = bytes.Slice(localWritten);
 554            }
 555
 1640556            return status;
 557        }
 558
 559        internal static OperationStatus DecodeWithWhiteSpaceBlockwise<TBase64Decoder>(TBase64Decoder decoder, ReadOnlySp
 560            where TBase64Decoder : IBase64Decoder<ushort>
 561        {
 562            const int BlockSize = 4;
 11894563            Span<ushort> buffer = stackalloc ushort[BlockSize];
 11894564            OperationStatus status = OperationStatus.Done;
 565
 24910566            while (!source.IsEmpty)
 567            {
 24876568                int encodedIdx = 0;
 24876569                int bufferIdx = 0;
 24876570                int skipped = 0;
 571
 383280572                for (; encodedIdx < source.Length && (uint)bufferIdx < (uint)buffer.Length; ++encodedIdx)
 573                {
 179202574                    if (IsWhiteSpace(source[encodedIdx]))
 575                    {
 81396576                        skipped++;
 577                    }
 578                    else
 579                    {
 97806580                        buffer[bufferIdx] = source[encodedIdx];
 97806581                        bufferIdx++;
 582                    }
 583                }
 584
 24876585                source = source.Slice(encodedIdx);
 24876586                bytesConsumed += skipped;
 587
 24876588                if (bufferIdx == 0)
 589                {
 590                    continue;
 591                }
 592
 593                bool hasAnotherBlock;
 594
 24842595                if (decoder is Base64DecoderByte)
 596                {
 0597                    hasAnotherBlock = source.Length >= BlockSize;
 598                }
 599                else
 600                {
 24842601                    hasAnotherBlock = source.Length > 1;
 602                }
 603
 24842604                bool localIsFinalBlock = !hasAnotherBlock;
 605
 606                // If this block contains padding and there's another block, then only whitespace may follow for being v
 24842607                if (hasAnotherBlock)
 608                {
 23566609                    int paddingCount = GetPaddingCount(decoder, ref buffer[BlockSize - 1]);
 23566610                    if (paddingCount > 0)
 611                    {
 84612                        hasAnotherBlock = false;
 84613                        localIsFinalBlock = true;
 614                    }
 615                }
 616
 24842617                if (localIsFinalBlock && !isFinalBlock)
 618                {
 0619                    localIsFinalBlock = false;
 620                }
 621
 24842622                status = DecodeFrom(decoder, buffer.Slice(0, bufferIdx), bytes, out int localConsumed, out int localWrit
 24842623                bytesConsumed += localConsumed;
 24842624                bytesWritten += localWritten;
 625
 24842626                if (status != OperationStatus.Done)
 627                {
 11826628                    return status;
 629                }
 630
 631                // The remaining data must all be whitespace in order to be valid.
 13016632                if (!hasAnotherBlock)
 633                {
 192634                    for (int i = 0; i < source.Length; ++i)
 635                    {
 82636                        if (!IsWhiteSpace(source[i]))
 637                        {
 638                            // Revert previous dest increment, since an invalid state followed.
 20639                            bytesConsumed -= localConsumed;
 20640                            bytesWritten -= localWritten;
 641
 20642                            return OperationStatus.InvalidData;
 643                        }
 644                    }
 645
 14646                    bytesConsumed += source.Length;
 14647                    break;
 648                }
 649
 12982650                bytes = bytes.Slice(localWritten);
 651            }
 652
 48653            return status;
 654        }
 655
 656        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 657        private static int GetPaddingCount<TBase64Decoder>(TBase64Decoder decoder, ref byte ptrToLastElement)
 658            where TBase64Decoder : IBase64Decoder<byte>
 659        {
 522826660            int padding = 0;
 661
 522826662            if (decoder.IsValidPadding(ptrToLastElement))
 663            {
 620664                padding++;
 665            }
 666
 522826667            if (decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))
 668            {
 264669                padding++;
 670            }
 671
 522826672            return padding;
 673        }
 674
 675        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 676        private static int GetPaddingCount<TBase64Decoder>(TBase64Decoder decoder, ref ushort ptrToLastElement)
 677            where TBase64Decoder : IBase64Decoder<ushort>
 678        {
 23566679            int padding = 0;
 680
 23566681            if (decoder.IsValidPadding(ptrToLastElement))
 682            {
 64683                padding++;
 684            }
 685
 23566686            if (decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))
 687            {
 62688                padding++;
 689            }
 690
 23566691            return padding;
 692        }
 693
 694        private static OperationStatus DecodeWithWhiteSpaceFromUtf8InPlace<TBase64Decoder>(TBase64Decoder decoder, Span<
 695            where TBase64Decoder : IBase64Decoder<byte>
 696        {
 11884697            int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);
 11884698            Span<byte> buffer = stackalloc byte[BlockSize];
 699
 11884700            OperationStatus status = OperationStatus.Done;
 11884701            int localDestIndex = destIndex;
 11884702            bool hasPaddingBeenProcessed = false;
 11884703            int localBytesWritten = 0;
 704
 1036582705            while (sourceIndex < (uint)source.Length)
 706            {
 1034646707                int bufferIdx = 0;
 708
 5808976709                while (bufferIdx < BlockSize && sourceIndex < (uint)source.Length)
 710                {
 4774330711                    if (!IsWhiteSpace(source[(int)sourceIndex]))
 712                    {
 4126794713                        buffer[bufferIdx] = source[(int)sourceIndex];
 4126794714                        bufferIdx++;
 715                    }
 716
 4774330717                    sourceIndex++;
 718                }
 719
 1034646720                if (bufferIdx == 0)
 721                {
 722                    continue;
 723                }
 724
 1033920725                if (bufferIdx != 4)
 726                {
 727                    // Base64 require 4 bytes, for Base64Url it can be less than 4 bytes but not 1 byte.
 4364728                    if (decoder is Base64DecoderByte || bufferIdx == 1)
 729                    {
 4364730                        status = OperationStatus.InvalidData;
 4364731                        break;
 732                    }
 733                    else // For Base64Url fill empty slots in last block with padding
 734                    {
 0735                        while (bufferIdx < BlockSize)  // Can happen only for last block
 736                        {
 0737                            Debug.Assert(source.Length == sourceIndex);
 0738                            buffer[bufferIdx++] = (byte)EncodingPad;
 739                        }
 740                    }
 741                }
 742
 1029556743                if (hasPaddingBeenProcessed)
 744                {
 745                    // Padding has already been processed, a new valid block cannot be processed.
 746                    // Revert previous dest increment, since an invalid state followed.
 306747                    localDestIndex -= localBytesWritten;
 306748                    status = OperationStatus.InvalidData;
 306749                    break;
 750                }
 751
 1029250752                status = DecodeFromUtf8InPlace<TBase64Decoder>(decoder, buffer, out localBytesWritten, ignoreWhiteSpace:
 1029250753                localDestIndex += localBytesWritten;
 1029250754                hasPaddingBeenProcessed = localBytesWritten < 3;
 755
 1029250756                if (status != OperationStatus.Done)
 757                {
 758                    break;
 759                }
 760
 761                // Write result to source span in place.
 8190696762                for (int i = 0; i < localBytesWritten; i++)
 763                {
 3071376764                    source[localDestIndex - localBytesWritten + i] = buffer[i];
 765                }
 766            }
 767
 11884768            destIndex = localDestIndex;
 11884769            return status;
 770        }
 771
 772#if NET
 773        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 774        [CompExactlyDependsOn(typeof(Avx512BW))]
 775        [CompExactlyDependsOn(typeof(Avx512Vbmi))]
 776        private static unsafe void Avx512Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* de
 777            where TBase64Decoder : IBase64Decoder<T>
 778            where T : unmanaged
 779        {
 780            // Reference for VBMI implementation : https://github.com/WojciechMula/base64simd/tree/master/decode
 781            // If we have AVX512 support, pick off 64 bytes at a time for as long as we can,
 782            // but make sure that we quit before seeing any == markers at the end of the
 783            // string. Also, because we write 16 zeroes at the end of the output, ensure
 784            // that there are at least 22 valid bytes of input data remaining to close the
 785            // gap. 64 + 2 + 22 = 88 bytes.
 0786            T* src = srcBytes;
 0787            byte* dest = destBytes;
 788
 789            // The JIT won't hoist these "constants", so help it
 0790            Vector512<sbyte> vbmiLookup0 = Vector512.Create(decoder.VbmiLookup0).AsSByte();
 0791            Vector512<sbyte> vbmiLookup1 = Vector512.Create(decoder.VbmiLookup1).AsSByte();
 0792            Vector512<byte> vbmiPackedLanesControl = Vector512.Create(
 0793                0x06000102, 0x090a0405, 0x0c0d0e08, 0x16101112,
 0794                0x191a1415, 0x1c1d1e18, 0x26202122, 0x292a2425,
 0795                0x2c2d2e28, 0x36303132, 0x393a3435, 0x3c3d3e38,
 0796                0x00000000, 0x00000000, 0x00000000, 0x00000000).AsByte();
 797
 0798            Vector512<sbyte> mergeConstant0 = Vector512.Create(0x01400140).AsSByte();
 0799            Vector512<short> mergeConstant1 = Vector512.Create(0x00011000).AsInt16();
 800
 801            // This algorithm requires AVX512VBMI support.
 802            // Vbmi was first introduced in CannonLake and is available from IceLake on.
 803            do
 804            {
 0805                if (!decoder.TryLoadVector512(src, srcStart, sourceLength, out Vector512<sbyte> str))
 806                {
 807                    break;
 808                }
 809
 810                // Step 1: Translate encoded Base64 input to their original indices
 811                // This step also checks for invalid inputs and exits.
 812                // After this, we have indices which are verified to have upper 2 bits set to 0 in each byte.
 813                // origIndex      = [...|00dddddd|00cccccc|00bbbbbb|00aaaaaa]
 0814                Vector512<sbyte> origIndex = Avx512Vbmi.PermuteVar64x8x2(vbmiLookup0, str, vbmiLookup1);
 0815                Vector512<sbyte> errorVec = (origIndex.AsInt32() | str.AsInt32()).AsSByte();
 0816                if (errorVec.ExtractMostSignificantBits() != 0)
 817                {
 818                    break;
 819                }
 820
 821                // Step 2: Now we need to reshuffle bits to remove the 0 bits.
 822                // multiAdd1: [...|0000cccc|ccdddddd|0000aaaa|aabbbbbb]
 0823                Vector512<short> multiAdd1 = Avx512BW.MultiplyAddAdjacent(origIndex.AsByte(), mergeConstant0);
 824                // multiAdd1: [...|00000000|aaaaaabb|bbbbcccc|ccdddddd]
 0825                Vector512<int> multiAdd2 = Avx512BW.MultiplyAddAdjacent(multiAdd1, mergeConstant1);
 826
 827                // Step 3: Pack 48 bytes
 0828                str = Avx512Vbmi.PermuteVar64x8(multiAdd2.AsByte(), vbmiPackedLanesControl).AsSByte();
 829
 0830                AssertWrite<Vector512<sbyte>>(dest, destStart, destLength);
 0831                str.Store((sbyte*)dest);
 0832                src += 64;
 0833                dest += 48;
 834            }
 0835            while (src <= srcEnd);
 836
 0837            srcBytes = src;
 0838            destBytes = dest;
 0839        }
 840
 841        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 842        [CompExactlyDependsOn(typeof(Avx2))]
 843        private static unsafe void Avx2Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* dest
 844            where TBase64Decoder : IBase64Decoder<T>
 845            where T : unmanaged
 846        {
 847            // If we have AVX2 support, pick off 32 bytes at a time for as long as we can,
 848            // but make sure that we quit before seeing any == markers at the end of the
 849            // string. Also, because we write 8 zeroes at the end of the output, ensure
 850            // that there are at least 11 valid bytes of input data remaining to close the
 851            // gap. 32 + 2 + 11 = 45 bytes.
 852
 853            // See SSSE3-version below for an explanation of how the code works.
 854
 855            // The JIT won't hoist these "constants", so help it
 604658856            Vector256<sbyte> lutHi = Vector256.Create(decoder.Avx2LutHigh);
 857
 604658858            Vector256<sbyte> lutLo = Vector256.Create(decoder.Avx2LutLow);
 859
 604658860            Vector256<sbyte> lutShift = Vector256.Create(decoder.Avx2LutShift);
 861
 604658862            Vector256<sbyte> packBytesInLaneMask = Vector256.Create(
 604658863                2, 1, 0, 6,
 604658864                5, 4, 10, 9,
 604658865                8, 14, 13, 12,
 604658866                -1, -1, -1, -1,
 604658867                2, 1, 0, 6,
 604658868                5, 4, 10, 9,
 604658869                8, 14, 13, 12,
 604658870                -1, -1, -1, -1);
 871
 604658872            Vector256<int> packLanesControl = Vector256.Create(
 604658873                 0, 0, 0, 0,
 604658874                1, 0, 0, 0,
 604658875                2, 0, 0, 0,
 604658876                4, 0, 0, 0,
 604658877                5, 0, 0, 0,
 604658878                6, 0, 0, 0,
 604658879                -1, -1, -1, -1,
 604658880                -1, -1, -1, -1).AsInt32();
 881
 604658882            Vector256<sbyte> maskSlashOrUnderscore = Vector256.Create((sbyte)decoder.MaskSlashOrUnderscore);
 604658883            Vector256<sbyte> shiftForUnderscore = Vector256.Create((sbyte)33);
 604658884            Vector256<sbyte> mergeConstant0 = Vector256.Create(0x01400140).AsSByte();
 604658885            Vector256<short> mergeConstant1 = Vector256.Create(0x00011000).AsInt16();
 886
 604658887            T* src = srcBytes;
 604658888            byte* dest = destBytes;
 889
 890            //while (remaining >= 45)
 891            do
 892            {
 4006736893                if (!decoder.TryLoadAvxVector256(src, srcStart, sourceLength, out Vector256<sbyte> str))
 894                {
 895                    break;
 896                }
 897
 3998464898                Vector256<sbyte> hiNibbles = ((str.AsInt32()) >>> 4).AsSByte() & maskSlashOrUnderscore;
 899
 3998464900                if (!decoder.TryDecode256Core(str, hiNibbles, maskSlashOrUnderscore, lutLo, lutHi, lutShift, shiftForUnd
 901                {
 902                    break;
 903                }
 904
 905                // in, lower lane, bits, upper case are most significant bits, lower case are least significant bits:
 906                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 907                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 908                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 909                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 910
 3541356911                Vector256<short> merge_ab_and_bc = Avx2.MultiplyAddAdjacent(str.AsByte(), mergeConstant0);
 912                // 0000kkkk LLllllll 0000JJJJ JJjjKKKK
 913                // 0000hhhh IIiiiiii 0000GGGG GGggHHHH
 914                // 0000eeee FFffffff 0000DDDD DDddEEEE
 915                // 0000bbbb CCcccccc 0000AAAA AAaaBBBB
 916
 3541356917                Vector256<int> output = Avx2.MultiplyAddAdjacent(merge_ab_and_bc, mergeConstant1);
 918                // 00000000 JJJJJJjj KKKKkkkk LLllllll
 919                // 00000000 GGGGGGgg HHHHhhhh IIiiiiii
 920                // 00000000 DDDDDDdd EEEEeeee FFffffff
 921                // 00000000 AAAAAAaa BBBBbbbb CCcccccc
 922
 923                // Pack bytes together in each lane:
 3541356924                output = Avx2.Shuffle(output.AsSByte(), packBytesInLaneMask).AsInt32();
 925                // 00000000 00000000 00000000 00000000
 926                // LLllllll KKKKkkkk JJJJJJjj IIiiiiii
 927                // HHHHhhhh GGGGGGgg FFffffff EEEEeeee
 928                // DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa
 929
 930                // Pack lanes
 3541356931                str = Avx2.PermuteVar8x32(output, packLanesControl).AsSByte();
 932
 3541356933                AssertWrite<Vector256<sbyte>>(dest, destStart, destLength);
 3541356934                Avx.Store(dest, str.AsByte());
 935
 3541356936                src += 32;
 3541356937                dest += 24;
 938            }
 3541356939            while (src <= srcEnd);
 940
 604658941            srcBytes = src;
 604658942            destBytes = dest;
 604658943        }
 944
 945        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 946        [CompExactlyDependsOn(typeof(Ssse3))]
 947        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 948        internal static Vector128<byte> SimdShuffle(Vector128<byte> left, Vector128<byte> right, Vector128<byte> mask8F)
 949        {
 1781084950            Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian);
 951
 1781084952            if (Ssse3.IsSupported)
 953            {
 1781084954                return Ssse3.Shuffle(left, right);
 955            }
 956            else
 957            {
 0958                return AdvSimd.Arm64.VectorTableLookup(left, right & mask8F);
 959            }
 960        }
 961
 962        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 963        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 964        private static unsafe void AdvSimdDecode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* d
 965            where TBase64Decoder : IBase64Decoder<T>
 966            where T : unmanaged
 967        {
 968            // C# implementation of https://github.com/aklomp/base64/blob/3a5add8652076612a8407627a42c768736a4263f/lib/a
 969            // If we have AdvSimd support, pick off 64 bytes at a time for as long as we can,
 970            // but make sure that we quit before seeing any == markers at the end of the
 971            // string. 64 + 2 = 66 bytes.
 972
 973            // In the decoding process, we want to map each byte, representing a Base64 value, to its 6-bit (0-63) repre
 974            // It uses the following mapping. Values outside the following groups are invalid and, we abort decoding whe
 975            //
 976            // #    From       To         Char
 977            // 1    [43]       [62]       +
 978            // 2    [47]       [63]       /
 979            // 3    [48..57]   [52..61]   0..9
 980            // 4    [65..90]   [0..25]    A..Z
 981            // 5    [97..122]  [26..51]   a..z
 982            //
 983            // To map an input value to its Base64 representation, we use look-up tables 'decLutOne' and 'decLutTwo'.
 984            // 'decLutOne' helps to map groups 1, 2 and 3 while 'decLutTwo' maps groups 4 and 5 in the above list.
 985            // After mapping, each value falls between 0-63. Consequently, the last six bits of each byte now hold a val
 986            // We then compress four such bytes (with valid 4 * 6 = 24 bits) to three UTF8 bytes (3 * 8 = 24 bits).
 987            // For faster decoding, we use SIMD operations that allow the processing of multiple bytes together.
 988            // However, the compress operation on adjacent values of a vector could be slower. Thus, we de-interleave wh
 989            // the input bytes that store adjacent bytes in separate vectors. This later simplifies the compress step wi
 990            // of logical operations. This requires interleaving while storing the decoded result.
 991
 992            // Values in 'decLutOne' maps input values from 0 to 63.
 993            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
 994            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
 995            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63
 996            //    52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 255, 255, 255
 997            var decLutOne = (Vector128<byte>.AllBitsSet,
 998                             Vector128<byte>.AllBitsSet,
 999                             Vector128.Create(decoder.AdvSimdLutOne3).AsByte(),
 1000                             Vector128.Create(0x37363534, 0x3B3A3938, 0xFFFF3D3C, 0xFFFFFFFF).AsByte());
 1001
 1002            // Values in 'decLutTwo' maps input values from 63 to 127.
 1003            //    0, 255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13
 1004            //   14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255
 1005            //  255, 255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39
 1006            //   40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255
 1007            var decLutTwo = (Vector128.Create(0x0100FF00, 0x05040302, 0x09080706, 0x0D0C0B0A).AsByte(),
 1008                             Vector128.Create(0x11100F0E, 0x15141312, 0x19181716, 0xFFFFFFFF).AsByte(),
 1009                             Vector128.Create(decoder.AdvSimdLutTwo3Uint1, 0x1F1E1D1C, 0x23222120, 0x27262524).AsByte(),
 1010                             Vector128.Create(0x2B2A2928, 0x2F2E2D2C, 0x33323130, 0xFFFFFFFF).AsByte());
 1011
 1012            T* src = srcBytes;
 1013            byte* dest = destBytes;
 1014            Vector128<byte> offset = Vector128.Create<byte>(63);
 1015
 1016            do
 1017            {
 1018                // Step 1: Load 64 bytes and de-interleave.
 1019                if (!decoder.TryLoadArmVector128x4(src, srcStart, sourceLength,
 1020                    out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> st
 1021                {
 1022                    break;
 1023                }
 1024
 1025                // Step 2: Map each valid input to its Base64 value.
 1026                // We use two look-ups to compute partial results and combine them later.
 1027
 1028                // Step 2.1: Detect valid Base64 values from the first three groups. Maps input as,
 1029                //  0 to  63 (Invalid) => 255
 1030                //  0 to  63 (Valid)   => Their Base64 equivalent
 1031                // 64 to 255           => 0
 1032
 1033                // Each input value acts as an index in the look-up table 'decLutOne'.
 1034                // e.g., for group 1: index 43 maps to 62 (Base64 '+').
 1035                // Group 4 and 5 values are out-of-range (>64), so they are mapped to zero.
 1036                // Other valid indices but invalid values are mapped to 255.
 1037                Vector128<byte> decOne1 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str1);
 1038                Vector128<byte> decOne2 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str2);
 1039                Vector128<byte> decOne3 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str3);
 1040                Vector128<byte> decOne4 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str4);
 1041
 1042                // Step 2.2: Detect valid Base64 values from groups 4 and 5. Maps input as,
 1043                //   0 to  63           => 0
 1044                //  64 to 122 (Valid)   => Their Base64 equivalent
 1045                //  64 to 122 (Invalid) => 255
 1046                // 123 to 255           => Remains unchanged
 1047
 1048                // Subtract/offset each input value by 63 so that it can be used as a valid offset.
 1049                // Subtract saturate makes values from the first three groups set to zero that are
 1050                // then mapped to zero in the subsequent look-up.
 1051                Vector128<byte> decTwo1 = AdvSimd.SubtractSaturate(str1, offset);
 1052                Vector128<byte> decTwo2 = AdvSimd.SubtractSaturate(str2, offset);
 1053                Vector128<byte> decTwo3 = AdvSimd.SubtractSaturate(str3, offset);
 1054                Vector128<byte> decTwo4 = AdvSimd.SubtractSaturate(str4, offset);
 1055
 1056                // We use VTBX to map values where out-of-range indices are unchanged.
 1057                decTwo1 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo1, decLutTwo, decTwo1);
 1058                decTwo2 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo2, decLutTwo, decTwo2);
 1059                decTwo3 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo3, decLutTwo, decTwo3);
 1060                decTwo4 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo4, decLutTwo, decTwo4);
 1061
 1062                // Step 3: Combine the partial result.
 1063                // Each look-up above maps valid values to their Base64 equivalent or zero.
 1064                // Thus the intermediate results 'decOne' and 'decTwo' could be OR-ed to get final values.
 1065                str1 = (decOne1 | decTwo1);
 1066                str2 = (decOne2 | decTwo2);
 1067                str3 = (decOne3 | decTwo3);
 1068                str4 = (decOne4 | decTwo4);
 1069
 1070                // Step 4: Detect an invalid input value.
 1071                // Invalid values < 122 are set to 255 while the ones above 122 are unchanged.
 1072                // Check for invalid input, any value larger than 63.
 1073                Vector128<byte> classified = (Vector128.GreaterThan(str1, offset)
 1074                                            | Vector128.GreaterThan(str2, offset)
 1075                                            | Vector128.GreaterThan(str3, offset)
 1076                                            | Vector128.GreaterThan(str4, offset));
 1077
 1078                // Check that all bits are zero.
 1079                if (classified != Vector128<byte>.Zero)
 1080                {
 1081                    break;
 1082                }
 1083
 1084                // Step 5: Compress four bytes into three.
 1085                Vector128<byte> res1 = ((str1 << 2) | (str2 >> 4));
 1086                Vector128<byte> res2 = ((str2 << 4) | (str3 >> 2));
 1087                Vector128<byte> res3 = ((str3 << 6) | str4);
 1088
 1089                // Step 6: Interleave and store decoded results.
 1090                AssertWrite<Vector128<byte>>(dest, destStart, destLength);
 1091                AdvSimd.Arm64.StoreVectorAndZip(dest, (res1, res2, res3));
 1092
 1093                src += 64;
 1094                dest += 48;
 1095            }
 1096            while (src <= srcEnd);
 1097
 1098            srcBytes = src;
 1099            destBytes = dest;
 1100        }
 1101
 1102        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1103        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1104        [CompExactlyDependsOn(typeof(Ssse3))]
 1105        private static unsafe void Vector128Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte*
 1106            where TBase64Decoder : IBase64Decoder<T>
 1107            where T : unmanaged
 1108        {
 1109            Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian);
 1110
 1111            // If we have Vector128 support, pick off 16 bytes at a time for as long as we can,
 1112            // but make sure that we quit before seeing any == markers at the end of the
 1113            // string. Also, because we write four zeroes at the end of the output, ensure
 1114            // that there are at least 6 valid bytes of input data remaining to close the
 1115            // gap. 16 + 2 + 6 = 24 bytes.
 1116
 1117            // The input consists of six character sets in the Base64 alphabet,
 1118            // which we need to map back to the 6-bit values they represent.
 1119            // There are three ranges, two singles, and then there's the rest.
 1120            //
 1121            //  #  From       To        Add  Characters
 1122            //  1  [43]       [62]      +19  +
 1123            //  2  [47]       [63]      +16  /
 1124            //  3  [48..57]   [52..61]   +4  0..9
 1125            //  4  [65..90]   [0..25]   -65  A..Z
 1126            //  5  [97..122]  [26..51]  -71  a..z
 1127            // (6) Everything else => invalid input
 1128
 1129            // We will use LUTS for character validation & offset computation
 1130            // Remember that 0x2X and 0x0X are the same index for _mm_shuffle_epi8,
 1131            // this allows to mask with 0x2F instead of 0x0F and thus save one constant declaration (register and/or mem
 1132
 1133            // For offsets:
 1134            // Perfect hash for lut = ((src>>4)&0x2F)+((src==0x2F)?0xFF:0x00)
 1135            // 0000 = garbage
 1136            // 0001 = /
 1137            // 0010 = +
 1138            // 0011 = 0-9
 1139            // 0100 = A-Z
 1140            // 0101 = A-Z
 1141            // 0110 = a-z
 1142            // 0111 = a-z
 1143            // 1000 >= garbage
 1144
 1145            // For validation, here's the table.
 1146            // A character is valid if and only if the AND of the 2 lookups equals 0:
 1147
 1148            // hi \ lo              0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
 1149            //      LUT             0x15 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x13 0x1A 0x1B 0x1B 0x1B 0x1A
 1150
 1151            // 0000 0X10 char        NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL   BS   HT   LF   VT   FF   CR   SO   SI
 1152            //           andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1153
 1154            // 0001 0x10 char        DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB  CAN   EM  SUB  ESC   FS   GS   RS   US
 1155            //           andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1156
 1157            // 0010 0x01 char               !    "    #    $    %    &    '    (    )    *    +    ,    -    .    /
 1158            //           andlut     0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00 0x01 0x01 0x01 0x00
 1159
 1160            // 0011 0x02 char          0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?
 1161            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x02 0x02 0x02 0x02 0x02
 1162
 1163            // 0100 0x04 char          @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    0
 1164            //           andlut     0x04 0x00 0x00 0x00 0X00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
 1165
 1166            // 0101 0x08 char          P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _
 1167            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08
 1168
 1169            // 0110 0x04 char          `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o
 1170            //           andlut     0x04 0x00 0x00 0x00 0X00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
 1171            // 0111 0X08 char          p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~
 1172            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08
 1173
 1174            // 1000 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1175            // 1001 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1176            // 1010 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1177            // 1011 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1178            // 1100 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1179            // 1101 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1180            // 1110 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1181            // 1111 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1182
 1183            // The JIT won't hoist these "constants", so help it
 5929361184            Vector128<byte> lutHi = Vector128.Create(decoder.Vector128LutHigh).AsByte();
 5929361185            Vector128<byte> lutLo = Vector128.Create(decoder.Vector128LutLow).AsByte();
 5929361186            Vector128<sbyte> lutShift = Vector128.Create(decoder.Vector128LutShift).AsSByte();
 5929361187            Vector128<sbyte> packBytesMask = Vector128.Create(0x06000102, 0x090A0405, 0x0C0D0E08, 0xffffffff).AsSByte();
 5929361188            Vector128<byte> mergeConstant0 = Vector128.Create(0x01400140).AsByte();
 5929361189            Vector128<short> mergeConstant1 = Vector128.Create(0x00011000).AsInt16();
 5929361190            Vector128<byte> one = Vector128<byte>.One;
 5929361191            Vector128<byte> mask2F = Vector128.Create(decoder.MaskSlashOrUnderscore);
 5929361192            Vector128<byte> mask8F = Vector128.Create((byte)0x8F);
 5929361193            Vector128<byte> shiftForUnderscore = Vector128.Create((byte)33);
 5929361194            T* src = srcBytes;
 5929361195            byte* dest = destBytes;
 1196
 1197            //while (remaining >= 24)
 1198            do
 1199            {
 6487141200                if (!decoder.TryLoadVector128(src, srcStart, sourceLength, out Vector128<byte> str))
 1201                {
 1202                    break;
 1203                }
 1204
 1205                // lookup
 6394261206                Vector128<byte> hiNibbles = Vector128.ShiftRightLogical(str.AsInt32(), 4).AsByte() & mask2F;
 1207
 6394261208                if (!decoder.TryDecode128Core(str, hiNibbles, mask2F, mask8F, lutLo, lutHi, lutShift, shiftForUnderscore
 1209                {
 1210                    break;
 1211                }
 1212
 1213                // in, bits, upper case are most significant bits, lower case are least significant bits
 1214                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 1215                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 1216                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 1217                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 1218
 1219                Vector128<short> merge_ab_and_bc;
 1694681220                if (Ssse3.IsSupported)
 1221                {
 1694681222                    merge_ab_and_bc = Ssse3.MultiplyAddAdjacent(str.AsByte(), mergeConstant0.AsSByte());
 1223                }
 1224                else if (AdvSimd.Arm64.IsSupported)
 1225                {
 1226                    Vector128<ushort> evens = AdvSimd.ShiftLeftLogicalWideningLower(AdvSimd.Arm64.UnzipEven(str, one).Ge
 1227                    Vector128<ushort> odds = AdvSimd.Arm64.TransposeOdd(str, Vector128<byte>.Zero).AsUInt16();
 1228                    merge_ab_and_bc = Vector128.Add(evens, odds).AsInt16();
 1229                }
 1230                else
 1231                {
 1232                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 01233                    ThrowUnreachableException();
 1234                    merge_ab_and_bc = default;
 1235                }
 1236                // 0000kkkk LLllllll 0000JJJJ JJjjKKKK
 1237                // 0000hhhh IIiiiiii 0000GGGG GGggHHHH
 1238                // 0000eeee FFffffff 0000DDDD DDddEEEE
 1239                // 0000bbbb CCcccccc 0000AAAA AAaaBBBB
 1240
 1241                Vector128<int> output;
 1694681242                if (Ssse3.IsSupported)
 1243                {
 1694681244                    output = Sse2.MultiplyAddAdjacent(merge_ab_and_bc, mergeConstant1);
 1245                }
 1246                else if (AdvSimd.Arm64.IsSupported)
 1247                {
 1248                    Vector128<int> ievens = AdvSimd.ShiftLeftLogicalWideningLower(AdvSimd.Arm64.UnzipEven(merge_ab_and_b
 1249                    Vector128<int> iodds = AdvSimd.Arm64.TransposeOdd(merge_ab_and_bc, Vector128<short>.Zero).AsInt32();
 1250                    output = Vector128.Add(ievens, iodds).AsInt32();
 1251                }
 1252                else
 1253                {
 1254                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 01255                    ThrowUnreachableException();
 1256                    output = default;
 1257                }
 1258                // 00000000 JJJJJJjj KKKKkkkk LLllllll
 1259                // 00000000 GGGGGGgg HHHHhhhh IIiiiiii
 1260                // 00000000 DDDDDDdd EEEEeeee FFffffff
 1261                // 00000000 AAAAAAaa BBBBbbbb CCcccccc
 1262
 1263                // Pack bytes together:
 1694681264                str = SimdShuffle(output.AsByte(), packBytesMask.AsByte(), mask8F);
 1265                // 00000000 00000000 00000000 00000000
 1266                // LLllllll KKKKkkkk JJJJJJjj IIiiiiii
 1267                // HHHHhhhh GGGGGGgg FFffffff EEEEeeee
 1268                // DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa
 1269
 1694681270                AssertWrite<Vector128<sbyte>>(dest, destStart, destLength);
 1694681271                str.Store(dest);
 1272
 1694681273                src += 16;
 1694681274                dest += 12;
 1275            }
 1694681276            while (src <= srcEnd);
 1277
 5929361278            srcBytes = src;
 5929361279            destBytes = dest;
 5929361280        }
 1281#endif
 1282
 1283        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1284        private static unsafe void WriteThreeLowOrderBytes(byte* destination, int value)
 1285        {
 51477761286            destination[0] = (byte)(value >> 16);
 51477761287            destination[1] = (byte)(value >> 8);
 51477761288            destination[2] = (byte)value;
 51477761289        }
 1290
 1291        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1292        internal static bool IsWhiteSpace(int value)
 1293        {
 97603041294            Debug.Assert(value >= 0 && value <= ushort.MaxValue);
 1295            uint charMinusLowUInt32;
 97603041296            return (int)((0xC8000100U << (short)(charMinusLowUInt32 = (ushort)(value - '\t'))) & (charMinusLowUInt32 - 3
 1297        }
 1298
 1299        internal readonly struct Base64DecoderByte : IBase64Decoder<byte>
 1300        {
 1301            // Pre-computing this table using a custom string(s_characters) and GenerateDecodingMapAndVerify (found in t
 1302            public ReadOnlySpan<sbyte> DecodingMap =>
 22567641303                [
 22567641304                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 22567641305                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 22567641306                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,         //62 is placed at index 43 (
 22567641307                    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,         //52-61 are placed at index 
 22567641308                    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
 22567641309                    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,         //0-25 are placed at index 6
 22567641310                    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
 22567641311                    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,         //26-51 are placed at index 
 22567641312                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,         // Bytes over 122 ('z') are 
 22567641313                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,         // Hence, padding the map wi
 22567641314                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 22567641315                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 22567641316                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 22567641317                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 22567641318                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 22567641319                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 22567641320                ];
 1321
 1322            public ReadOnlySpan<uint> VbmiLookup0 =>
 01323                [
 01324                    0x80808080, 0x80808080, 0x80808080, 0x80808080,
 01325                    0x80808080, 0x80808080, 0x80808080, 0x80808080,
 01326                    0x80808080, 0x80808080, 0x3e808080, 0x3f808080,
 01327                    0x37363534, 0x3b3a3938, 0x80803d3c, 0x80808080
 01328                ];
 1329
 1330            public ReadOnlySpan<uint> VbmiLookup1 =>
 01331                [
 01332                    0x02010080, 0x06050403, 0x0a090807, 0x0e0d0c0b,
 01333                    0x1211100f, 0x16151413, 0x80191817, 0x80808080,
 01334                    0x1c1b1a80, 0x201f1e1d, 0x24232221, 0x28272625,
 01335                    0x2c2b2a29, 0x302f2e2d, 0x80333231, 0x80808080
 01336                ];
 1337
 1338            public ReadOnlySpan<sbyte> Avx2LutHigh =>
 6046581339                [
 6046581340                    0x10, 0x10, 0x01, 0x02,
 6046581341                    0x04, 0x08, 0x04, 0x08,
 6046581342                    0x10, 0x10, 0x10, 0x10,
 6046581343                    0x10, 0x10, 0x10, 0x10,
 6046581344                    0x10, 0x10, 0x01, 0x02,
 6046581345                    0x04, 0x08, 0x04, 0x08,
 6046581346                    0x10, 0x10, 0x10, 0x10,
 6046581347                    0x10, 0x10, 0x10, 0x10
 6046581348                ];
 1349
 1350            public ReadOnlySpan<sbyte> Avx2LutLow =>
 6046581351                [
 6046581352                    0x15, 0x11, 0x11, 0x11,
 6046581353                    0x11, 0x11, 0x11, 0x11,
 6046581354                    0x11, 0x11, 0x13, 0x1A,
 6046581355                    0x1B, 0x1B, 0x1B, 0x1A,
 6046581356                    0x15, 0x11, 0x11, 0x11,
 6046581357                    0x11, 0x11, 0x11, 0x11,
 6046581358                    0x11, 0x11, 0x13, 0x1A,
 6046581359                    0x1B, 0x1B, 0x1B, 0x1A
 6046581360                ];
 1361
 1362            public ReadOnlySpan<sbyte> Avx2LutShift =>
 6046581363                [
 6046581364                    0, 16, 19, 4,
 6046581365                    -65, -65, -71, -71,
 6046581366                    0, 0, 0, 0,
 6046581367                    0, 0, 0, 0,
 6046581368                    0, 16, 19, 4,
 6046581369                    -65, -65, -71, -71,
 6046581370                    0, 0, 0, 0,
 6046581371                    0, 0, 0, 0
 6046581372                ];
 1373
 11975941374            public byte MaskSlashOrUnderscore => (byte)'/';
 1375
 5929361376            public ReadOnlySpan<int> Vector128LutHigh => [0x02011010, 0x08040804, 0x10101010, 0x10101010];
 1377
 5929361378            public ReadOnlySpan<int> Vector128LutLow => [0x11111115, 0x11111111, 0x1A131111, 0x1A1B1B1B];
 1379
 5929361380            public ReadOnlySpan<uint> Vector128LutShift => [0x04131000, 0xb9b9bfbf, 0x00000000, 0x00000000];
 1381
 01382            public ReadOnlySpan<uint> AdvSimdLutOne3 => [0xFFFFFFFF, 0xFFFFFFFF, 0x3EFFFFFF, 0x3FFFFFFF];
 1383
 01384            public uint AdvSimdLutTwo3Uint1 => 0x1B1AFFFF;
 1385
 8512881386            public int GetMaxDecodedLength(int utf8Length) => Base64.GetMaxDecodedFromUtf8Length(utf8Length);
 1387
 10530861388            public bool IsInvalidLength(int bufferLength) => bufferLength % 4 != 0; // only decode input if it is a mult
 1389
 21822141390            public bool IsValidPadding(uint padChar) => padChar == EncodingPad;
 1391
 8512881392            public int SrcLength(bool _, int utf8Length) => utf8Length & ~0x3;  // only decode input up to the closest m
 1393
 1394#if NET
 1395            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1396            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1397            [CompExactlyDependsOn(typeof(Ssse3))]
 1398            public bool TryDecode128Core(
 1399                Vector128<byte> str,
 1400                Vector128<byte> hiNibbles,
 1401                Vector128<byte> maskSlashOrUnderscore,
 1402                Vector128<byte> mask8F,
 1403                Vector128<byte> lutLow,
 1404                Vector128<byte> lutHigh,
 1405                Vector128<sbyte> lutShift,
 1406                Vector128<byte> _,
 1407                out Vector128<byte> result)
 1408            {
 6394261409                Vector128<byte> loNibbles = str & maskSlashOrUnderscore;
 6394261410                Vector128<byte> hi = SimdShuffle(lutHigh, hiNibbles, mask8F);
 6394261411                Vector128<byte> lo = SimdShuffle(lutLow, loNibbles, mask8F);
 1412
 1413                // Check for invalid input: if any "and" values from lo and hi are not zero,
 1414                // fall back on bytewise code to do error checking and reporting:
 6394261415                if ((lo & hi) != Vector128<byte>.Zero)
 1416                {
 4699581417                    result = default;
 4699581418                    return false;
 1419                }
 1420
 1694681421                Vector128<byte> eq2F = Vector128.Equals(str, maskSlashOrUnderscore);
 1694681422                Vector128<byte> shift = SimdShuffle(lutShift.AsByte(), (eq2F + hiNibbles), mask8F);
 1423
 1424                // Now simply add the delta values to the input:
 1694681425                result = str + shift;
 1426
 1694681427                return true;
 1428            }
 1429
 1430            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1431            [CompExactlyDependsOn(typeof(Avx2))]
 1432            public bool TryDecode256Core(
 1433                Vector256<sbyte> str,
 1434                Vector256<sbyte> hiNibbles,
 1435                Vector256<sbyte> maskSlashOrUnderscore,
 1436                Vector256<sbyte> lutLow,
 1437                Vector256<sbyte> lutHigh,
 1438                Vector256<sbyte> lutShift,
 1439                Vector256<sbyte> _,
 1440                out Vector256<sbyte> result)
 1441            {
 39984641442                Vector256<sbyte> loNibbles = str & maskSlashOrUnderscore;
 39984641443                Vector256<sbyte> hi = Avx2.Shuffle(lutHigh, hiNibbles);
 39984641444                Vector256<sbyte> lo = Avx2.Shuffle(lutLow, loNibbles);
 1445
 39984641446                if ((lo & hi) != Vector256<sbyte>.Zero)
 1447                {
 4571081448                    result = default;
 4571081449                    return false;
 1450                }
 1451
 35413561452                Vector256<sbyte> eq2F = Avx2.CompareEqual(str, maskSlashOrUnderscore);
 35413561453                Vector256<sbyte> shift = Avx2.Shuffle(lutShift, eq2F + hiNibbles);
 1454
 35413561455                result = str + shift;
 1456
 35413561457                return true;
 1458            }
 1459
 1460            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1461            public unsafe bool TryLoadVector512(byte* src, byte* srcStart, int sourceLength, out Vector512<sbyte> str)
 1462            {
 01463                AssertRead<Vector512<sbyte>>(src, srcStart, sourceLength);
 01464                str = Vector512.Load(src).AsSByte();
 01465                return true;
 1466            }
 1467
 1468            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1469            [CompExactlyDependsOn(typeof(Avx2))]
 1470            public unsafe bool TryLoadAvxVector256(byte* src, byte* srcStart, int sourceLength, out Vector256<sbyte> str
 1471            {
 15526021472                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 15526021473                str = Avx.LoadVector256(src).AsSByte();
 15526021474                return true;
 1475            }
 1476
 1477            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1478            public unsafe bool TryLoadVector128(byte* src, byte* srcStart, int sourceLength, out Vector128<byte> str)
 1479            {
 3278721480                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 3278721481                str = Vector128.LoadUnsafe(ref *src);
 3278721482                return true;
 1483            }
 1484
 1485            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1486            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1487            public unsafe bool TryLoadArmVector128x4(byte* src, byte* srcStart, int sourceLength,
 1488                out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> str4)
 1489            {
 01490                AssertRead<Vector128<byte>>(src, srcStart, sourceLength);
 01491                (str1, str2, str3, str4) = AdvSimd.Arm64.Load4xVector128AndUnzip(src);
 1492
 01493                return true;
 1494            }
 1495#endif // NET
 1496
 1497            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1498            public unsafe int DecodeFourElements(byte* source, ref sbyte decodingMap)
 1499            {
 1500                // The 'source' span expected to have at least 4 elements, and the 'decodingMap' consists 256 sbytes
 34159281501                uint t0 = source[0];
 34159281502                uint t1 = source[1];
 34159281503                uint t2 = source[2];
 34159281504                uint t3 = source[3];
 1505
 34159281506                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 34159281507                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 34159281508                int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 34159281509                int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 1510
 34159281511                i0 <<= 18;
 34159281512                i1 <<= 12;
 34159281513                i2 <<= 6;
 1514
 34159281515                i0 |= i3;
 34159281516                i1 |= i2;
 1517
 34159281518                i0 |= i1;
 34159281519                return i0;
 1520            }
 1521
 1522            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1523            public unsafe int DecodeRemaining(byte* srcEnd, ref sbyte decodingMap, long remaining, out uint t2, out uint
 1524            {
 1525                uint t0;
 1526                uint t1;
 544441527                t2 = EncodingPad;
 544441528                t3 = EncodingPad;
 1529                switch (remaining)
 1530                {
 1531                    case 2:
 01532                        t0 = srcEnd[-2];
 01533                        t1 = srcEnd[-1];
 01534                        break;
 1535                    case 3:
 01536                        t0 = srcEnd[-3];
 01537                        t1 = srcEnd[-2];
 01538                        t2 = srcEnd[-1];
 01539                        break;
 1540                    case 4:
 544441541                        t0 = srcEnd[-4];
 544441542                        t1 = srcEnd[-3];
 544441543                        t2 = srcEnd[-2];
 544441544                        t3 = srcEnd[-1];
 544441545                        break;
 1546                    default:
 01547                        return -1;
 1548                }
 1549
 544441550                int i0 = Unsafe.Add(ref decodingMap, (IntPtr)t0);
 544441551                int i1 = Unsafe.Add(ref decodingMap, (IntPtr)t1);
 1552
 544441553                i0 <<= 18;
 544441554                i1 <<= 12;
 1555
 544441556                i0 |= i1;
 544441557                return i0;
 1558            }
 1559
 1560            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1561            public int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<byte> span)
 1562            {
 11824881563                for (int i = 0; i < span.Length; i++)
 1564                {
 5910941565                    if (!IsWhiteSpace(span[i]))
 1566                    {
 2620801567                        return i;
 1568                    }
 1569                }
 1570
 1501571                return -1;
 1572            }
 1573
 1574            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1575            public OperationStatus DecodeWithWhiteSpaceBlockwiseWrapper<TBase64Decoder>(TBase64Decoder decoder, ReadOnly
 1576                Span<byte> bytes, ref int bytesConsumed, ref int bytesWritten, bool isFinalBlock = true)
 1577                where TBase64Decoder : IBase64Decoder<byte> =>
 115881578                DecodeWithWhiteSpaceBlockwise(decoder, utf8, bytes, ref bytesConsumed, ref bytesWritten, isFinalBlock);
 1579        }
 1580
 1581        internal readonly struct Base64DecoderChar : IBase64Decoder<ushort>
 1582        {
 3587621583            public ReadOnlySpan<sbyte> DecodingMap => default(Base64DecoderByte).DecodingMap;
 1584
 01585            public ReadOnlySpan<uint> VbmiLookup0 => default(Base64DecoderByte).VbmiLookup0;
 1586
 01587            public ReadOnlySpan<uint> VbmiLookup1 => default(Base64DecoderByte).VbmiLookup1;
 1588
 3028121589            public ReadOnlySpan<sbyte> Avx2LutHigh => default(Base64DecoderByte).Avx2LutHigh;
 1590
 3028121591            public ReadOnlySpan<sbyte> Avx2LutLow => default(Base64DecoderByte).Avx2LutLow;
 1592
 3028121593            public ReadOnlySpan<sbyte> Avx2LutShift => default(Base64DecoderByte).Avx2LutShift;
 1594
 5965381595            public byte MaskSlashOrUnderscore => default(Base64DecoderByte).MaskSlashOrUnderscore;
 1596
 2937261597            public ReadOnlySpan<int> Vector128LutHigh => default(Base64DecoderByte).Vector128LutHigh;
 1598
 2937261599            public ReadOnlySpan<int> Vector128LutLow => default(Base64DecoderByte).Vector128LutLow;
 1600
 2937261601            public ReadOnlySpan<uint> Vector128LutShift => default(Base64DecoderByte).Vector128LutShift;
 1602
 01603            public ReadOnlySpan<uint> AdvSimdLutOne3 => default(Base64DecoderByte).AdvSimdLutOne3;
 1604
 01605            public uint AdvSimdLutTwo3Uint1 => default(Base64DecoderByte).AdvSimdLutTwo3Uint1;
 1606
 3587621607            public int GetMaxDecodedLength(int sourceLength) => Base64.GetMaxDecodedFromUtf8Length(sourceLength);
 1608
 01609            public bool IsInvalidLength(int bufferLength) => bufferLength % 4 != 0;
 1610
 2071081611            public bool IsValidPadding(uint padChar) => padChar == EncodingPad;
 1612
 3587621613            public int SrcLength(bool _, int sourceLength) => sourceLength & ~0x3;
 1614
 1615#if NET
 1616            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1617            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1618            [CompExactlyDependsOn(typeof(Ssse3))]
 1619            public bool TryDecode128Core(Vector128<byte> str, Vector128<byte> hiNibbles, Vector128<byte> maskSlashOrUnde
 1620                Vector128<byte> lutLow, Vector128<byte> lutHigh, Vector128<sbyte> lutShift, Vector128<byte> shiftForUnde
 3115541621                default(Base64DecoderByte).TryDecode128Core(str, hiNibbles, maskSlashOrUnderscore, mask8F, lutLow, lutHi
 1622
 1623            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1624            [CompExactlyDependsOn(typeof(Avx2))]
 1625            public bool TryDecode256Core(Vector256<sbyte> str, Vector256<sbyte> hiNibbles, Vector256<sbyte> maskSlashOrU
 1626                Vector256<sbyte> lutHigh, Vector256<sbyte> lutShift, Vector256<sbyte> shiftForUnderscore, out Vector256<
 24458621627                default(Base64DecoderByte).TryDecode256Core(str, hiNibbles, maskSlashOrUnderscore, lutLow, lutHigh, lutS
 1628
 1629            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1630            public unsafe bool TryLoadVector512(ushort* src, ushort* srcStart, int sourceLength, out Vector512<sbyte> st
 1631            {
 01632                AssertRead<Vector512<ushort>>(src, srcStart, sourceLength);
 01633                Vector512<ushort> utf16VectorLower = Vector512.Load(src);
 01634                Vector512<ushort> utf16VectorUpper = Vector512.Load(src + 32);
 01635                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1636                {
 01637                    str = default;
 01638                    return false;
 1639                }
 1640
 01641                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper).AsSByte();
 01642                return true;
 1643            }
 1644
 1645            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1646            [CompExactlyDependsOn(typeof(Avx2))]
 1647            public unsafe bool TryLoadAvxVector256(ushort* src, ushort* srcStart, int sourceLength, out Vector256<sbyte>
 1648            {
 24541341649                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 24541341650                Vector256<ushort> utf16VectorLower = Avx.LoadVector256(src);
 24541341651                Vector256<ushort> utf16VectorUpper = Avx.LoadVector256(src + 16);
 1652
 24541341653                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1654                {
 82721655                    str = default;
 82721656                    return false;
 1657                }
 1658
 24458621659                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper).AsSByte();
 24458621660                return true;
 1661            }
 1662
 1663            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1664            public unsafe bool TryLoadVector128(ushort* src, ushort* srcStart, int sourceLength, out Vector128<byte> str
 1665            {
 3208421666                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 3208421667                Vector128<ushort> utf16VectorLower = Vector128.LoadUnsafe(ref *src);
 3208421668                Vector128<ushort> utf16VectorUpper = Vector128.LoadUnsafe(ref *src, 8);
 3208421669                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1670                {
 92881671                    str = default;
 92881672                    return false;
 1673                }
 1674
 3115541675                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper);
 3115541676                return true;
 1677            }
 1678
 1679            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1680            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1681            public unsafe bool TryLoadArmVector128x4(ushort* src, ushort* srcStart, int sourceLength,
 1682                out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> str4)
 1683            {
 01684                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 01685                var (s11, s12, s21, s22) = AdvSimd.Arm64.Load4xVector128AndUnzip(src);
 01686                var (s31, s32, s41, s42) = AdvSimd.Arm64.Load4xVector128AndUnzip(src + 32);
 1687
 01688                if (Ascii.VectorContainsNonAsciiChar(s11 | s12 | s21 | s22 | s31 | s32 | s41 | s42))
 1689                {
 01690                    str1 = str2 = str3 = str4 = default;
 01691                    return false;
 1692                }
 1693
 01694                str1 = Ascii.ExtractAsciiVector(s11, s31);
 01695                str2 = Ascii.ExtractAsciiVector(s12, s32);
 01696                str3 = Ascii.ExtractAsciiVector(s21, s41);
 01697                str4 = Ascii.ExtractAsciiVector(s22, s42);
 1698
 01699                return true;
 1700            }
 1701#endif // NET
 1702
 1703            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1704            public unsafe int DecodeFourElements(ushort* source, ref sbyte decodingMap)
 1705            {
 1706                // The 'source' span expected to have at least 4 elements, and the 'decodingMap' consists 256 sbytes
 11522681707                uint t0 = source[0];
 11522681708                uint t1 = source[1];
 11522681709                uint t2 = source[2];
 11522681710                uint t3 = source[3];
 1711
 11522681712                if (((t0 | t1 | t2 | t3) & 0xffffff00) != 0)
 1713                {
 204601714                    return -1; // One or more chars falls outside the 00..ff range, invalid Base64 character.
 1715                }
 1716
 11318081717                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 11318081718                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 11318081719                int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 11318081720                int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 1721
 11318081722                i0 <<= 18;
 11318081723                i1 <<= 12;
 11318081724                i2 <<= 6;
 1725
 11318081726                i0 |= i3;
 11318081727                i1 |= i2;
 1728
 11318081729                i0 |= i1;
 11318081730                return i0;
 1731            }
 1732
 1733            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1734            public unsafe int DecodeRemaining(ushort* srcEnd, ref sbyte decodingMap, long remaining, out uint t2, out ui
 1735            {
 1736                uint t0;
 1737                uint t1;
 978741738                t2 = EncodingPad;
 978741739                t3 = EncodingPad;
 1740                switch (remaining)
 1741                {
 1742                    case 2:
 01743                        t0 = srcEnd[-2];
 01744                        t1 = srcEnd[-1];
 01745                        break;
 1746                    case 3:
 01747                        t0 = srcEnd[-3];
 01748                        t1 = srcEnd[-2];
 01749                        t2 = srcEnd[-1];
 01750                        break;
 1751                    case 4:
 978741752                        t0 = srcEnd[-4];
 978741753                        t1 = srcEnd[-3];
 978741754                        t2 = srcEnd[-2];
 978741755                        t3 = srcEnd[-1];
 978741756                        break;
 1757                    default:
 01758                        return -1;
 1759                }
 1760
 978741761                if (((t0 | t1 | t2 | t3) & 0xffffff00) != 0)
 1762                {
 11061763                    return -1;
 1764                }
 1765
 967681766                int i0 = Unsafe.Add(ref decodingMap, (IntPtr)t0);
 967681767                int i1 = Unsafe.Add(ref decodingMap, (IntPtr)t1);
 1768
 967681769                i0 <<= 18;
 967681770                i1 <<= 12;
 1771
 967681772                i0 |= i1;
 967681773                return i0;
 1774            }
 1775
 1776            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1777            public int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<ushort> span)
 1778            {
 13122841779                for (int i = 0; i < span.Length; i++)
 1780                {
 6561301781                    if (!IsWhiteSpace(span[i]))
 1782                    {
 2266901783                        return i;
 1784                    }
 1785                }
 1786
 121787                return -1;
 1788            }
 1789
 1790            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1791            public OperationStatus DecodeWithWhiteSpaceBlockwiseWrapper<TBase64Decoder>(TBase64Decoder decoder, ReadOnly
 1792                Span<byte> bytes, ref int bytesConsumed, ref int bytesWritten, bool isFinalBlock = true) where TBase64De
 118941793                DecodeWithWhiteSpaceBlockwise(default(Base64DecoderChar), source, bytes, ref bytesConsumed, ref bytesWri
 1794        }
 1795    }
 1796}

C:\h\w\B7B10A05\w\A2F5091A\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
 39331030            fixed (byte* srcBytes = &MemoryMarshal.GetReference(source))
 39331031            fixed (T* destBytes = &MemoryMarshal.GetReference(destination))
 32            {
 39331033                int srcLength = source.Length;
 39331034                int destLength = destination.Length;
 39331035                int maxSrcLength = encoder.GetMaxSrcLength(srcLength, destLength);
 36
 39331037                byte* src = srcBytes;
 39331038                T* dest = destBytes;
 39331039                byte* srcEnd = srcBytes + (uint)srcLength;
 39331040                byte* srcMax = srcBytes + (uint)maxSrcLength;
 41
 42#if NET
 39331043                if (maxSrcLength >= 16)
 44                {
 37210645                    byte* end = srcMax - 64;
 37210646                    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
 37210654                    end = srcMax - 32;
 37210655                    if (Avx2.IsSupported && (end >= src))
 56                    {
 35727657                        Avx2Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 58
 35727659                        if (src == srcEnd)
 60                            goto DoneExit;
 61                    }
 62
 37210663                    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
 37210672                    end = srcMax - 16;
 37210673                    if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian && (end >= src))
 74                    {
 7822675                        Vector128Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 76
 7822677                        if (src == srcEnd)
 78                            goto DoneExit;
 79                    }
 80                }
 81#endif
 39331082                ref byte encodingMap = ref MemoryMarshal.GetReference(encoder.EncodingMap);
 83
 39331084                srcMax -= 2;
 151550285                while (src < srcMax)
 86                {
 112219287                    encoder.EncodeThreeAndWrite(src, dest, ref encodingMap);
 112219288                    src += 3;
 112219289                    dest += 4;
 90                }
 91
 39331092                if (srcMax + 2 != srcEnd)
 93                    goto DestinationTooSmallExit;
 94
 39331095                if (!isFinalBlock)
 96                {
 2383697                    if (src == srcEnd)
 834498                        goto DoneExit;
 99
 100                    goto NeedMoreData;
 101                }
 102
 369474103                if (src + 1 == srcEnd)
 104                {
 52676105                    encoder.EncodeOneOptionallyPadTwo(src, dest, ref encodingMap);
 52676106                    src += 1;
 52676107                    dest += encoder.IncrementPadTwo;
 108                }
 316798109                else if (src + 2 == srcEnd)
 110                {
 48022111                    encoder.EncodeTwoOptionallyPadOne(src, dest, ref encodingMap);
 48022112                    src += 2;
 48022113                    dest += encoder.IncrementPadOne;
 114                }
 115
 116            DoneExit:
 377818117                bytesConsumed = (int)(src - srcBytes);
 377818118                bytesWritten = (int)(dest - destBytes);
 377818119                return OperationStatus.Done;
 120
 121            DestinationTooSmallExit:
 0122                bytesConsumed = (int)(src - srcBytes);
 0123                bytesWritten = (int)(dest - destBytes);
 0124                return OperationStatus.DestinationTooSmall;
 125
 126            NeedMoreData:
 15492127                bytesConsumed = (int)(src - srcBytes);
 15492128                bytesWritten = (int)(dest - destBytes);
 15492129                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
 357276228            Vector256<sbyte> shuffleVec = Vector256.Create(
 357276229                5, 4, 6, 5,
 357276230                8, 7, 9, 8,
 357276231                11, 10, 12, 11,
 357276232                14, 13, 15, 14,
 357276233                1, 0, 2, 1,
 357276234                4, 3, 5, 4,
 357276235                7, 6, 8, 7,
 357276236                10, 9, 11, 10);
 237
 357276238            Vector256<sbyte> lut = Vector256.Create(
 357276239                65, 71, -4, -4,
 357276240                -4, -4, -4, -4,
 357276241                -4, -4, -4, -4,
 357276242                encoder.Avx2LutChar62, encoder.Avx2LutChar63, 0, 0,
 357276243                65, 71, -4, -4,
 357276244                -4, -4, -4, -4,
 357276245                -4, -4, -4, -4,
 357276246                encoder.Avx2LutChar62, encoder.Avx2LutChar63, 0, 0);
 247
 357276248            Vector256<sbyte> maskAC = Vector256.Create(0x0fc0fc00).AsSByte();
 357276249            Vector256<sbyte> maskBB = Vector256.Create(0x003f03f0).AsSByte();
 357276250            Vector256<ushort> shiftAC = Vector256.Create(0x04000040).AsUInt16();
 357276251            Vector256<short> shiftBB = Vector256.Create(0x01000010).AsInt16();
 357276252            Vector256<byte> const51 = Vector256.Create((byte)51);
 357276253            Vector256<sbyte> const25 = Vector256.Create((sbyte)25);
 254
 357276255            byte* src = srcBytes;
 357276256            T* dest = destBytes;
 257
 258            // first load is done at c-0 not to get a segfault
 357276259            AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 357276260            Vector256<sbyte> str = Avx.LoadVector256(src).AsSByte();
 261
 262            // shift by 4 bytes, as required by Reshuffle
 357276263            str = Avx2.PermuteVar8x32(str.AsInt32(), Vector256.Create(
 357276264                0, 0, 0, 0,
 357276265                0, 0, 0, 0,
 357276266                1, 0, 0, 0,
 357276267                2, 0, 0, 0,
 357276268                3, 0, 0, 0,
 357276269                4, 0, 0, 0,
 357276270                5, 0, 0, 0,
 357276271                6, 0, 0, 0).AsInt32()).AsSByte();
 272
 273            // Next loads are done at src-4, as required by Reshuffle, so shift it once
 357276274            src -= 4;
 275
 3471936276            while (true)
 277            {
 278                // Reshuffle
 3829212279                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
 3829212290                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
 3829212301                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
 3829212311                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
 3829212321                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
 3829212331                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:
 3829212353                Vector256<byte> indices = Avx2.SubtractSaturate(str.AsByte(), const51);
 354
 355                // mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0:
 3829212356                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:
 3829212359                Vector256<sbyte> tmp = indices.AsSByte() - mask;
 360
 361                // Add offsets to input values:
 3829212362                str += Avx2.Shuffle(lut, tmp);
 363
 3829212364                encoder.StoreVector256ToDestination(dest, destStart, destLength, str.AsByte());
 365
 3829212366                src += 24;
 3829212367                dest += 32;
 368
 3829212369                if (src > srcEnd)
 370                    break;
 371
 372                // Load at src-4, as required by Reshuffle (already shifted by -4)
 3471936373                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 3471936374                str = Avx.LoadVector256(src).AsSByte();
 375            }
 376
 357276377            srcBytes = src + 4;
 357276378            destBytes = dest;
 357276379        }
 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();
 78226456            Vector128<byte> lut = Vector128.Create(0xFCFC4741, 0xFCFCFCFC, 0xFCFCFCFC, encoder.Ssse3AdvSimdLutE3).AsByte
 78226457            Vector128<byte> maskAC = Vector128.Create(0x0fc0fc00).AsByte();
 78226458            Vector128<byte> maskBB = Vector128.Create(0x003f03f0).AsByte();
 78226459            Vector128<ushort> shiftAC = Vector128.Create(0x04000040).AsUInt16();
 78226460            Vector128<short> shiftBB = Vector128.Create(0x01000010).AsInt16();
 78226461            Vector128<byte> const51 = Vector128.Create((byte)51);
 78226462            Vector128<sbyte> const25 = Vector128.Create((sbyte)25);
 78226463            Vector128<byte> mask8F = Vector128.Create((byte)0x8F);
 464
 78226465            byte* src = srcBytes;
 78226466            T* dest = destBytes;
 467
 468            //while (remaining >= 16)
 469            do
 470            {
 81648471                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 81648472                Vector128<byte> str = Vector128.LoadUnsafe(ref *src);
 473
 474                // Reshuffle
 81648475                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
 81648482                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
 81648489                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;
 81648496                if (Ssse3.IsSupported)
 497                {
 81648498                    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
 81648517                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
 81648523                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;
 81648542                if (Ssse3.IsSupported)
 543                {
 81648544                    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:
 81648558                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:
 81648561                Vector128<sbyte> tmp = indices.AsSByte() - mask;
 562
 563                // Add offsets to input values:
 81648564                str += SimdShuffle(lut, tmp.AsByte(), mask8F);
 565
 81648566                encoder.StoreVector128ToDestination(dest, destStart, destLength, str);
 567
 81648568                src += 12;
 81648569                dest += 16;
 570            }
 81648571            while (src <= srcEnd);
 572
 78226573            srcBytes = src;
 78226574            destBytes = dest;
 78226575        }
 576#endif
 577
 578        internal static unsafe OperationStatus EncodeToUtf8InPlace<TBase64Encoder>(TBase64Encoder encoder, Span<byte> bu
 579            where TBase64Encoder : IBase64Encoder<byte>
 580        {
 23836581            if (buffer.IsEmpty)
 582            {
 0583                bytesWritten = 0;
 0584                return OperationStatus.Done;
 585            }
 586
 23836587            fixed (byte* bufferBytes = &MemoryMarshal.GetReference(buffer))
 588            {
 23836589                int encodedLength = encoder.GetMaxEncodedLength(dataLength);
 23836590                if (buffer.Length < encodedLength)
 591                {
 0592                    bytesWritten = 0;
 0593                    return OperationStatus.DestinationTooSmall;
 594                }
 595
 23836596                int leftover = (int)((uint)dataLength % 3); // how many bytes after packs of 3
 597
 23836598                uint destinationIndex = encoder.GetInPlaceDestinationLength(encodedLength, leftover);
 23836599                uint sourceIndex = (uint)(dataLength - leftover);
 23836600                ref byte encodingMap = ref MemoryMarshal.GetReference(encoder.EncodingMap);
 601
 602                // encode last pack to avoid conditional in the main loop
 23836603                if (leftover != 0)
 604                {
 15492605                    if (leftover == 1)
 606                    {
 8104607                        encoder.EncodeOneOptionallyPadTwo(bufferBytes + sourceIndex, bufferBytes + destinationIndex, ref
 608                    }
 609                    else
 610                    {
 7388611                        encoder.EncodeTwoOptionallyPadOne(bufferBytes + sourceIndex, bufferBytes + destinationIndex, ref
 612                    }
 613
 15492614                    destinationIndex -= 4;
 615                }
 616
 23836617                sourceIndex -= 3;
 4301500618                while ((int)sourceIndex >= 0)
 619                {
 4277664620                    uint result = Encode(bufferBytes + sourceIndex, ref encodingMap);
 4277664621                    Unsafe.WriteUnaligned(bufferBytes + destinationIndex, result);
 4277664622                    destinationIndex -= 4;
 4277664623                    sourceIndex -= 3;
 624                }
 625
 23836626                bytesWritten = encodedLength;
 23836627                return OperationStatus.Done;
 628            }
 629        }
 630
 631        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 632        private static unsafe uint Encode(byte* threeBytes, ref byte encodingMap)
 633        {
 4438304634            uint t0 = threeBytes[0];
 4438304635            uint t1 = threeBytes[1];
 4438304636            uint t2 = threeBytes[2];
 637
 4438304638            uint i = (t0 << 16) | (t1 << 8) | t2;
 639
 4438304640            uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 4438304641            uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 4438304642            uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 4438304643            uint i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F));
 644
 4438304645            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            {
 4484780653                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
 36468666            uint i = t0 << 8;
 667
 36468668            uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10));
 36468669            uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F));
 670
 671            uint result;
 672
 36468673            if (BitConverter.IsLittleEndian)
 674            {
 36468675                result = (i0 | (i1 << 16));
 676            }
 677            else
 678            {
 679                result = ((i0 << 16) | i1);
 680            }
 681
 36468682            Unsafe.WriteUnaligned(dest, result);
 36468683        }
 684
 685        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 686        public static unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, ushort* dest, ref byte encodingMap)
 687        {
 33246688            uint t0 = twoBytes[0];
 33246689            uint t1 = twoBytes[1];
 690
 33246691            uint i = (t0 << 16) | (t1 << 8);
 692
 33246693            ushort i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 33246694            ushort i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 33246695            ushort i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 696
 33246697            dest[0] = i0;
 33246698            dest[1] = i1;
 33246699            dest[2] = i2;
 33246700        }
 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        {
 417147708            public ReadOnlySpan<byte> EncodingMap => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"u
 709
 714552710            public sbyte Avx2LutChar62 => -19;  // char '+' diff
 711
 714552712            public sbyte Avx2LutChar63 => -16;   // char '/' diff
 713
 0714            public ReadOnlySpan<byte> AdvSimdLut4 => "wxyz0123456789+/"u8;
 715
 78226716            public uint Ssse3AdvSimdLutE3 => 0x0000F0ED;
 717
 52676718            public int IncrementPadTwo => 4;
 719
 48022720            public int IncrementPadOne => 4;
 721
 722            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 723            public int GetMaxSrcLength(int srcLength, int destLength) =>
 393310724                srcLength <= MaximumEncodeLength && destLength >= Base64.GetMaxEncodedToUtf8Length(srcLength) ?
 393310725                srcLength : (destLength >> 2) * 3;
 726
 23836727            public uint GetInPlaceDestinationLength(int encodedLength, int _) => (uint)(encodedLength - 4);
 728
 23836729            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            {
 24312734                uint t0 = oneByte[0];
 735
 24312736                uint i = t0 << 8;
 737
 24312738                uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10));
 24312739                uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F));
 740
 24312741                uint result = ConstructResult(i0, i1, EncodingPad, EncodingPad);
 24312742                Unsafe.WriteUnaligned(dest, result);
 24312743            }
 744
 745            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 746            public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, byte* dest, ref byte encodingMap)
 747            {
 22164748                uint t0 = twoBytes[0];
 22164749                uint t1 = twoBytes[1];
 750
 22164751                uint i = (t0 << 16) | (t1 << 8);
 752
 22164753                uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 22164754                uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 22164755                uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 756
 22164757                uint result = ConstructResult(i0, i1, i2, EncodingPad);
 22164758                Unsafe.WriteUnaligned(dest, result);
 22164759            }
 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            {
 1303300773                AssertWrite<Vector256<sbyte>>(dest, destStart, destLength);
 1303300774                Avx.Store(dest, str.AsByte());
 1303300775            }
 776
 777            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 778            public unsafe void StoreVector128ToDestination(byte* dest, byte* destStart, int destLength, Vector128<byte> 
 779            {
 26780780                AssertWrite<Vector128<sbyte>>(dest, destStart, destLength);
 26780781                str.Store(dest);
 26780782            }
 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            {
 160640797                uint result = Encode(threeBytes, ref encodingMap);
 160640798                Unsafe.WriteUnaligned(destination, result);
 160640799            }
 800        }
 801
 802        internal readonly struct Base64EncoderChar : IBase64Encoder<ushort>
 803        {
 333720804            public ReadOnlySpan<byte> EncodingMap => default(Base64EncoderByte).EncodingMap;
 805
 612072806            public sbyte Avx2LutChar62 => default(Base64EncoderByte).Avx2LutChar62;
 807
 612072808            public sbyte Avx2LutChar63 => default(Base64EncoderByte).Avx2LutChar63;
 809
 0810            public ReadOnlySpan<byte> AdvSimdLut4 => default(Base64EncoderByte).AdvSimdLut4;
 811
 52196812            public uint Ssse3AdvSimdLutE3 => default(Base64EncoderByte).Ssse3AdvSimdLutE3;
 813
 36468814            public int IncrementPadTwo => default(Base64EncoderByte).IncrementPadTwo;
 815
 33246816            public int IncrementPadOne => default(Base64EncoderByte).IncrementPadOne;
 817
 818            public int GetMaxSrcLength(int srcLength, int destLength) =>
 333720819                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            {
 36468828                Base64Helper.EncodeOneOptionallyPadTwo(oneByte, dest, ref encodingMap);
 36468829                dest[2] = (ushort)EncodingPad;
 36468830                dest[3] = (ushort)EncodingPad;
 36468831            }
 832
 833            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 834            public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, ushort* dest, ref byte encodingMap)
 835            {
 33246836                Base64Helper.EncodeTwoOptionallyPadOne(twoBytes, dest, ref encodingMap);
 33246837                dest[3] = (ushort)EncodingPad;
 33246838            }
 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            {
 2525912853                AssertWrite<Vector256<short>>(dest, destStart, destLength);
 2525912854                (Vector256<ushort> utf16LowVector, Vector256<ushort> utf16HighVector) = Vector256.Widen(str);
 2525912855                utf16LowVector.Store(dest);
 2525912856                utf16HighVector.Store(dest + 16);
 2525912857            }
 858
 859            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 860            public unsafe void StoreVector128ToDestination(ushort* dest, ushort* destStart, int destLength, Vector128<by
 861            {
 54868862                AssertWrite<Vector128<short>>(dest, destStart, destLength);
 54868863                (Vector128<ushort> utf16LowVector, Vector128<ushort> utf16HighVector) = Vector128.Widen(str);
 54868864                utf16LowVector.Store(dest);
 54868865                utf16HighVector.Store(dest + 8);
 54868866            }
 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];
 961552887                uint t1 = threeBytes[1];
 961552888                uint t2 = threeBytes[2];
 889
 961552890                uint i = (t0 << 16) | (t1 << 8) | t2;
 891
 961552892                ulong i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 961552893                ulong i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 961552894                ulong i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 961552895                ulong i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F));
 896
 897                ulong result;
 961552898                if (BitConverter.IsLittleEndian)
 899                {
 961552900                    result = i0 | (i1 << 16) | (i2 << 32) | (i3 << 48);
 901                }
 902                else
 903                {
 904                    result = (i0 << 48) | (i1 << 32) | (i2 << 16) | i3;
 905                }
 906
 961552907                Unsafe.WriteUnaligned(destination, result);
 961552908            }
 909        }
 910    }
 911}

C:\h\w\B7B10A05\w\A2F5091A\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        {
 579133418            int vectorElements = Unsafe.SizeOf<TVector>();
 579133419            byte* readEnd = src + vectorElements;
 579133420            byte* srcEnd = srcStart + srcLength;
 21
 579133422            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            }
 579133427        }
 28
 29        [Conditional("DEBUG")]
 30        internal static unsafe void AssertWrite<TVector>(byte* dest, byte* destStart, int destLength)
 31        {
 504090432            int vectorElements = Unsafe.SizeOf<TVector>();
 504090433            byte* writeEnd = dest + vectorElements;
 504090434            byte* destEnd = destStart + destLength;
 35
 504090436            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            }
 504090441        }
 42
 43        [Conditional("DEBUG")]
 44        internal static unsafe void AssertRead<TVector>(ushort* src, ushort* srcStart, int srcLength)
 45        {
 277497646            int vectorElements = Unsafe.SizeOf<TVector>();
 277497647            ushort* readEnd = src + vectorElements;
 277497648            ushort* srcEnd = srcStart + srcLength;
 49
 277497650            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            }
 277497655        }
 56
 57        [Conditional("DEBUG")]
 58        internal static unsafe void AssertWrite<TVector>(ushort* dest, ushort* destStart, int destLength)
 59        {
 258078060            int vectorElements = Unsafe.SizeOf<TVector>();
 258078061            ushort* writeEnd = dest + vectorElements;
 258078062            ushort* destEnd = destStart + destLength;
 63
 258078064            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            }
 258078069        }
 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\B7B10A05\w\A2F5091A\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        {
 2383614            int length = 0, paddingCount = 0;
 1191815            T lastChar = default;
 16
 1191817            if (!base64Text.IsEmpty)
 18            {
 19#if NET
 48728820                while (!base64Text.IsEmpty)
 21                {
 48519022                    int index = validatable.IndexOfAnyExcept(base64Text);
 48519023                    if ((uint)index >= (uint)base64Text.Length)
 24                    {
 339625                        length += base64Text.Length;
 339626                        lastChar = base64Text[base64Text.Length - 1];
 339627                        break;
 28                    }
 29
 48179430                    length += index;
 48179431                    if (index != 0)
 32                    {
 47145433                        lastChar = base64Text[index - 1];
 34                    }
 35
 48179436                    T charToValidate = base64Text[index];
 48179437                    base64Text = base64Text.Slice(index + 1);
 38
 48179439                    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.
 60847043                        while (!base64Text.IsEmpty && validatable.IsWhiteSpace(base64Text[0]))
 44                        {
 13310045                            base64Text = base64Text.Slice(1);
 46                        }
 47327247                        continue;
 48                    }
 49
 642450                    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.
 112657                    paddingCount = 1;
 7806258                    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
 3826894                        if (validatable.IsEncodingPad(charToValidateInPadding))
 95                        {
 96                            // There can be at most 2 padding chars.
 42897                            if (paddingCount >= 2)
 98                            {
 99                                goto Fail;
 100                            }
 101
 360102                            paddingCount++;
 103                        }
 37840104                        else if (!validatable.IsWhiteSpace(charToValidateInPadding))
 105                        {
 106                            // Invalid char was found.
 107                            goto Fail;
 108                        }
 109                    }
 110
 400111                    length += paddingCount;
 400112                    break;
 113                }
 114
 5894115                if (!validatable.ValidateAndDecodeLength(lastChar, length, paddingCount, out decodedLength))
 116                {
 117                    goto Fail;
 118                }
 119
 1970120                return true;
 121            }
 122
 0123            decodedLength = 0;
 0124            return true;
 125
 126        Fail:
 9948127            decodedLength = 0;
 9948128            return false;
 129        }
 130
 131        internal interface IBase64Validatable<T>
 132        {
 133#if NET
 134            int IndexOfAnyExcept(ReadOnlySpan<T> span);
 135#else
 136            int DecodeValue(T value);
 137#endif
 138            bool IsWhiteSpace(T value);
 139            bool IsEncodingPad(T value);
 140            bool ValidateAndDecodeLength(T lastChar, int length, int paddingCount, out int decodedLength);
 141        }
 142
 143        internal readonly struct Base64CharValidatable : IBase64Validatable<char>
 144        {
 145#if NET
 0146            private static readonly SearchValues<char> s_validBase64Chars = SearchValues.Create("ABCDEFGHIJKLMNOPQRSTUVW
 147
 0148            public int IndexOfAnyExcept(ReadOnlySpan<char> span) => span.IndexOfAnyExcept(s_validBase64Chars);
 149#else
 150            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 151            public int DecodeValue(char value)
 152            {
 153                if (value > byte.MaxValue)
 154                {
 155                    // Invalid char was found.
 156                    return -2;
 157                }
 158
 159                return default(Base64DecoderByte).DecodingMap[value];
 160            }
 161#endif
 0162            public bool IsWhiteSpace(char value) => Base64Helper.IsWhiteSpace(value);
 0163            public bool IsEncodingPad(char value) => value == EncodingPad;
 164            public bool ValidateAndDecodeLength(char lastChar, int length, int paddingCount, out int decodedLength) =>
 0165                default(Base64ByteValidatable).ValidateAndDecodeLength((byte)lastChar, length, paddingCount, out decoded
 166        }
 167
 168        internal readonly struct Base64ByteValidatable : IBase64Validatable<byte>
 169        {
 170#if NET
 1171            private static readonly SearchValues<byte> s_validBase64Chars = SearchValues.Create(default(Base64EncoderByt
 172
 485190173            public int IndexOfAnyExcept(ReadOnlySpan<byte> span) => span.IndexOfAnyExcept(s_validBase64Chars);
 174#else
 175            public int DecodeValue(byte value) => default(Base64DecoderByte).DecodingMap[value];
 176#endif
 1126006177            public bool IsWhiteSpace(byte value) => Base64Helper.IsWhiteSpace(value);
 44692178            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            {
 5894182                if (length % 4 == 0)
 183                {
 2180184                    int decoded = default(Base64DecoderByte).DecodingMap[lastChar];
 2180185                    if ((paddingCount == 1 && (decoded & 0x03) != 0) ||
 2180186                        (paddingCount == 2 && (decoded & 0x0F) != 0))
 187                    {
 188                        // unused lower bits are not 0, reject input
 210189                        decodedLength = 0;
 210190                        return false;
 191                    }
 192
 193                    // Remove padding to get exact length.
 1970194                    decodedLength = (int)((uint)length / 4 * 3) - paddingCount;
 1970195                    return true;
 196                }
 197
 3714198                decodedLength = 0;
 3714199                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&)