< Summary

Line coverage
80%
Covered lines: 872
Uncovered lines: 208
Coverable lines: 1080
Total lines: 3183
Line coverage: 80.7%
Branch coverage
70%
Covered branches: 308
Total branches: 436
Branch coverage: 70.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(...)67.5%808083.67%
File 1: InvalidDataFallback(TBase64Decoder,System.ReadOnlySpan`1<T>,System.Span`1<System.Byte>,System.Int32&,System.Int32&,System.Boolean)90%1010100%
File 1: DecodeFromUtf8InPlace(...)72.91%484883.33%
File 1: DecodeWithWhiteSpaceBlockwise(...)90%303095.65%
File 1: DecodeWithWhiteSpaceBlockwise(...)90%303095.65%
File 1: GetPaddingCount(...)83.33%66100%
File 1: GetPaddingCount(...)83.33%66100%
File 1: DecodeWithWhiteSpaceFromUtf8InPlace(...)87.5%242490.9%
File 1: Avx512Decode(...)0%660%
File 1: Avx2Decode(...)100%66100%
File 1: SimdShuffle(...)25%8866.66%
File 1: Vector128Decode(...)40.9%222293.33%
File 1: WriteThreeLowOrderBytes(...)100%11100%
File 1: IsWhiteSpace(...)50%22100%
File 1: GetMaxDecodedLength(...)100%11100%
File 1: IsInvalidLength(...)100%11100%
File 1: IsValidPadding(...)100%11100%
File 1: SrcLength(...)100%11100%
File 1: TryDecode128Core(...)100%22100%
File 1: TryDecode256Core(...)100%22100%
File 1: TryLoadVector512(...)100%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(...)65%404084.9%
File 2: Avx512Encode(...)0%440%
File 2: Avx2Encode(...)75%44100%
File 2: Vector128Encode(...)28.57%141493.93%
File 2: EncodeToUtf8InPlace(...)80%101084%
File 2: Encode(...)100%11100%
File 2: ConstructResult(...)100%11100%
File 2: EncodeOneOptionallyPadTwo(...)100%11100%
File 2: EncodeTwoOptionallyPadOne(...)100%11100%
File 2: GetMaxSrcLength(...)50%44100%
File 2: GetInPlaceDestinationLength(...)100%11100%
File 2: GetMaxEncodedLength(...)100%11100%
File 2: EncodeOneOptionallyPadTwo(...)100%11100%
File 2: EncodeTwoOptionallyPadOne(...)100%11100%
File 2: StoreVector512ToDestination(...)100%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)

D:\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;
 6using System.Runtime.CompilerServices;
 7using System.Runtime.InteropServices;
 8using System.Text;
 9using System.Diagnostics.CodeAnalysis;
 10
 11#if NET
 12using System.Runtime.Intrinsics.Arm;
 13using System.Runtime.Intrinsics.Wasm;
 14using System.Runtime.Intrinsics.X86;
 15using System.Runtime.Intrinsics;
 16#endif
 17
 18namespace System.Buffers.Text
 19{
 20    // AVX2 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arch/avx
 21    // Vector128 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arc
 22    internal static partial class Base64Helper
 23    {
 24        [SuppressReadyToRunCanonicalRooting]
 25        internal static unsafe OperationStatus DecodeFrom<TBase64Decoder, T>(TBase64Decoder decoder, ReadOnlySpan<T> sou
 26            out int bytesConsumed, out int bytesWritten, bool isFinalBlock, bool ignoreWhiteSpace)
 27            where TBase64Decoder : IBase64Decoder<T>
 28            where T : unmanaged
 29        {
 30            if (source.IsEmpty)
 31            {
 11232                bytesConsumed = 0;
 11233                bytesWritten = 0;
 11234                return OperationStatus.Done;
 35            }
 36
 15700837            fixed (T* srcBytes = &MemoryMarshal.GetReference(source))
 15700838            fixed (byte* destBytes = &MemoryMarshal.GetReference(bytes))
 39            {
 15700840                int srcLength = decoder.SrcLength(isFinalBlock, source.Length);
 15700841                int destLength = bytes.Length;
 15700842                int maxSrcLength = srcLength;
 15700843                int decodedLength = decoder.GetMaxDecodedLength(srcLength);
 44
 45                // max. 2 padding chars
 15700846                if (destLength < decodedLength - 2)
 47                {
 48                    // For overflow see comment below
 049                    maxSrcLength = destLength / 3 * 4;
 50                }
 51
 15700852                T* src = srcBytes;
 15700853                byte* dest = destBytes;
 15700854                T* srcEnd = srcBytes + (uint)srcLength;
 15700855                T* srcMax = srcBytes + (uint)maxSrcLength;
 56
 57#if NET
 15700858                if (maxSrcLength >= 24)
 59                {
 9975060                    T* end = srcMax - 88;
 9975061                    if (Vector512.IsHardwareAccelerated && Avx512Vbmi.IsSupported && (end >= src))
 62                    {
 063                        Avx512Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 64
 065                        if (src == srcEnd)
 66                        {
 67                            goto DoneExit;
 68                        }
 69                    }
 70
 9975071                    end = srcMax - 45;
 9975072                    if (Avx2.IsSupported && (end >= src))
 73                    {
 8913874                        Avx2Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 75
 8913876                        if (src == srcEnd)
 77                        {
 78                            goto DoneExit;
 79                        }
 80                    }
 81
 9975082                    end = srcMax - 66;
 83                    if (AdvSimd.Arm64.IsSupported && (end >= src))
 84                    {
 85                        AdvSimdDecode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 86
 87                        if (src == srcEnd)
 88                        {
 89                            goto DoneExit;
 90                        }
 91                    }
 92
 9975093                    end = srcMax - 24;
 9975094                    if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && BitConverter.IsLit
 95                    {
 9137896                        Vector128Decode(decoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 97
 9137898                        if (src == srcEnd)
 99                        {
 100                            goto DoneExit;
 101                        }
 102                    }
 103                }
 104#endif
 105
 106                // Last bytes could have padding characters, so process them separately and treat them as valid only if 
 107                // if isFinalBlock is false, padding characters are considered invalid
 157008108                int skipLastChunk = isFinalBlock ? 4 : 0;
 109
 157008110                if (destLength >= decodedLength)
 111                {
 157008112                    maxSrcLength = srcLength - skipLastChunk;
 113                }
 114                else
 115                {
 116                    // This should never overflow since destLength here is less than int.MaxValue / 4 * 3 (i.e. 16106127
 117                    // Therefore, (destLength / 3) * 4 will always be less than 2147483641
 0118                    Debug.Assert(destLength < (int.MaxValue / 4 * 3));
 119#if NET
 0120                    (maxSrcLength, int remainder) = int.DivRem(destLength, 3);
 0121                    maxSrcLength *= 4;
 122#else
 123                    maxSrcLength = (destLength / 3) * 4;
 124                    int remainder = (int)((uint)destLength % 3);
 125#endif
 0126                    if (isFinalBlock && remainder > 0)
 127                    {
 0128                        srcLength &= ~0x3; // In case of Base64UrlDecoder source can be not a multiple of 4, round down 
 129                    }
 130                }
 131
 157008132                ref sbyte decodingMap = ref MemoryMarshal.GetReference(decoder.DecodingMap);
 157008133                srcMax = srcBytes + maxSrcLength;
 134
 467528135                while (src < srcMax)
 136                {
 378584137                    int result = decoder.DecodeFourElements(src, ref decodingMap);
 138
 378584139                    if (result < 0)
 140                    {
 141                        goto InvalidDataExit;
 142                    }
 143
 310520144                    WriteThreeLowOrderBytes(dest, result);
 310520145                    src += 4;
 310520146                    dest += 3;
 147                }
 148
 88944149                if (maxSrcLength != srcLength - skipLastChunk)
 150                {
 151                    goto DestinationTooSmallExit;
 152                }
 153
 88944154                if (src == srcEnd)
 155                {
 43852156                    if (isFinalBlock)
 157                    {
 158                        goto InvalidDataExit;
 159                    }
 160
 42288161                    if (src == srcBytes + source.Length)
 162                    {
 42288163                        goto DoneExit;
 164                    }
 165
 166                    goto NeedMoreDataExit;
 167                }
 168
 169                // if isFinalBlock is false, we will never reach this point
 170                // Handle remaining bytes, for Base64 its always 4 bytes, for Base64Url up to 8 bytes left.
 171                // If more than 4 bytes remained it will end up in DestinationTooSmallExit or InvalidDataExit (might suc
 45092172                long remaining = srcEnd - src;
 45092173                Debug.Assert(typeof(TBase64Decoder) == typeof(Base64DecoderByte) ? remaining == 4 : remaining < 8);
 45092174                int i0 = decoder.DecodeRemaining(srcEnd, ref decodingMap, remaining, out uint t2, out uint t3);
 175
 45092176                if (i0 < 0)
 177                {
 178                    goto InvalidDataExit;
 179                }
 180
 43198181                byte* destMax = destBytes + (uint)destLength;
 182
 43198183                if (!decoder.IsValidPadding(t3))
 184                {
 12362185                    int i2 = Unsafe.Add(ref decodingMap, (IntPtr)t2);
 12362186                    int i3 = Unsafe.Add(ref decodingMap, (IntPtr)t3);
 187
 12362188                    i2 <<= 6;
 189
 12362190                    i0 |= i3;
 12362191                    i0 |= i2;
 192
 12362193                    if (i0 < 0)
 194                    {
 195                        goto InvalidDataExit;
 196                    }
 12168197                    if (dest + 3 > destMax)
 198                    {
 199                        goto DestinationTooSmallExit;
 200                    }
 201
 12168202                    WriteThreeLowOrderBytes(dest, i0);
 12168203                    dest += 3;
 12168204                    src += 4;
 205                }
 30836206                else if (!decoder.IsValidPadding(t2))
 207                {
 13394208                    int i2 = Unsafe.Add(ref decodingMap, (IntPtr)t2);
 209
 13394210                    i2 <<= 6;
 211
 13394212                    i0 |= i2;
 213
 13394214                    if ((i0 & 0x800000c0) != 0) // if negative or 2 unused bits are not 0.
 215                    {
 216                        goto InvalidDataExit;
 217                    }
 13340218                    if (dest + 2 > destMax)
 219                    {
 220                        goto DestinationTooSmallExit;
 221                    }
 222
 13340223                    dest[0] = (byte)(i0 >> 16);
 13340224                    dest[1] = (byte)(i0 >> 8);
 13340225                    dest += 2;
 13340226                    src += remaining;
 227                }
 228                else
 229                {
 17442230                    if ((i0 & 0x8000F000) != 0) // if negative or 4 unused bits are not 0.
 231                    {
 232                        goto InvalidDataExit;
 233                    }
 17400234                    if (dest + 1 > destMax)
 235                    {
 236                        goto DestinationTooSmallExit;
 237                    }
 238
 17400239                    dest[0] = (byte)(i0 >> 16);
 17400240                    dest += 1;
 17400241                    src += remaining;
 242                }
 243
 42908244                if (srcLength != source.Length)
 245                {
 246                    goto InvalidDataExit;
 247                }
 248
 249            DoneExit:
 85112250                bytesConsumed = (int)(src - srcBytes);
 85112251                bytesWritten = (int)(dest - destBytes);
 85112252                return OperationStatus.Done;
 253
 254            DestinationTooSmallExit:
 0255                if (srcLength != source.Length && isFinalBlock)
 256                {
 257                    goto InvalidDataExit; // if input is not a multiple of 4, and there is no more data, return invalid 
 258                }
 259
 0260                if (ignoreWhiteSpace)
 261                {
 262                    // Fall through to InvalidDataFallback which strips whitespace and re-evaluates destination size req
 263                    goto InvalidDataExit;
 264                }
 265
 0266                bytesConsumed = (int)(src - srcBytes);
 0267                bytesWritten = (int)(dest - destBytes);
 0268                return OperationStatus.DestinationTooSmall;
 269
 270            NeedMoreDataExit:
 0271                bytesConsumed = (int)(src - srcBytes);
 0272                bytesWritten = (int)(dest - destBytes);
 0273                return OperationStatus.NeedMoreData;
 274
 275            InvalidDataExit:
 71896276                bytesConsumed = (int)(src - srcBytes);
 71896277                bytesWritten = (int)(dest - destBytes);
 71896278                return ignoreWhiteSpace ?
 71896279                    InvalidDataFallback(decoder, source, bytes, ref bytesConsumed, ref bytesWritten, isFinalBlock) :
 71896280                    OperationStatus.InvalidData;
 281            }
 282
 283            [SuppressReadyToRunCanonicalRooting]
 284            static OperationStatus InvalidDataFallback(TBase64Decoder decoder, ReadOnlySpan<T> source, Span<byte> bytes,
 285            {
 10834286                source = source.Slice(bytesConsumed);
 10834287                bytes = bytes.Slice(bytesWritten);
 288
 289                OperationStatus status;
 290                do
 291                {
 65624292                    int localConsumed = decoder.IndexOfAnyExceptWhiteSpace(source);
 65624293                    if (localConsumed < 0)
 294                    {
 295                        // The remainder of the input is all whitespace. Mark it all as having been consumed,
 296                        // and mark the operation as being done.
 48297                        bytesConsumed += source.Length;
 48298                        status = OperationStatus.Done;
 48299                        break;
 300                    }
 301
 65576302                    if (localConsumed == 0)
 303                    {
 304                        // Non-whitespace was found at the beginning of the input. Since it wasn't consumed
 305                        // by the previous call to DecodeFromUtf8, it must be part of a Base64 sequence
 306                        // that was interrupted by whitespace or something else considered invalid.
 307                        // Fall back to block-wise decoding. This is very slow, but it's also very non-standard
 308                        // formatting of the input; whitespace is typically only found between blocks, such as
 309                        // when Convert.ToBase64String inserts a line break every 76 output characters.
 6918310                        return decoder.DecodeWithWhiteSpaceBlockwiseWrapper(decoder, source, bytes, ref bytesConsumed, r
 311                    }
 312
 313                    // Skip over the starting whitespace and continue.
 58658314                    bytesConsumed += localConsumed;
 58658315                    source = source.Slice(localConsumed);
 316
 317                    // Try again after consumed whitespace
 58658318                    status = DecodeFrom(decoder, source, bytes, out localConsumed, out int localWritten, isFinalBlock, i
 58658319                    bytesConsumed += localConsumed;
 58658320                    bytesWritten += localWritten;
 321
 58658322                    if (status is OperationStatus.Done or OperationStatus.NeedMoreData)
 323                    {
 324                        break;
 325                    }
 326
 327                    // The DecodeFrom helper will return DestinationTooSmall if the destination is too small,
 328                    // regardless of whether it's actually too small once you skip whitespace characters.
 329                    // In that case we loop again and fall back to block-wise decoding if we can't make progress.
 330
 54790331                    source = source.Slice(localConsumed);
 54790332                    bytes = bytes.Slice(localWritten);
 333                }
 54790334                while (!source.IsEmpty);
 335
 3916336                return status;
 337            }
 338        }
 339
 340        [SuppressReadyToRunCanonicalRooting]
 341        internal static unsafe OperationStatus DecodeFromUtf8InPlace<TBase64Decoder>(TBase64Decoder decoder, Span<byte> 
 342            where TBase64Decoder : IBase64Decoder<byte>
 343        {
 344            if (buffer.IsEmpty)
 345            {
 0346                bytesWritten = 0;
 0347                return OperationStatus.Done;
 348            }
 349
 39320350            fixed (byte* bufferBytes = &MemoryMarshal.GetReference(buffer))
 351            {
 39320352                uint bufferLength = (uint)buffer.Length;
 39320353                uint sourceIndex = 0;
 39320354                uint destIndex = 0;
 355
 39320356                if (decoder.IsInvalidLength(buffer.Length))
 357                {
 358                    goto InvalidExit;
 359                }
 360
 37028361                ref sbyte decodingMap = ref MemoryMarshal.GetReference(decoder.DecodingMap);
 362
 363#if NET
 364                // Decode in place using the same vectorized helpers as DecodeFrom. This is safe because the
 365                // write cursor (dest) always trails the read cursor (src) by 25%, so each vector store -- including
 366                // its zero-padded overshoot -- ends at or before the next vector load and never clobbers source
 367                // that hasn't been read yet.
 37028368                if (bufferLength >= 24)
 369                {
 3638370                    byte* src = bufferBytes;
 3638371                    byte* dest = bufferBytes;
 3638372                    int length = (int)bufferLength;
 3638373                    byte* srcMax = bufferBytes + length;
 374
 3638375                    byte* end = srcMax - 88;
 3638376                    if (Vector512.IsHardwareAccelerated && Avx512Vbmi.IsSupported && (end >= src))
 377                    {
 0378                        Avx512Decode(decoder, ref src, ref dest, end, length, length, bufferBytes, bufferBytes);
 379                    }
 380
 3638381                    end = srcMax - 45;
 3638382                    if (Avx2.IsSupported && (end >= src))
 383                    {
 2938384                        Avx2Decode(decoder, ref src, ref dest, end, length, length, bufferBytes, bufferBytes);
 385                    }
 386
 3638387                    end = srcMax - 66;
 388                    if (AdvSimd.Arm64.IsSupported && (end >= src))
 389                    {
 390                        AdvSimdDecode(decoder, ref src, ref dest, end, length, length, bufferBytes, bufferBytes);
 391                    }
 392
 3638393                    end = srcMax - 24;
 3638394                    if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && BitConverter.IsLit
 395                    {
 3062396                        Vector128Decode(decoder, ref src, ref dest, end, length, length, bufferBytes, bufferBytes);
 397                    }
 398
 3638399                    sourceIndex = (uint)(src - bufferBytes);
 3638400                    destIndex = (uint)(dest - bufferBytes);
 401                }
 402#endif
 403
 37028404                if (bufferLength > 4)
 405                {
 12334406                    while (sourceIndex < bufferLength - 4)
 407                    {
 8898408                        int result = decoder.DecodeFourElements(bufferBytes + sourceIndex, ref decodingMap);
 8898409                        if (result < 0)
 410                        {
 411                            goto InvalidExit;
 412                        }
 413
 7820414                        WriteThreeLowOrderBytes(bufferBytes + destIndex, result);
 7820415                        destIndex += 3;
 7820416                        sourceIndex += 4;
 417                    }
 418                }
 419
 420                uint t0;
 421                uint t1;
 422                uint t2;
 423                uint t3;
 424
 35950425                switch (bufferLength - sourceIndex)
 426                {
 427                    case 2:
 0428                        t0 = bufferBytes[bufferLength - 2];
 0429                        t1 = bufferBytes[bufferLength - 1];
 0430                        t2 = EncodingPad;
 0431                        t3 = EncodingPad;
 0432                        break;
 433                    case 3:
 0434                        t0 = bufferBytes[bufferLength - 3];
 0435                        t1 = bufferBytes[bufferLength - 2];
 0436                        t2 = bufferBytes[bufferLength - 1];
 0437                        t3 = EncodingPad;
 0438                        break;
 439                    case 4:
 35950440                        t0 = bufferBytes[bufferLength - 4];
 35950441                        t1 = bufferBytes[bufferLength - 3];
 35950442                        t2 = bufferBytes[bufferLength - 2];
 35950443                        t3 = bufferBytes[bufferLength - 1];
 444                        break;
 445                    default:
 446                        goto InvalidExit;
 447                }
 448
 35950449                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 35950450                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 451
 35950452                i0 <<= 18;
 35950453                i1 <<= 12;
 454
 35950455                i0 |= i1;
 456
 35950457                if (!decoder.IsValidPadding(t3))
 458                {
 33030459                    int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 33030460                    int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 461
 33030462                    i2 <<= 6;
 463
 33030464                    i0 |= i3;
 33030465                    i0 |= i2;
 466
 33030467                    if (i0 < 0)
 468                    {
 469                        goto InvalidExit;
 470                    }
 471
 30700472                    WriteThreeLowOrderBytes(bufferBytes + destIndex, i0);
 30700473                    destIndex += 3;
 474                }
 2920475                else if (!decoder.IsValidPadding(t2))
 476                {
 1298477                    int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 478
 1298479                    i2 <<= 6;
 480
 1298481                    i0 |= i2;
 482
 1298483                    if ((i0 & 0x800000c0) != 0) // if negative or 2 unused bits are not 0.
 484                    {
 485                        goto InvalidExit;
 486                    }
 487
 1222488                    bufferBytes[destIndex] = (byte)(i0 >> 16);
 1222489                    bufferBytes[destIndex + 1] = (byte)(i0 >> 8);
 1222490                    destIndex += 2;
 491                }
 492                else
 493                {
 1622494                    if ((i0 & 0x8000F000) != 0) // if negative or 4 unused bits are not 0.
 495                    {
 496                        goto InvalidExit;
 497                    }
 498
 1560499                    bufferBytes[destIndex] = (byte)(i0 >> 16);
 1560500                    destIndex += 1;
 501                }
 502
 33482503                bytesWritten = (int)destIndex;
 33482504                return OperationStatus.Done;
 505
 506            InvalidExit:
 5838507                bytesWritten = (int)destIndex;
 5838508                return ignoreWhiteSpace ?
 5838509                    DecodeWithWhiteSpaceFromUtf8InPlace<TBase64Decoder>(decoder, buffer, ref bytesWritten, sourceIndex) 
 5838510                    OperationStatus.InvalidData;
 511            }
 512        }
 513
 514        [SuppressReadyToRunCanonicalRooting]
 515        internal static unsafe OperationStatus DecodeWithWhiteSpaceBlockwise<TBase64Decoder>(TBase64Decoder decoder, Rea
 516            where TBase64Decoder : IBase64Decoder<byte>
 517        {
 518            const int BlockSize = 4;
 3434519            Span<byte> buffer = stackalloc byte[BlockSize];
 3434520            OperationStatus status = OperationStatus.Done;
 521
 26830522            while (!source.IsEmpty)
 523            {
 524                // Skip over any leading whitespace
 26810525                if (IsWhiteSpace(source[0]))
 526                {
 3862527                    source = source.Slice(1);
 3862528                    bytesConsumed++;
 3862529                    continue;
 530                }
 531
 22948532                int encodedIdx = 0;
 22948533                int bufferIdx = 0;
 22948534                int skipped = 0;
 535
 222640536                for (; encodedIdx < source.Length && (uint)bufferIdx < (uint)buffer.Length; ++encodedIdx)
 537                {
 99846538                    if (IsWhiteSpace(source[encodedIdx]))
 539                    {
 9116540                        skipped++;
 541                    }
 542                    else
 543                    {
 90730544                        buffer[bufferIdx] = source[encodedIdx];
 90730545                        bufferIdx++;
 546                    }
 547                }
 548
 22948549                source = source.Slice(encodedIdx);
 22948550                Debug.Assert(bufferIdx > 0);
 551
 552                bool hasAnotherBlock;
 553
 22948554                if (typeof(TBase64Decoder) == typeof(Base64DecoderByte))
 555                {
 22948556                    hasAnotherBlock = source.Length >= BlockSize;
 557                }
 558                else
 559                {
 0560                    hasAnotherBlock = source.Length > 1;
 561                }
 562
 22948563                bool localIsFinalBlock = !hasAnotherBlock;
 564
 565                // If this block contains padding and there's another block, then only whitespace may follow for being v
 22948566                if (hasAnotherBlock)
 567                {
 21818568                    int paddingCount = GetPaddingCount(decoder, ref buffer[BlockSize - 1]);
 21818569                    if (paddingCount > 0)
 570                    {
 266571                        hasAnotherBlock = false;
 266572                        localIsFinalBlock = true;
 573                    }
 574                }
 575
 22948576                if (localIsFinalBlock && !isFinalBlock)
 577                {
 0578                    localIsFinalBlock = false;
 579                }
 580
 22948581                status = DecodeFrom<TBase64Decoder, byte>(decoder, buffer.Slice(0, bufferIdx), bytes, out int localConsu
 582
 22948583                if (status != OperationStatus.Done)
 584                {
 2904585                    Debug.Assert(localConsumed == 0 && localWritten == 0, "On failure, should not have consumed or writt
 2904586                    return status;
 587                }
 588
 20044589                bytesConsumed += skipped;
 20044590                bytesConsumed += localConsumed;
 20044591                bytesWritten += localWritten;
 592
 593                // The remaining data must all be whitespace in order to be valid.
 20044594                if (!hasAnotherBlock)
 595                {
 8448596                    for (int i = 0; i < source.Length; ++i)
 597                    {
 4076598                        if (!IsWhiteSpace(source[i]))
 599                        {
 600                            // Revert previous dest increment, since an invalid state followed.
 362601                            bytesConsumed -= localConsumed;
 362602                            bytesWritten -= localWritten;
 603
 362604                            return OperationStatus.InvalidData;
 605                        }
 606
 3714607                        bytesConsumed++;
 608                    }
 609
 148610                    break;
 611                }
 612
 19534613                bytes = bytes.Slice(localWritten);
 19534614                Debug.Assert(!source.IsEmpty);
 615            }
 616
 168617            return status;
 618        }
 619
 620        [SuppressReadyToRunCanonicalRooting]
 621        internal static unsafe OperationStatus DecodeWithWhiteSpaceBlockwise<TBase64Decoder>(TBase64Decoder decoder, Rea
 622            where TBase64Decoder : IBase64Decoder<ushort>
 623        {
 624            const int BlockSize = 4;
 3484625            Span<ushort> buffer = stackalloc ushort[BlockSize];
 3484626            OperationStatus status = OperationStatus.Done;
 627
 25678628            while (!source.IsEmpty)
 629            {
 630                // Skip over any leading whitespace
 25664631                if (IsWhiteSpace(source[0]))
 632                {
 6356633                    source = source.Slice(1);
 6356634                    bytesConsumed++;
 6356635                    continue;
 636                }
 637
 19308638                int encodedIdx = 0;
 19308639                int bufferIdx = 0;
 19308640                int skipped = 0;
 641
 207932642                for (; encodedIdx < source.Length && (uint)bufferIdx < (uint)buffer.Length; ++encodedIdx)
 643                {
 94312644                    if (IsWhiteSpace(source[encodedIdx]))
 645                    {
 17884646                        skipped++;
 647                    }
 648                    else
 649                    {
 76428650                        buffer[bufferIdx] = source[encodedIdx];
 76428651                        bufferIdx++;
 652                    }
 653                }
 654
 19308655                source = source.Slice(encodedIdx);
 19308656                Debug.Assert(bufferIdx > 0);
 657
 658                bool hasAnotherBlock;
 659
 19308660                if (decoder is Base64DecoderChar)
 661                {
 19308662                    hasAnotherBlock = source.Length >= BlockSize;
 663                }
 664                else
 665                {
 0666                    hasAnotherBlock = source.Length > 1;
 667                }
 668
 19308669                bool localIsFinalBlock = !hasAnotherBlock;
 670
 671                // If this block contains padding and there's another block, then only whitespace may follow for being v
 19308672                if (hasAnotherBlock)
 673                {
 18440674                    int paddingCount = GetPaddingCount(decoder, ref buffer[BlockSize - 1]);
 18440675                    if (paddingCount > 0)
 676                    {
 94677                        hasAnotherBlock = false;
 94678                        localIsFinalBlock = true;
 679                    }
 680                }
 681
 19308682                if (localIsFinalBlock && !isFinalBlock)
 683                {
 0684                    localIsFinalBlock = false;
 685                }
 686
 19308687                status = DecodeFrom(decoder, buffer.Slice(0, bufferIdx), bytes, out int localConsumed, out int localWrit
 688
 19308689                if (status != OperationStatus.Done)
 690                {
 3368691                    Debug.Assert(localConsumed == 0 && localWritten == 0, "On failure, should not have consumed or writt
 3368692                    return status;
 693                }
 694
 15940695                bytesConsumed += skipped;
 15940696                bytesConsumed += localConsumed;
 15940697                bytesWritten += localWritten;
 698
 699                // The remaining data must all be whitespace in order to be valid.
 15940700                if (!hasAnotherBlock)
 701                {
 1020702                    for (int i = 0; i < source.Length; ++i)
 703                    {
 478704                        if (!IsWhiteSpace(source[i]))
 705                        {
 706                            // Revert previous dest increment, since an invalid state followed.
 70707                            bytesConsumed -= localConsumed;
 70708                            bytesWritten -= localWritten;
 709
 70710                            return OperationStatus.InvalidData;
 711                        }
 712                    }
 713
 32714                    bytesConsumed += source.Length;
 32715                    break;
 716                }
 717
 15838718                bytes = bytes.Slice(localWritten);
 15838719                Debug.Assert(!source.IsEmpty);
 720            }
 721
 46722            return status;
 723        }
 724
 725        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 726        [SuppressReadyToRunCanonicalRooting]
 727        private static int GetPaddingCount<TBase64Decoder>(TBase64Decoder decoder, ref byte ptrToLastElement)
 728            where TBase64Decoder : IBase64Decoder<byte>
 729        {
 21818730            int padding = 0;
 731
 21818732            if (decoder.IsValidPadding(ptrToLastElement))
 733            {
 252734                padding++;
 735            }
 736
 21818737            if (decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))
 738            {
 134739                padding++;
 740            }
 741
 21818742            return padding;
 743        }
 744
 745        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 746        [SuppressReadyToRunCanonicalRooting]
 747        private static int GetPaddingCount<TBase64Decoder>(TBase64Decoder decoder, ref ushort ptrToLastElement)
 748            where TBase64Decoder : IBase64Decoder<ushort>
 749        {
 18440750            int padding = 0;
 751
 18440752            if (decoder.IsValidPadding(ptrToLastElement))
 753            {
 86754                padding++;
 755            }
 756
 18440757            if (decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))
 758            {
 10759                padding++;
 760            }
 761
 18440762            return padding;
 763        }
 764
 765        [SuppressReadyToRunCanonicalRooting]
 766        private static unsafe OperationStatus DecodeWithWhiteSpaceFromUtf8InPlace<TBase64Decoder>(TBase64Decoder decoder
 767            where TBase64Decoder : IBase64Decoder<byte>
 768        {
 3480769            int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);
 3480770            Span<byte> buffer = stackalloc byte[BlockSize];
 771
 3480772            OperationStatus status = OperationStatus.Done;
 3480773            int localDestIndex = destIndex;
 3480774            bool hasPaddingBeenProcessed = false;
 3480775            int localBytesWritten = 0;
 776
 33512777            while (sourceIndex < (uint)source.Length)
 778            {
 33298779                int bufferIdx = 0;
 780
 206338781                while (bufferIdx < BlockSize && sourceIndex < (uint)source.Length)
 782                {
 173040783                    if (!IsWhiteSpace(source[(int)sourceIndex]))
 784                    {
 131098785                        buffer[bufferIdx] = source[(int)sourceIndex];
 131098786                        bufferIdx++;
 787                    }
 788
 173040789                    sourceIndex++;
 790                }
 791
 33298792                if (bufferIdx == 0)
 793                {
 794                    continue;
 795                }
 796
 33200797                if (bufferIdx != 4)
 798                {
 799                    // Base64 require 4 bytes, for Base64Url it can be less than 4 bytes but not 1 byte.
 834800                    if (decoder is Base64DecoderByte || bufferIdx == 1)
 801                    {
 834802                        status = OperationStatus.InvalidData;
 834803                        break;
 804                    }
 805                    else // For Base64Url fill empty slots in last block with padding
 806                    {
 0807                        while (bufferIdx < BlockSize)  // Can happen only for last block
 808                        {
 0809                            Debug.Assert(source.Length == sourceIndex);
 0810                            buffer[bufferIdx++] = (byte)EncodingPad;
 811                        }
 812                    }
 813                }
 814
 32366815                if (hasPaddingBeenProcessed)
 816                {
 817                    // Padding has already been processed, a new valid block cannot be processed.
 818                    // Revert previous dest increment, since an invalid state followed.
 74819                    localDestIndex -= localBytesWritten;
 74820                    status = OperationStatus.InvalidData;
 74821                    break;
 822                }
 823
 32292824                status = DecodeFromUtf8InPlace<TBase64Decoder>(decoder, buffer, out localBytesWritten, ignoreWhiteSpace:
 32292825                localDestIndex += localBytesWritten;
 32292826                hasPaddingBeenProcessed = localBytesWritten < 3;
 827
 32292828                if (status != OperationStatus.Done)
 829                {
 830                    break;
 831                }
 832
 833                // Write result to source span in place.
 238768834                for (int i = 0; i < localBytesWritten; i++)
 835                {
 89450836                    source[localDestIndex - localBytesWritten + i] = buffer[i];
 837                }
 838            }
 839
 3480840            destIndex = localDestIndex;
 3480841            return status;
 842        }
 843
 844#if NET
 845        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 846        [CompExactlyDependsOn(typeof(Avx512BW))]
 847        [CompExactlyDependsOn(typeof(Avx512Vbmi))]
 848        [SuppressReadyToRunCanonicalRooting]
 849        private static unsafe void Avx512Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* de
 850            where TBase64Decoder : IBase64Decoder<T>
 851            where T : unmanaged
 852        {
 853            // Reference for VBMI implementation : https://github.com/WojciechMula/base64simd/tree/master/decode
 854            // If we have AVX512 support, pick off 64 bytes at a time for as long as we can,
 855            // but make sure that we quit before seeing any == markers at the end of the
 856            // string. Also, because we write 16 zeroes at the end of the output, ensure
 857            // that there are at least 22 valid bytes of input data remaining to close the
 858            // gap. 64 + 2 + 22 = 88 bytes.
 0859            T* src = srcBytes;
 0860            byte* dest = destBytes;
 861
 862            // The JIT won't hoist these "constants", so help it
 0863            Vector512<sbyte> vbmiLookup0 = Vector512.Create(decoder.VbmiLookup0).AsSByte();
 0864            Vector512<sbyte> vbmiLookup1 = Vector512.Create(decoder.VbmiLookup1).AsSByte();
 0865            Vector512<byte> vbmiPackedLanesControl = Vector512.Create(
 0866                0x06000102, 0x090a0405, 0x0c0d0e08, 0x16101112,
 0867                0x191a1415, 0x1c1d1e18, 0x26202122, 0x292a2425,
 0868                0x2c2d2e28, 0x36303132, 0x393a3435, 0x3c3d3e38,
 0869                0x00000000, 0x00000000, 0x00000000, 0x00000000).AsByte();
 870
 0871            Vector512<sbyte> mergeConstant0 = Vector512.Create(0x01400140).AsSByte();
 0872            Vector512<short> mergeConstant1 = Vector512.Create(0x00011000).AsInt16();
 873
 874            // This algorithm requires AVX512VBMI support.
 875            // Vbmi was first introduced in CannonLake and is available from IceLake on.
 876            do
 877            {
 0878                if (!decoder.TryLoadVector512(src, srcStart, sourceLength, out Vector512<sbyte> str))
 879                {
 880                    break;
 881                }
 882
 883                // Step 1: Translate encoded Base64 input to their original indices
 884                // This step also checks for invalid inputs and exits.
 885                // After this, we have indices which are verified to have upper 2 bits set to 0 in each byte.
 886                // origIndex      = [...|00dddddd|00cccccc|00bbbbbb|00aaaaaa]
 0887                Vector512<sbyte> origIndex = Avx512Vbmi.PermuteVar64x8x2(vbmiLookup0, str, vbmiLookup1);
 0888                Vector512<sbyte> errorVec = (origIndex.AsInt32() | str.AsInt32()).AsSByte();
 0889                if (errorVec.ExtractMostSignificantBits() != 0)
 890                {
 891                    break;
 892                }
 893
 894                // Step 2: Now we need to reshuffle bits to remove the 0 bits.
 895                // multiAdd1: [...|0000cccc|ccdddddd|0000aaaa|aabbbbbb]
 0896                Vector512<short> multiAdd1 = Avx512BW.MultiplyAddAdjacent(origIndex.AsByte(), mergeConstant0);
 897                // multiAdd1: [...|00000000|aaaaaabb|bbbbcccc|ccdddddd]
 0898                Vector512<int> multiAdd2 = Avx512BW.MultiplyAddAdjacent(multiAdd1, mergeConstant1);
 899
 900                // Step 3: Pack 48 bytes
 0901                str = Avx512Vbmi.PermuteVar64x8(multiAdd2.AsByte(), vbmiPackedLanesControl).AsSByte();
 902
 0903                AssertWrite<Vector512<sbyte>>(dest, destStart, destLength);
 0904                str.Store((sbyte*)dest);
 0905                src += 64;
 0906                dest += 48;
 907            }
 0908            while (src <= srcEnd);
 909
 0910            srcBytes = src;
 0911            destBytes = dest;
 0912        }
 913
 914        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 915        [CompExactlyDependsOn(typeof(Avx2))]
 916        [SuppressReadyToRunCanonicalRooting]
 917        private static unsafe void Avx2Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* dest
 918            where TBase64Decoder : IBase64Decoder<T>
 919            where T : unmanaged
 920        {
 921            // If we have AVX2 support, pick off 32 bytes at a time for as long as we can,
 922            // but make sure that we quit before seeing any == markers at the end of the
 923            // string. Also, because we write 8 zeroes at the end of the output, ensure
 924            // that there are at least 11 valid bytes of input data remaining to close the
 925            // gap. 32 + 2 + 11 = 45 bytes.
 926
 927            // See SSSE3-version below for an explanation of how the code works.
 928
 929            // The JIT won't hoist these "constants", so help it
 92076930            Vector256<sbyte> lutHi = Vector256.Create(decoder.Avx2LutHigh);
 931
 92076932            Vector256<sbyte> lutLo = Vector256.Create(decoder.Avx2LutLow);
 933
 92076934            Vector256<sbyte> lutShift = Vector256.Create(decoder.Avx2LutShift);
 935
 92076936            Vector256<sbyte> packBytesInLaneMask = Vector256.Create(
 92076937                2, 1, 0, 6,
 92076938                5, 4, 10, 9,
 92076939                8, 14, 13, 12,
 92076940                -1, -1, -1, -1,
 92076941                2, 1, 0, 6,
 92076942                5, 4, 10, 9,
 92076943                8, 14, 13, 12,
 92076944                -1, -1, -1, -1);
 945
 92076946            Vector256<int> packLanesControl = Vector256.Create(
 92076947                 0, 0, 0, 0,
 92076948                1, 0, 0, 0,
 92076949                2, 0, 0, 0,
 92076950                4, 0, 0, 0,
 92076951                5, 0, 0, 0,
 92076952                6, 0, 0, 0,
 92076953                -1, -1, -1, -1,
 92076954                -1, -1, -1, -1).AsInt32();
 955
 92076956            Vector256<sbyte> maskSlashOrUnderscore = Vector256.Create((sbyte)decoder.MaskSlashOrUnderscore);
 92076957            Vector256<sbyte> shiftForUnderscore = Vector256.Create((sbyte)33);
 92076958            Vector256<sbyte> mergeConstant0 = Vector256.Create(0x01400140).AsSByte();
 92076959            Vector256<short> mergeConstant1 = Vector256.Create(0x00011000).AsInt16();
 960
 92076961            T* src = srcBytes;
 92076962            byte* dest = destBytes;
 963
 964            //while (remaining >= 45)
 965            do
 966            {
 974316967                if (!decoder.TryLoadAvxVector256(src, srcStart, sourceLength, out Vector256<sbyte> str))
 968                {
 969                    break;
 970                }
 971
 972806972                Vector256<sbyte> hiNibbles = ((str.AsInt32()) >>> 4).AsSByte() & maskSlashOrUnderscore;
 973
 972806974                if (!decoder.TryDecode256Core(str, hiNibbles, maskSlashOrUnderscore, lutLo, lutHi, lutShift, shiftForUnd
 975                {
 976                    break;
 977                }
 978
 979                // in, lower lane, bits, upper case are most significant bits, lower case are least significant bits:
 980                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 981                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 982                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 983                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 984
 915050985                Vector256<short> merge_ab_and_bc = Avx2.MultiplyAddAdjacent(str.AsByte(), mergeConstant0);
 986                // 0000kkkk LLllllll 0000JJJJ JJjjKKKK
 987                // 0000hhhh IIiiiiii 0000GGGG GGggHHHH
 988                // 0000eeee FFffffff 0000DDDD DDddEEEE
 989                // 0000bbbb CCcccccc 0000AAAA AAaaBBBB
 990
 915050991                Vector256<int> output = Avx2.MultiplyAddAdjacent(merge_ab_and_bc, mergeConstant1);
 992                // 00000000 JJJJJJjj KKKKkkkk LLllllll
 993                // 00000000 GGGGGGgg HHHHhhhh IIiiiiii
 994                // 00000000 DDDDDDdd EEEEeeee FFffffff
 995                // 00000000 AAAAAAaa BBBBbbbb CCcccccc
 996
 997                // Pack bytes together in each lane:
 915050998                output = Avx2.Shuffle(output.AsSByte(), packBytesInLaneMask).AsInt32();
 999                // 00000000 00000000 00000000 00000000
 1000                // LLllllll KKKKkkkk JJJJJJjj IIiiiiii
 1001                // HHHHhhhh GGGGGGgg FFffffff EEEEeeee
 1002                // DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa
 1003
 1004                // Pack lanes
 9150501005                str = Avx2.PermuteVar8x32(output, packLanesControl).AsSByte();
 1006
 9150501007                AssertWrite<Vector256<sbyte>>(dest, destStart, destLength);
 9150501008                Avx.Store(dest, str.AsByte());
 1009
 9150501010                src += 32;
 9150501011                dest += 24;
 1012            }
 9150501013            while (src <= srcEnd);
 1014
 920761015            srcBytes = src;
 920761016            destBytes = dest;
 920761017        }
 1018
 1019        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1020        [CompExactlyDependsOn(typeof(Ssse3))]
 1021        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1022        [CompExactlyDependsOn(typeof(PackedSimd))]
 1023        internal static Vector128<byte> SimdShuffle(Vector128<byte> left, Vector128<byte> right, Vector128<byte> mask8F)
 1024        {
 1025            Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && BitConverter.IsLi
 1026
 3402281027            if (Ssse3.IsSupported)
 1028            {
 3402281029                return Ssse3.Shuffle(left, right);
 1030            }
 1031            else if (PackedSimd.IsSupported)
 1032            {
 1033                return PackedSimd.Swizzle(left, right & mask8F);
 1034            }
 1035            else
 1036            {
 01037                return AdvSimd.Arm64.VectorTableLookup(left, right & mask8F);
 1038            }
 1039        }
 1040
 1041        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1042        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1043        [SuppressReadyToRunCanonicalRooting]
 1044        private static unsafe void AdvSimdDecode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte* d
 1045            where TBase64Decoder : IBase64Decoder<T>
 1046            where T : unmanaged
 1047        {
 1048            // C# implementation of https://github.com/aklomp/base64/blob/3a5add8652076612a8407627a42c768736a4263f/lib/a
 1049            // If we have AdvSimd support, pick off 64 bytes at a time for as long as we can,
 1050            // but make sure that we quit before seeing any == markers at the end of the
 1051            // string. 64 + 2 = 66 bytes.
 1052
 1053            // In the decoding process, we want to map each byte, representing a Base64 value, to its 6-bit (0-63) repre
 1054            // It uses the following mapping. Values outside the following groups are invalid and, we abort decoding whe
 1055            //
 1056            // #    From       To         Char
 1057            // 1    [43]       [62]       +
 1058            // 2    [47]       [63]       /
 1059            // 3    [48..57]   [52..61]   0..9
 1060            // 4    [65..90]   [0..25]    A..Z
 1061            // 5    [97..122]  [26..51]   a..z
 1062            //
 1063            // To map an input value to its Base64 representation, we use look-up tables 'decLutOne' and 'decLutTwo'.
 1064            // 'decLutOne' helps to map groups 1, 2 and 3 while 'decLutTwo' maps groups 4 and 5 in the above list.
 1065            // After mapping, each value falls between 0-63. Consequently, the last six bits of each byte now hold a val
 1066            // We then compress four such bytes (with valid 4 * 6 = 24 bits) to three UTF8 bytes (3 * 8 = 24 bits).
 1067            // For faster decoding, we use SIMD operations that allow the processing of multiple bytes together.
 1068            // However, the compress operation on adjacent values of a vector could be slower. Thus, we de-interleave wh
 1069            // the input bytes that store adjacent bytes in separate vectors. This later simplifies the compress step wi
 1070            // of logical operations. This requires interleaving while storing the decoded result.
 1071
 1072            // Values in 'decLutOne' maps input values from 0 to 63.
 1073            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
 1074            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
 1075            //   255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63
 1076            //    52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255, 255, 255, 255, 255
 1077            var decLutOne = (Vector128<byte>.AllBitsSet,
 1078                             Vector128<byte>.AllBitsSet,
 1079                             Vector128.Create(decoder.AdvSimdLutOne3).AsByte(),
 1080                             Vector128.Create(0x37363534, 0x3B3A3938, 0xFFFF3D3C, 0xFFFFFFFF).AsByte());
 1081
 1082            // Values in 'decLutTwo' maps input values from 63 to 127.
 1083            //    0, 255,   0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13
 1084            //   14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255
 1085            //  255, 255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39
 1086            //   40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51, 255, 255, 255, 255
 1087            var decLutTwo = (Vector128.Create(0x0100FF00, 0x05040302, 0x09080706, 0x0D0C0B0A).AsByte(),
 1088                             Vector128.Create(0x11100F0E, 0x15141312, 0x19181716, 0xFFFFFFFF).AsByte(),
 1089                             Vector128.Create(decoder.AdvSimdLutTwo3Uint1, 0x1F1E1D1C, 0x23222120, 0x27262524).AsByte(),
 1090                             Vector128.Create(0x2B2A2928, 0x2F2E2D2C, 0x33323130, 0xFFFFFFFF).AsByte());
 1091
 1092            T* src = srcBytes;
 1093            byte* dest = destBytes;
 1094            Vector128<byte> offset = Vector128.Create<byte>(63);
 1095
 1096            do
 1097            {
 1098                // Step 1: Load 64 bytes and de-interleave.
 1099                if (!decoder.TryLoadArmVector128x4(src, srcStart, sourceLength,
 1100                    out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> st
 1101                {
 1102                    break;
 1103                }
 1104
 1105                // Step 2: Map each valid input to its Base64 value.
 1106                // We use two look-ups to compute partial results and combine them later.
 1107
 1108                // Step 2.1: Detect valid Base64 values from the first three groups. Maps input as,
 1109                //  0 to  63 (Invalid) => 255
 1110                //  0 to  63 (Valid)   => Their Base64 equivalent
 1111                // 64 to 255           => 0
 1112
 1113                // Each input value acts as an index in the look-up table 'decLutOne'.
 1114                // e.g., for group 1: index 43 maps to 62 (Base64 '+').
 1115                // Group 4 and 5 values are out-of-range (>64), so they are mapped to zero.
 1116                // Other valid indices but invalid values are mapped to 255.
 1117                Vector128<byte> decOne1 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str1);
 1118                Vector128<byte> decOne2 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str2);
 1119                Vector128<byte> decOne3 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str3);
 1120                Vector128<byte> decOne4 = AdvSimd.Arm64.VectorTableLookup(decLutOne, str4);
 1121
 1122                // Step 2.2: Detect valid Base64 values from groups 4 and 5. Maps input as,
 1123                //   0 to  63           => 0
 1124                //  64 to 122 (Valid)   => Their Base64 equivalent
 1125                //  64 to 122 (Invalid) => 255
 1126                // 123 to 255           => Remains unchanged
 1127
 1128                // Subtract/offset each input value by 63 so that it can be used as a valid offset.
 1129                // Subtract saturate makes values from the first three groups set to zero that are
 1130                // then mapped to zero in the subsequent look-up.
 1131                Vector128<byte> decTwo1 = AdvSimd.SubtractSaturate(str1, offset);
 1132                Vector128<byte> decTwo2 = AdvSimd.SubtractSaturate(str2, offset);
 1133                Vector128<byte> decTwo3 = AdvSimd.SubtractSaturate(str3, offset);
 1134                Vector128<byte> decTwo4 = AdvSimd.SubtractSaturate(str4, offset);
 1135
 1136                // We use VTBX to map values where out-of-range indices are unchanged.
 1137                decTwo1 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo1, decLutTwo, decTwo1);
 1138                decTwo2 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo2, decLutTwo, decTwo2);
 1139                decTwo3 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo3, decLutTwo, decTwo3);
 1140                decTwo4 = AdvSimd.Arm64.VectorTableLookupExtension(decTwo4, decLutTwo, decTwo4);
 1141
 1142                // Step 3: Combine the partial result.
 1143                // Each look-up above maps valid values to their Base64 equivalent or zero.
 1144                // Thus the intermediate results 'decOne' and 'decTwo' could be OR-ed to get final values.
 1145                str1 = (decOne1 | decTwo1);
 1146                str2 = (decOne2 | decTwo2);
 1147                str3 = (decOne3 | decTwo3);
 1148                str4 = (decOne4 | decTwo4);
 1149
 1150                // Step 4: Detect an invalid input value.
 1151                // Invalid values < 122 are set to 255 while the ones above 122 are unchanged.
 1152                // Check for invalid input, any value larger than 63.
 1153                Vector128<byte> classified = (Vector128.GreaterThan(str1, offset)
 1154                                            | Vector128.GreaterThan(str2, offset)
 1155                                            | Vector128.GreaterThan(str3, offset)
 1156                                            | Vector128.GreaterThan(str4, offset));
 1157
 1158                // Check that all bits are zero.
 1159                if (classified != Vector128<byte>.Zero)
 1160                {
 1161                    break;
 1162                }
 1163
 1164                // Step 5: Compress four bytes into three.
 1165                Vector128<byte> res1 = ((str1 << 2) | (str2 >> 4));
 1166                Vector128<byte> res2 = ((str2 << 4) | (str3 >> 2));
 1167                Vector128<byte> res3 = ((str3 << 6) | str4);
 1168
 1169                // Step 6: Interleave and store decoded results.
 1170                AssertWrite<Vector128<byte>>(dest, destStart, destLength);
 1171                AdvSimd.Arm64.StoreVectorAndZip(dest, (res1, res2, res3));
 1172
 1173                src += 64;
 1174                dest += 48;
 1175            }
 1176            while (src <= srcEnd);
 1177
 1178            srcBytes = src;
 1179            destBytes = dest;
 1180        }
 1181
 1182        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1183        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1184        [CompExactlyDependsOn(typeof(Ssse3))]
 1185        [CompExactlyDependsOn(typeof(PackedSimd))]
 1186        [SuppressReadyToRunCanonicalRooting]
 1187        private static unsafe void Vector128Decode<TBase64Decoder, T>(TBase64Decoder decoder, ref T* srcBytes, ref byte*
 1188            where TBase64Decoder : IBase64Decoder<T>
 1189            where T : unmanaged
 1190        {
 1191            Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && BitConverter.IsLi
 1192
 1193            // If we have Vector128 support, pick off 16 bytes at a time for as long as we can,
 1194            // but make sure that we quit before seeing any == markers at the end of the
 1195            // string. Also, because we write four zeroes at the end of the output, ensure
 1196            // that there are at least 6 valid bytes of input data remaining to close the
 1197            // gap. 16 + 2 + 6 = 24 bytes.
 1198
 1199            // The input consists of six character sets in the Base64 alphabet,
 1200            // which we need to map back to the 6-bit values they represent.
 1201            // There are three ranges, two singles, and then there's the rest.
 1202            //
 1203            //  #  From       To        Add  Characters
 1204            //  1  [43]       [62]      +19  +
 1205            //  2  [47]       [63]      +16  /
 1206            //  3  [48..57]   [52..61]   +4  0..9
 1207            //  4  [65..90]   [0..25]   -65  A..Z
 1208            //  5  [97..122]  [26..51]  -71  a..z
 1209            // (6) Everything else => invalid input
 1210
 1211            // We will use LUTS for character validation & offset computation
 1212            // Remember that 0x2X and 0x0X are the same index for _mm_shuffle_epi8,
 1213            // this allows to mask with 0x2F instead of 0x0F and thus save one constant declaration (register and/or mem
 1214
 1215            // For offsets:
 1216            // Perfect hash for lut = ((src>>4)&0x2F)+((src==0x2F)?0xFF:0x00)
 1217            // 0000 = garbage
 1218            // 0001 = /
 1219            // 0010 = +
 1220            // 0011 = 0-9
 1221            // 0100 = A-Z
 1222            // 0101 = A-Z
 1223            // 0110 = a-z
 1224            // 0111 = a-z
 1225            // 1000 >= garbage
 1226
 1227            // For validation, here's the table.
 1228            // A character is valid if and only if the AND of the 2 lookups equals 0:
 1229
 1230            // hi \ lo              0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
 1231            //      LUT             0x15 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x13 0x1A 0x1B 0x1B 0x1B 0x1A
 1232
 1233            // 0000 0X10 char        NUL  SOH  STX  ETX  EOT  ENQ  ACK  BEL   BS   HT   LF   VT   FF   CR   SO   SI
 1234            //           andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1235
 1236            // 0001 0x10 char        DLE  DC1  DC2  DC3  DC4  NAK  SYN  ETB  CAN   EM  SUB  ESC   FS   GS   RS   US
 1237            //           andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1238
 1239            // 0010 0x01 char               !    "    #    $    %    &    '    (    )    *    +    ,    -    .    /
 1240            //           andlut     0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00 0x01 0x01 0x01 0x00
 1241
 1242            // 0011 0x02 char          0    1    2    3    4    5    6    7    8    9    :    ;    <    =    >    ?
 1243            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x02 0x02 0x02 0x02 0x02 0x02
 1244
 1245            // 0100 0x04 char          @    A    B    C    D    E    F    G    H    I    J    K    L    M    N    0
 1246            //           andlut     0x04 0x00 0x00 0x00 0X00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
 1247
 1248            // 0101 0x08 char          P    Q    R    S    T    U    V    W    X    Y    Z    [    \    ]    ^    _
 1249            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08
 1250
 1251            // 0110 0x04 char          `    a    b    c    d    e    f    g    h    i    j    k    l    m    n    o
 1252            //           andlut     0x04 0x00 0x00 0x00 0X00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
 1253            // 0111 0X08 char          p    q    r    s    t    u    v    w    x    y    z    {    |    }    ~
 1254            //           andlut     0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x08 0x08 0x08 0x08 0x08
 1255
 1256            // 1000 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1257            // 1001 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1258            // 1010 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1259            // 1011 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1260            // 1100 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1261            // 1101 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1262            // 1110 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1263            // 1111 0x10 andlut     0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10 0x10
 1264
 1265            // The JIT won't hoist these "constants", so help it
 944401266            Vector128<byte> lutHi = Vector128.Create(decoder.Vector128LutHigh).AsByte();
 944401267            Vector128<byte> lutLo = Vector128.Create(decoder.Vector128LutLow).AsByte();
 944401268            Vector128<sbyte> lutShift = Vector128.Create(decoder.Vector128LutShift).AsSByte();
 944401269            Vector128<sbyte> packBytesMask = Vector128.Create(0x06000102, 0x090A0405, 0x0C0D0E08, 0xffffffff).AsSByte();
 944401270            Vector128<byte> mergeConstant0 = Vector128.Create(0x01400140).AsByte();
 944401271            Vector128<short> mergeConstant1 = Vector128.Create(0x00011000).AsInt16();
 944401272            Vector128<byte> one = Vector128<byte>.One;
 944401273            Vector128<byte> mask2F = Vector128.Create(decoder.MaskSlashOrUnderscore);
 944401274            Vector128<byte> mask8F = Vector128.Create((byte)0x8F);
 944401275            Vector128<byte> shiftForUnderscore = Vector128.Create((byte)33);
 944401276            T* src = srcBytes;
 944401277            byte* dest = destBytes;
 1278
 1279            //while (remaining >= 24)
 1280            do
 1281            {
 1044281282                if (!decoder.TryLoadVector128(src, srcStart, sourceLength, out Vector128<byte> str))
 1283                {
 1284                    break;
 1285                }
 1286
 1287                // lookup
 1024101288                Vector128<byte> hiNibbles = Vector128.ShiftRightLogical(str.AsInt32(), 4).AsByte() & mask2F;
 1289
 1024101290                if (!decoder.TryDecode128Core(str, hiNibbles, mask2F, mask8F, lutLo, lutHi, lutShift, shiftForUnderscore
 1291                {
 1292                    break;
 1293                }
 1294
 1295                // in, bits, upper case are most significant bits, lower case are least significant bits
 1296                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 1297                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 1298                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 1299                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 1300
 1301                Vector128<short> merge_ab_and_bc;
 422521302                if (Ssse3.IsSupported)
 1303                {
 422521304                    merge_ab_and_bc = Ssse3.MultiplyAddAdjacent(str.AsByte(), mergeConstant0.AsSByte());
 1305                }
 1306                else if (AdvSimd.Arm64.IsSupported)
 1307                {
 1308                    Vector128<ushort> evens = AdvSimd.ShiftLeftLogicalWideningLower(AdvSimd.Arm64.UnzipEven(str, one).Ge
 1309                    Vector128<ushort> odds = AdvSimd.Arm64.TransposeOdd(str, Vector128<byte>.Zero).AsUInt16();
 1310                    merge_ab_and_bc = Vector128.Add(evens, odds).AsInt16();
 1311                }
 1312                else if (PackedSimd.IsSupported)
 1313                {
 1314                    // MultiplyAddAdjacent by {64,1,...} is the even byte (low of each u16 lane) times 64 plus the odd b
 1315                    Vector128<ushort> u = str.AsUInt16();
 1316                    Vector128<ushort> evens = Vector128.ShiftLeft(u & Vector128.Create((ushort)0x00FF), 6);
 1317                    Vector128<ushort> odds = Vector128.ShiftRightLogical(u, 8);
 1318                    merge_ab_and_bc = (evens + odds).AsInt16();
 1319                }
 1320                else
 1321                {
 1322                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 01323                    ThrowUnreachableException();
 1324                    merge_ab_and_bc = default;
 1325                }
 1326                // 0000kkkk LLllllll 0000JJJJ JJjjKKKK
 1327                // 0000hhhh IIiiiiii 0000GGGG GGggHHHH
 1328                // 0000eeee FFffffff 0000DDDD DDddEEEE
 1329                // 0000bbbb CCcccccc 0000AAAA AAaaBBBB
 1330
 1331                Vector128<int> output;
 422521332                if (Ssse3.IsSupported)
 1333                {
 422521334                    output = Sse2.MultiplyAddAdjacent(merge_ab_and_bc, mergeConstant1);
 1335                }
 1336                else if (AdvSimd.Arm64.IsSupported)
 1337                {
 1338                    Vector128<int> ievens = AdvSimd.ShiftLeftLogicalWideningLower(AdvSimd.Arm64.UnzipEven(merge_ab_and_b
 1339                    Vector128<int> iodds = AdvSimd.Arm64.TransposeOdd(merge_ab_and_bc, Vector128<short>.Zero).AsInt32();
 1340                    output = Vector128.Add(ievens, iodds).AsInt32();
 1341                }
 1342                else if (PackedSimd.IsSupported)
 1343                {
 1344                    // MultiplyAddAdjacent by {4096,1,...} is the even i16 (low of each i32 lane) times 4096 plus the od
 1345                    Vector128<uint> m = merge_ab_and_bc.AsUInt32();
 1346                    Vector128<uint> ievens = Vector128.ShiftLeft(m & Vector128.Create(0x0000FFFFu), 12);
 1347                    Vector128<uint> iodds = Vector128.ShiftRightLogical(m, 16);
 1348                    output = (ievens + iodds).AsInt32();
 1349                }
 1350                else
 1351                {
 1352                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 01353                    ThrowUnreachableException();
 1354                    output = default;
 1355                }
 1356                // 00000000 JJJJJJjj KKKKkkkk LLllllll
 1357                // 00000000 GGGGGGgg HHHHhhhh IIiiiiii
 1358                // 00000000 DDDDDDdd EEEEeeee FFffffff
 1359                // 00000000 AAAAAAaa BBBBbbbb CCcccccc
 1360
 1361                // Pack bytes together:
 422521362                str = SimdShuffle(output.AsByte(), packBytesMask.AsByte(), mask8F);
 1363                // 00000000 00000000 00000000 00000000
 1364                // LLllllll KKKKkkkk JJJJJJjj IIiiiiii
 1365                // HHHHhhhh GGGGGGgg FFffffff EEEEeeee
 1366                // DDDDDDdd CCcccccc BBBBbbbb AAAAAAaa
 1367
 422521368                AssertWrite<Vector128<sbyte>>(dest, destStart, destLength);
 422521369                str.Store(dest);
 1370
 422521371                src += 16;
 422521372                dest += 12;
 1373            }
 422521374            while (src <= srcEnd);
 1375
 944401376            srcBytes = src;
 944401377            destBytes = dest;
 944401378        }
 1379#endif
 1380
 1381        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1382        private static unsafe void WriteThreeLowOrderBytes(byte* destination, int value)
 1383        {
 3612081384            destination[0] = (byte)(value >> 16);
 3612081385            destination[1] = (byte)(value >> 8);
 3612081386            destination[2] = (byte)value;
 3612081387        }
 1388
 1389        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1390        internal static bool IsWhiteSpace(int value)
 1391        {
 6728121392            Debug.Assert(value >= 0 && value <= ushort.MaxValue);
 1393            uint charMinusLowUInt32;
 6728121394            return (int)((0xC8000100U << (short)(charMinusLowUInt32 = (ushort)(value - '\t'))) & (charMinusLowUInt32 - 3
 1395        }
 1396
 1397        internal readonly struct Base64DecoderByte : IBase64Decoder<byte>
 1398        {
 1399            // Pre-computing this table using a custom string(s_characters) and GenerateDecodingMapAndVerify (found in t
 1400            public ReadOnlySpan<sbyte> DecodingMap =>
 1943081401                [
 1943081402                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1943081403                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1943081404                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,         //62 is placed at index 43 (
 1943081405                    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,         //52-61 are placed at index 
 1943081406                    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
 1943081407                    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,         //0-25 are placed at index 6
 1943081408                    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
 1943081409                    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,         //26-51 are placed at index 
 1943081410                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,         // Bytes over 122 ('z') are 
 1943081411                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,         // Hence, padding the map wi
 1943081412                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1943081413                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1943081414                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1943081415                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1943081416                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1943081417                    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1943081418                ];
 1419
 1420            public ReadOnlySpan<uint> VbmiLookup0 =>
 01421                [
 01422                    0x80808080, 0x80808080, 0x80808080, 0x80808080,
 01423                    0x80808080, 0x80808080, 0x80808080, 0x80808080,
 01424                    0x80808080, 0x80808080, 0x3e808080, 0x3f808080,
 01425                    0x37363534, 0x3b3a3938, 0x80803d3c, 0x80808080
 01426                ];
 1427
 1428            public ReadOnlySpan<uint> VbmiLookup1 =>
 01429                [
 01430                    0x02010080, 0x06050403, 0x0a090807, 0x0e0d0c0b,
 01431                    0x1211100f, 0x16151413, 0x80191817, 0x80808080,
 01432                    0x1c1b1a80, 0x201f1e1d, 0x24232221, 0x28272625,
 01433                    0x2c2b2a29, 0x302f2e2d, 0x80333231, 0x80808080
 01434                ];
 1435
 1436            public ReadOnlySpan<sbyte> Avx2LutHigh =>
 920761437                [
 920761438                    0x10, 0x10, 0x01, 0x02,
 920761439                    0x04, 0x08, 0x04, 0x08,
 920761440                    0x10, 0x10, 0x10, 0x10,
 920761441                    0x10, 0x10, 0x10, 0x10,
 920761442                    0x10, 0x10, 0x01, 0x02,
 920761443                    0x04, 0x08, 0x04, 0x08,
 920761444                    0x10, 0x10, 0x10, 0x10,
 920761445                    0x10, 0x10, 0x10, 0x10
 920761446                ];
 1447
 1448            public ReadOnlySpan<sbyte> Avx2LutLow =>
 920761449                [
 920761450                    0x15, 0x11, 0x11, 0x11,
 920761451                    0x11, 0x11, 0x11, 0x11,
 920761452                    0x11, 0x11, 0x13, 0x1A,
 920761453                    0x1B, 0x1B, 0x1B, 0x1A,
 920761454                    0x15, 0x11, 0x11, 0x11,
 920761455                    0x11, 0x11, 0x11, 0x11,
 920761456                    0x11, 0x11, 0x13, 0x1A,
 920761457                    0x1B, 0x1B, 0x1B, 0x1A
 920761458                ];
 1459
 1460            public ReadOnlySpan<sbyte> Avx2LutShift =>
 920761461                [
 920761462                    0, 16, 19, 4,
 920761463                    -65, -65, -71, -71,
 920761464                    0, 0, 0, 0,
 920761465                    0, 0, 0, 0,
 920761466                    0, 16, 19, 4,
 920761467                    -65, -65, -71, -71,
 920761468                    0, 0, 0, 0,
 920761469                    0, 0, 0, 0
 920761470                ];
 1471
 1865161472            public byte MaskSlashOrUnderscore => (byte)'/';
 1473
 944401474            public ReadOnlySpan<int> Vector128LutHigh => [0x02011010, 0x08040804, 0x10101010, 0x10101010];
 1475
 944401476            public ReadOnlySpan<int> Vector128LutLow => [0x11111115, 0x11111111, 0x1A131111, 0x1A1B1B1B];
 1477
 944401478            public ReadOnlySpan<uint> Vector128LutShift => [0x04131000, 0xb9b9bfbf, 0x00000000, 0x00000000];
 1479
 01480            public ReadOnlySpan<uint> AdvSimdLutOne3 => [0xFFFFFFFF, 0xFFFFFFFF, 0x3EFFFFFF, 0x3FFFFFFF];
 1481
 01482            public uint AdvSimdLutTwo3Uint1 => 0x1B1AFFFF;
 1483
 501941484            public int GetMaxDecodedLength(int utf8Length) => Base64.GetMaxDecodedFromUtf8Length(utf8Length);
 1485
 393201486            public bool IsInvalidLength(int bufferLength) => bufferLength % 4 != 0; // only decode input if it is a mult
 1487
 1078841488            public bool IsValidPadding(uint padChar) => padChar == EncodingPad;
 1489
 501941490            public int SrcLength(bool _, int utf8Length) => utf8Length & ~0x3;  // only decode input up to the closest m
 1491
 1492#if NET
 1493            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1494            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1495            [CompExactlyDependsOn(typeof(Ssse3))]
 1496            [CompExactlyDependsOn(typeof(PackedSimd))]
 1497            public bool TryDecode128Core(
 1498                Vector128<byte> str,
 1499                Vector128<byte> hiNibbles,
 1500                Vector128<byte> maskSlashOrUnderscore,
 1501                Vector128<byte> mask8F,
 1502                Vector128<byte> lutLow,
 1503                Vector128<byte> lutHigh,
 1504                Vector128<sbyte> lutShift,
 1505                Vector128<byte> _,
 1506                out Vector128<byte> result)
 1507            {
 1024101508                Vector128<byte> loNibbles = str & maskSlashOrUnderscore;
 1024101509                Vector128<byte> hi = SimdShuffle(lutHigh, hiNibbles, mask8F);
 1024101510                Vector128<byte> lo = SimdShuffle(lutLow, loNibbles, mask8F);
 1511
 1512                // Check for invalid input: if any "and" values from lo and hi are not zero,
 1513                // fall back on bytewise code to do error checking and reporting:
 1024101514                if ((lo & hi) != Vector128<byte>.Zero)
 1515                {
 601581516                    result = default;
 601581517                    return false;
 1518                }
 1519
 422521520                Vector128<byte> eq2F = Vector128.Equals(str, maskSlashOrUnderscore);
 422521521                Vector128<byte> shift = SimdShuffle(lutShift.AsByte(), (eq2F + hiNibbles), mask8F);
 1522
 1523                // Now simply add the delta values to the input:
 422521524                result = str + shift;
 1525
 422521526                return true;
 1527            }
 1528
 1529            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1530            [CompExactlyDependsOn(typeof(Avx2))]
 1531            public bool TryDecode256Core(
 1532                Vector256<sbyte> str,
 1533                Vector256<sbyte> hiNibbles,
 1534                Vector256<sbyte> maskSlashOrUnderscore,
 1535                Vector256<sbyte> lutLow,
 1536                Vector256<sbyte> lutHigh,
 1537                Vector256<sbyte> lutShift,
 1538                Vector256<sbyte> _,
 1539                out Vector256<sbyte> result)
 1540            {
 9728061541                Vector256<sbyte> loNibbles = str & maskSlashOrUnderscore;
 9728061542                Vector256<sbyte> hi = Avx2.Shuffle(lutHigh, hiNibbles);
 9728061543                Vector256<sbyte> lo = Avx2.Shuffle(lutLow, loNibbles);
 1544
 9728061545                if ((lo & hi) != Vector256<sbyte>.Zero)
 1546                {
 577561547                    result = default;
 577561548                    return false;
 1549                }
 1550
 9150501551                Vector256<sbyte> eq2F = Avx2.CompareEqual(str, maskSlashOrUnderscore);
 9150501552                Vector256<sbyte> shift = Avx2.Shuffle(lutShift, eq2F + hiNibbles);
 1553
 9150501554                result = str + shift;
 1555
 9150501556                return true;
 1557            }
 1558
 1559            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1560            public unsafe bool TryLoadVector512(byte* src, byte* srcStart, int sourceLength, out Vector512<sbyte> str)
 1561            {
 01562                AssertRead<Vector512<sbyte>>(src, srcStart, sourceLength);
 01563                str = Vector512.Load(src).AsSByte();
 01564                return true;
 1565            }
 1566
 1567            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1568            [CompExactlyDependsOn(typeof(Avx2))]
 1569            public unsafe bool TryLoadAvxVector256(byte* src, byte* srcStart, int sourceLength, out Vector256<sbyte> str
 1570            {
 3816461571                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 3816461572                str = Avx.LoadVector256(src).AsSByte();
 3816461573                return true;
 1574            }
 1575
 1576            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1577            public unsafe bool TryLoadVector128(byte* src, byte* srcStart, int sourceLength, out Vector128<byte> str)
 1578            {
 261301579                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 261301580                str = Vector128.LoadUnsafe(ref *src);
 261301581                return true;
 1582            }
 1583
 1584            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1585            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1586            public unsafe bool TryLoadArmVector128x4(byte* src, byte* srcStart, int sourceLength,
 1587                out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> str4)
 1588            {
 01589                AssertRead<Vector128<byte>>(src, srcStart, sourceLength);
 01590                (str1, str2, str3, str4) = AdvSimd.Arm64.Load4xVector128AndUnzip(src);
 1591
 01592                return true;
 1593            }
 1594#endif // NET
 1595
 1596            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1597            public unsafe int DecodeFourElements(byte* source, ref sbyte decodingMap)
 1598            {
 1599                // The 'source' span expected to have at least 4 elements, and the 'decodingMap' consists 256 sbytes
 874821600                uint t0 = source[0];
 874821601                uint t1 = source[1];
 874821602                uint t2 = source[2];
 874821603                uint t3 = source[3];
 1604
 874821605                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 874821606                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 874821607                int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 874821608                int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 1609
 874821610                i0 <<= 18;
 874821611                i1 <<= 12;
 874821612                i2 <<= 6;
 1613
 874821614                i0 |= i3;
 874821615                i1 |= i2;
 1616
 874821617                i0 |= i1;
 874821618                return i0;
 1619            }
 1620
 1621            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1622            public unsafe int DecodeRemaining(byte* srcEnd, ref sbyte decodingMap, long remaining, out uint t2, out uint
 1623            {
 1624                uint t0;
 1625                uint t1;
 154201626                t2 = EncodingPad;
 154201627                t3 = EncodingPad;
 1628                switch (remaining)
 1629                {
 1630                    case 2:
 01631                        t0 = srcEnd[-2];
 01632                        t1 = srcEnd[-1];
 01633                        break;
 1634                    case 3:
 01635                        t0 = srcEnd[-3];
 01636                        t1 = srcEnd[-2];
 01637                        t2 = srcEnd[-1];
 01638                        break;
 1639                    case 4:
 154201640                        t0 = srcEnd[-4];
 154201641                        t1 = srcEnd[-3];
 154201642                        t2 = srcEnd[-2];
 154201643                        t3 = srcEnd[-1];
 154201644                        break;
 1645                    default:
 01646                        return -1;
 1647                }
 1648
 154201649                int i0 = Unsafe.Add(ref decodingMap, (IntPtr)t0);
 154201650                int i1 = Unsafe.Add(ref decodingMap, (IntPtr)t1);
 1651
 154201652                i0 <<= 18;
 154201653                i1 <<= 12;
 1654
 154201655                i0 |= i1;
 154201656                return i0;
 1657            }
 1658
 1659            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1660            public int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<byte> span)
 1661            {
 697561662                for (int i = 0; i < span.Length; i++)
 1663                {
 348401664                    if (!IsWhiteSpace(span[i]))
 1665                    {
 96521666                        return i;
 1667                    }
 1668                }
 1669
 381670                return -1;
 1671            }
 1672
 1673            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1674            [SuppressReadyToRunCanonicalRooting]
 1675            public OperationStatus DecodeWithWhiteSpaceBlockwiseWrapper<TBase64Decoder>(TBase64Decoder decoder, ReadOnly
 1676                Span<byte> bytes, ref int bytesConsumed, ref int bytesWritten, bool isFinalBlock = true)
 1677                where TBase64Decoder : IBase64Decoder<byte> =>
 34341678                DecodeWithWhiteSpaceBlockwise(decoder, utf8, bytes, ref bytesConsumed, ref bytesWritten, isFinalBlock);
 1679        }
 1680
 1681        internal readonly struct Base64DecoderChar : IBase64Decoder<ushort>
 1682        {
 1068141683            public ReadOnlySpan<sbyte> DecodingMap => default(Base64DecoderByte).DecodingMap;
 1684
 01685            public ReadOnlySpan<uint> VbmiLookup0 => default(Base64DecoderByte).VbmiLookup0;
 1686
 01687            public ReadOnlySpan<uint> VbmiLookup1 => default(Base64DecoderByte).VbmiLookup1;
 1688
 710821689            public ReadOnlySpan<sbyte> Avx2LutHigh => default(Base64DecoderByte).Avx2LutHigh;
 1690
 710821691            public ReadOnlySpan<sbyte> Avx2LutLow => default(Base64DecoderByte).Avx2LutLow;
 1692
 710821693            public ReadOnlySpan<sbyte> Avx2LutShift => default(Base64DecoderByte).Avx2LutShift;
 1694
 1435721695            public byte MaskSlashOrUnderscore => default(Base64DecoderByte).MaskSlashOrUnderscore;
 1696
 724901697            public ReadOnlySpan<int> Vector128LutHigh => default(Base64DecoderByte).Vector128LutHigh;
 1698
 724901699            public ReadOnlySpan<int> Vector128LutLow => default(Base64DecoderByte).Vector128LutLow;
 1700
 724901701            public ReadOnlySpan<uint> Vector128LutShift => default(Base64DecoderByte).Vector128LutShift;
 1702
 01703            public ReadOnlySpan<uint> AdvSimdLutOne3 => default(Base64DecoderByte).AdvSimdLutOne3;
 1704
 01705            public uint AdvSimdLutTwo3Uint1 => default(Base64DecoderByte).AdvSimdLutTwo3Uint1;
 1706
 1068141707            public int GetMaxDecodedLength(int sourceLength) => Base64.GetMaxDecodedFromUtf8Length(sourceLength);
 1708
 01709            public bool IsInvalidLength(int bufferLength) => bufferLength % 4 != 0;
 1710
 855361711            public bool IsValidPadding(uint padChar) => padChar == EncodingPad;
 1712
 1068141713            public int SrcLength(bool _, int sourceLength) => sourceLength & ~0x3;
 1714
 1715#if NET
 1716            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1717            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1718            [CompExactlyDependsOn(typeof(Ssse3))]
 1719            [CompExactlyDependsOn(typeof(PackedSimd))]
 1720            public bool TryDecode128Core(Vector128<byte> str, Vector128<byte> hiNibbles, Vector128<byte> maskSlashOrUnde
 1721                Vector128<byte> lutLow, Vector128<byte> lutHigh, Vector128<sbyte> lutShift, Vector128<byte> shiftForUnde
 762801722                default(Base64DecoderByte).TryDecode128Core(str, hiNibbles, maskSlashOrUnderscore, mask8F, lutLow, lutHi
 1723
 1724            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1725            [CompExactlyDependsOn(typeof(Avx2))]
 1726            public bool TryDecode256Core(Vector256<sbyte> str, Vector256<sbyte> hiNibbles, Vector256<sbyte> maskSlashOrU
 1727                Vector256<sbyte> lutHigh, Vector256<sbyte> lutShift, Vector256<sbyte> shiftForUnderscore, out Vector256<
 5911601728                default(Base64DecoderByte).TryDecode256Core(str, hiNibbles, maskSlashOrUnderscore, lutLow, lutHigh, lutS
 1729
 1730            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1731            public unsafe bool TryLoadVector512(ushort* src, ushort* srcStart, int sourceLength, out Vector512<sbyte> st
 1732            {
 01733                AssertRead<Vector512<ushort>>(src, srcStart, sourceLength);
 01734                Vector512<ushort> utf16VectorLower = Vector512.Load(src);
 01735                Vector512<ushort> utf16VectorUpper = Vector512.Load(src + 32);
 01736                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1737                {
 01738                    str = default;
 01739                    return false;
 1740                }
 1741
 01742                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper).AsSByte();
 01743                return true;
 1744            }
 1745
 1746            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1747            [CompExactlyDependsOn(typeof(Avx2))]
 1748            public unsafe bool TryLoadAvxVector256(ushort* src, ushort* srcStart, int sourceLength, out Vector256<sbyte>
 1749            {
 5926701750                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 5926701751                Vector256<ushort> utf16VectorLower = Avx.LoadVector256(src);
 5926701752                Vector256<ushort> utf16VectorUpper = Avx.LoadVector256(src + 16);
 1753
 5926701754                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1755                {
 15101756                    str = default;
 15101757                    return false;
 1758                }
 1759
 5911601760                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper).AsSByte();
 5911601761                return true;
 1762            }
 1763
 1764            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1765            public unsafe bool TryLoadVector128(ushort* src, ushort* srcStart, int sourceLength, out Vector128<byte> str
 1766            {
 782981767                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 782981768                Vector128<ushort> utf16VectorLower = Vector128.LoadUnsafe(ref *src);
 782981769                Vector128<ushort> utf16VectorUpper = Vector128.LoadUnsafe(ref *src, 8);
 782981770                if (Ascii.VectorContainsNonAsciiChar(utf16VectorLower | utf16VectorUpper))
 1771                {
 20181772                    str = default;
 20181773                    return false;
 1774                }
 1775
 762801776                str = Ascii.ExtractAsciiVector(utf16VectorLower, utf16VectorUpper);
 762801777                return true;
 1778            }
 1779
 1780            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1781            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 1782            public unsafe bool TryLoadArmVector128x4(ushort* src, ushort* srcStart, int sourceLength,
 1783                out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> str4)
 1784            {
 01785                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 01786                var (s11, s12, s21, s22) = AdvSimd.Arm64.Load4xVector128AndUnzip(src);
 01787                var (s31, s32, s41, s42) = AdvSimd.Arm64.Load4xVector128AndUnzip(src + 32);
 1788
 01789                if (Ascii.VectorContainsNonAsciiChar(s11 | s12 | s21 | s22 | s31 | s32 | s41 | s42))
 1790                {
 01791                    str1 = str2 = str3 = str4 = default;
 01792                    return false;
 1793                }
 1794
 01795                str1 = Ascii.ExtractAsciiVector(s11, s31);
 01796                str2 = Ascii.ExtractAsciiVector(s12, s32);
 01797                str3 = Ascii.ExtractAsciiVector(s21, s41);
 01798                str4 = Ascii.ExtractAsciiVector(s22, s42);
 1799
 01800                return true;
 1801            }
 1802#endif // NET
 1803
 1804            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1805            public unsafe int DecodeFourElements(ushort* source, ref sbyte decodingMap)
 1806            {
 1807                // The 'source' span expected to have at least 4 elements, and the 'decodingMap' consists 256 sbytes
 3000001808                uint t0 = source[0];
 3000001809                uint t1 = source[1];
 3000001810                uint t2 = source[2];
 3000001811                uint t3 = source[3];
 1812
 3000001813                if (((t0 | t1 | t2 | t3) & 0xffffff00) != 0)
 1814                {
 46521815                    return -1; // One or more chars falls outside the 00..ff range, invalid Base64 character.
 1816                }
 1817
 2953481818                int i0 = Unsafe.Add(ref decodingMap, (int)t0);
 2953481819                int i1 = Unsafe.Add(ref decodingMap, (int)t1);
 2953481820                int i2 = Unsafe.Add(ref decodingMap, (int)t2);
 2953481821                int i3 = Unsafe.Add(ref decodingMap, (int)t3);
 1822
 2953481823                i0 <<= 18;
 2953481824                i1 <<= 12;
 2953481825                i2 <<= 6;
 1826
 2953481827                i0 |= i3;
 2953481828                i1 |= i2;
 1829
 2953481830                i0 |= i1;
 2953481831                return i0;
 1832            }
 1833
 1834            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1835            public unsafe int DecodeRemaining(ushort* srcEnd, ref sbyte decodingMap, long remaining, out uint t2, out ui
 1836            {
 1837                uint t0;
 1838                uint t1;
 296721839                t2 = EncodingPad;
 296721840                t3 = EncodingPad;
 1841                switch (remaining)
 1842                {
 1843                    case 2:
 01844                        t0 = srcEnd[-2];
 01845                        t1 = srcEnd[-1];
 01846                        break;
 1847                    case 3:
 01848                        t0 = srcEnd[-3];
 01849                        t1 = srcEnd[-2];
 01850                        t2 = srcEnd[-1];
 01851                        break;
 1852                    case 4:
 296721853                        t0 = srcEnd[-4];
 296721854                        t1 = srcEnd[-3];
 296721855                        t2 = srcEnd[-2];
 296721856                        t3 = srcEnd[-1];
 296721857                        break;
 1858                    default:
 01859                        return -1;
 1860                }
 1861
 296721862                if (((t0 | t1 | t2 | t3) & 0xffffff00) != 0)
 1863                {
 6941864                    return -1;
 1865                }
 1866
 289781867                int i0 = Unsafe.Add(ref decodingMap, (IntPtr)t0);
 289781868                int i1 = Unsafe.Add(ref decodingMap, (IntPtr)t1);
 1869
 289781870                i0 <<= 18;
 289781871                i1 <<= 12;
 1872
 289781873                i0 |= i1;
 289781874                return i0;
 1875            }
 1876
 1877            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1878            public int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<ushort> span)
 1879            {
 3227481880                for (int i = 0; i < span.Length; i++)
 1881                {
 1613641882                    if (!IsWhiteSpace(span[i]))
 1883                    {
 559241884                        return i;
 1885                    }
 1886                }
 1887
 101888                return -1;
 1889            }
 1890
 1891            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 1892            [SuppressReadyToRunCanonicalRooting]
 1893            public OperationStatus DecodeWithWhiteSpaceBlockwiseWrapper<TBase64Decoder>(TBase64Decoder decoder, ReadOnly
 1894                Span<byte> bytes, ref int bytesConsumed, ref int bytesWritten, bool isFinalBlock = true) where TBase64De
 34841895                DecodeWithWhiteSpaceBlockwise(default(Base64DecoderChar), source, bytes, ref bytesConsumed, ref bytesWri
 1896        }
 1897    }
 1898}

D:\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.Diagnostics.CodeAnalysis;
 5using System.Runtime.CompilerServices;
 6using System.Runtime.InteropServices;
 7#if NET
 8using System.Runtime.Intrinsics;
 9using System.Runtime.Intrinsics.Arm;
 10using System.Runtime.Intrinsics.Wasm;
 11using System.Runtime.Intrinsics.X86;
 12#endif
 13
 14namespace System.Buffers.Text
 15{
 16    // AVX2 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arch/avx
 17    // Vector128 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arc
 18    internal static partial class Base64Helper
 19    {
 20        internal static unsafe OperationStatus EncodeTo<TBase64Encoder, T>(TBase64Encoder encoder, ReadOnlySpan<byte> so
 21            Span<T> destination, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true)
 22            where TBase64Encoder : IBase64Encoder<T>
 23            where T : unmanaged
 24        {
 25            if (source.IsEmpty)
 26            {
 027                bytesConsumed = 0;
 028                bytesWritten = 0;
 029                return OperationStatus.Done;
 30            }
 31
 10475832            fixed (byte* srcBytes = &MemoryMarshal.GetReference(source))
 10475833            fixed (T* destBytes = &MemoryMarshal.GetReference(destination))
 34            {
 10475835                int srcLength = source.Length;
 10475836                int destLength = destination.Length;
 10475837                int maxSrcLength = encoder.GetMaxSrcLength(srcLength, destLength);
 38
 10475839                byte* src = srcBytes;
 10475840                T* dest = destBytes;
 10475841                byte* srcEnd = srcBytes + (uint)srcLength;
 10475842                byte* srcMax = srcBytes + (uint)maxSrcLength;
 43
 44#if NET
 10475845                if (maxSrcLength >= 16)
 46                {
 9245047                    byte* end = srcMax - 64;
 9245048                    if (Vector512.IsHardwareAccelerated && Avx512Vbmi.IsSupported && (end >= src))
 49                    {
 050                        Avx512Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 51
 052                        if (src == srcEnd)
 53                            goto DoneExit;
 54                    }
 55
 9245056                    end = srcMax - 32;
 9245057                    if (Avx2.IsSupported && (end >= src))
 58                    {
 8358659                        Avx2Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 60
 8358661                        if (src == srcEnd)
 62                            goto DoneExit;
 63                    }
 64
 9245065                    end = srcMax - 48;
 66                    if (AdvSimd.Arm64.IsSupported && (end >= src))
 67                    {
 68                        AdvSimdEncode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 69
 70                        if (src == srcEnd)
 71                            goto DoneExit;
 72                    }
 73
 9245074                    end = srcMax - 16;
 9245075                    if ((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported || PackedSimd.IsSupported) && BitConverter.IsLit
 76                    {
 2396477                        Vector128Encode(encoder, ref src, ref dest, end, maxSrcLength, destLength, srcBytes, destBytes);
 78
 2396479                        if (src == srcEnd)
 80                            goto DoneExit;
 81                    }
 82                }
 83#endif
 10475884                ref byte encodingMap = ref MemoryMarshal.GetReference(encoder.EncodingMap);
 85
 10475886                srcMax -= 2;
 38845687                while (src < srcMax)
 88                {
 28369889                    encoder.EncodeThreeAndWrite(src, dest, ref encodingMap);
 28369890                    src += 3;
 28369891                    dest += 4;
 92                }
 93
 10475894                if (srcMax + 2 != srcEnd)
 95                    goto DestinationTooSmallExit;
 96
 10475897                if (!isFinalBlock)
 98                {
 702899                    if (src == srcEnd)
 1960100                        goto DoneExit;
 101
 102                    goto NeedMoreData;
 103                }
 104
 97730105                if (src + 1 == srcEnd)
 106                {
 18720107                    encoder.EncodeOneOptionallyPadTwo(src, dest, ref encodingMap);
 18720108                    src += 1;
 18720109                    dest += encoder.IncrementPadTwo;
 110                }
 79010111                else if (src + 2 == srcEnd)
 112                {
 14222113                    encoder.EncodeTwoOptionallyPadOne(src, dest, ref encodingMap);
 14222114                    src += 2;
 14222115                    dest += encoder.IncrementPadOne;
 116                }
 117
 118            DoneExit:
 99690119                bytesConsumed = (int)(src - srcBytes);
 99690120                bytesWritten = (int)(dest - destBytes);
 99690121                return OperationStatus.Done;
 122
 123            DestinationTooSmallExit:
 0124                bytesConsumed = (int)(src - srcBytes);
 0125                bytesWritten = (int)(dest - destBytes);
 0126                return OperationStatus.DestinationTooSmall;
 127
 128            NeedMoreData:
 5068129                bytesConsumed = (int)(src - srcBytes);
 5068130                bytesWritten = (int)(dest - destBytes);
 5068131                return OperationStatus.NeedMoreData;
 132            }
 133        }
 134
 135#if NET
 136        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 137        [CompExactlyDependsOn(typeof(Avx512BW))]
 138        [CompExactlyDependsOn(typeof(Avx512Vbmi))]
 139        private static unsafe void Avx512Encode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T* de
 140            where TBase64Encoder : IBase64Encoder<T>
 141            where T : unmanaged
 142        {
 143            // Reference for VBMI implementation : https://github.com/WojciechMula/base64simd/tree/master/encode
 144            // If we have AVX512 support, pick off 48 bytes at a time for as long as we can.
 145            // But because we read 64 bytes at a time, ensure we have enough room to do a
 146            // full 64-byte read without segfaulting.
 147
 0148            byte* src = srcBytes;
 0149            T* dest = destBytes;
 150
 151            // The JIT won't hoist these "constants", so help it
 0152            Vector512<sbyte> shuffleVecVbmi = Vector512.Create(
 0153                0x01020001, 0x04050304, 0x07080607, 0x0a0b090a,
 0154                0x0d0e0c0d, 0x10110f10, 0x13141213, 0x16171516,
 0155                0x191a1819, 0x1c1d1b1c, 0x1f201e1f, 0x22232122,
 0156                0x25262425, 0x28292728, 0x2b2c2a2b, 0x2e2f2d2e).AsSByte();
 0157            Vector512<sbyte> vbmiLookup = Vector512.Create(encoder.EncodingMap).AsSByte();
 158
 0159            Vector512<ushort> maskAC = Vector512.Create((uint)0x0fc0fc00).AsUInt16();
 0160            Vector512<uint> maskBB = Vector512.Create((uint)0x3f003f00);
 0161            Vector512<ushort> shiftAC = Vector512.Create((uint)0x0006000a).AsUInt16();
 0162            Vector512<ushort> shiftBB = Vector512.Create((uint)0x00080004).AsUInt16();
 163
 0164            AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 165
 166            // This algorithm requires AVX512VBMI support.
 167            // Vbmi was first introduced in CannonLake and is available from IceLake on.
 168
 169            // str = [...|PONM|LKJI|HGFE|DCBA]
 0170            Vector512<sbyte> str = Vector512.Load(src).AsSByte();
 171
 0172            while (true)
 173            {
 174                // Step 1 : Split 48 bytes into 64 bytes with each byte using 6-bits from input
 175                // str = [...|KLJK|HIGH|EFDE|BCAB]
 0176                str = Avx512Vbmi.PermuteVar64x8(str, shuffleVecVbmi);
 177
 178                // TO-DO- This can be achieved faster with multishift
 179                // Consider the first 4 bytes - BCAB
 180                // temp1    = [...|0000cccc|cc000000|aaaaaa00|00000000]
 0181                Vector512<ushort> temp1 = (str.AsUInt16() & maskAC);
 182
 183                // temp2    = [...|00000000|00cccccc|00000000|00aaaaaa]
 0184                Vector512<ushort> temp2 = Avx512BW.ShiftRightLogicalVariable(temp1, shiftAC).AsUInt16();
 185
 186                // temp3    = [...|ccdddddd|00000000|aabbbbbb|cccc0000]
 0187                Vector512<ushort> temp3 = Avx512BW.ShiftLeftLogicalVariable(str.AsUInt16(), shiftBB).AsUInt16();
 188
 189                // str      = [...|00dddddd|00cccccc|00bbbbbb|00aaaaaa]
 0190                str = Vector512.ConditionalSelect(maskBB, temp3.AsUInt32(), temp2.AsUInt32()).AsSByte();
 191
 192                // Step 2: Now we have the indices calculated. Next step is to use these indices to translate.
 0193                str = Avx512Vbmi.PermuteVar64x8(vbmiLookup, str);
 194
 0195                encoder.StoreVector512ToDestination(dest, destStart, destLength, str.AsByte());
 196
 0197                src += 48;
 0198                dest += 64;
 199
 0200                if (src > srcEnd)
 201                    break;
 202
 0203                AssertRead<Vector512<sbyte>>(src, srcStart, sourceLength);
 0204                str = Vector512.Load(src).AsSByte();
 205            }
 206
 0207            srcBytes = src;
 0208            destBytes = dest;
 0209        }
 210
 211        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 212        [CompExactlyDependsOn(typeof(Avx2))]
 213        private static unsafe void Avx2Encode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T* dest
 214            where TBase64Encoder : IBase64Encoder<T>
 215            where T : unmanaged
 216        {
 217            // If we have AVX2 support, pick off 24 bytes at a time for as long as we can.
 218            // But because we read 32 bytes at a time, ensure we have enough room to do a
 219            // full 32-byte read without segfaulting.
 220
 221            // translation from SSSE3 into AVX2 of procedure
 222            // This one works with shifted (4 bytes) input in order to
 223            // be able to work efficiently in the 2 128-bit lanes
 224
 225            // srcBytes, bytes MSB to LSB:
 226            // 0 0 0 0 x w v u t s r q p o n m
 227            // l k j i h g f e d c b a 0 0 0 0
 228
 229            // The JIT won't hoist these "constants", so help it
 83586230            Vector256<sbyte> shuffleVec = Vector256.Create(
 83586231                5, 4, 6, 5,
 83586232                8, 7, 9, 8,
 83586233                11, 10, 12, 11,
 83586234                14, 13, 15, 14,
 83586235                1, 0, 2, 1,
 83586236                4, 3, 5, 4,
 83586237                7, 6, 8, 7,
 83586238                10, 9, 11, 10);
 239
 83586240            Vector256<sbyte> lut = Vector256.Create(
 83586241                65, 71, -4, -4,
 83586242                -4, -4, -4, -4,
 83586243                -4, -4, -4, -4,
 83586244                encoder.Avx2LutChar62, encoder.Avx2LutChar63, 0, 0,
 83586245                65, 71, -4, -4,
 83586246                -4, -4, -4, -4,
 83586247                -4, -4, -4, -4,
 83586248                encoder.Avx2LutChar62, encoder.Avx2LutChar63, 0, 0);
 249
 83586250            Vector256<sbyte> maskAC = Vector256.Create(0x0fc0fc00).AsSByte();
 83586251            Vector256<sbyte> maskBB = Vector256.Create(0x003f03f0).AsSByte();
 83586252            Vector256<ushort> shiftAC = Vector256.Create(0x04000040).AsUInt16();
 83586253            Vector256<short> shiftBB = Vector256.Create(0x01000010).AsInt16();
 83586254            Vector256<byte> const51 = Vector256.Create((byte)51);
 83586255            Vector256<sbyte> const25 = Vector256.Create((sbyte)25);
 256
 83586257            byte* src = srcBytes;
 83586258            T* dest = destBytes;
 259
 260            // first load is done at c-0 not to get a segfault
 83586261            AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 83586262            Vector256<sbyte> str = Avx.LoadVector256(src).AsSByte();
 263
 264            // shift by 4 bytes, as required by Reshuffle
 83586265            str = Avx2.PermuteVar8x32(str.AsInt32(), Vector256.Create(
 83586266                0, 0, 0, 0,
 83586267                0, 0, 0, 0,
 83586268                1, 0, 0, 0,
 83586269                2, 0, 0, 0,
 83586270                3, 0, 0, 0,
 83586271                4, 0, 0, 0,
 83586272                5, 0, 0, 0,
 83586273                6, 0, 0, 0).AsInt32()).AsSByte();
 274
 275            // Next loads are done at src-4, as required by Reshuffle, so shift it once
 83586276            src -= 4;
 277
 839442278            while (true)
 279            {
 280                // Reshuffle
 923028281                str = Avx2.Shuffle(str, shuffleVec);
 282                // str, bytes MSB to LSB:
 283                // w x v w
 284                // t u s t
 285                // q r p q
 286                // n o m n
 287                // k l j k
 288                // h i g h
 289                // e f d e
 290                // b c a b
 291
 923028292                Vector256<sbyte> t0 = str & maskAC;
 293                // bits, upper case are most significant bits, lower case are least significant bits.
 294                // 0000wwww XX000000 VVVVVV00 00000000
 295                // 0000tttt UU000000 SSSSSS00 00000000
 296                // 0000qqqq RR000000 PPPPPP00 00000000
 297                // 0000nnnn OO000000 MMMMMM00 00000000
 298                // 0000kkkk LL000000 JJJJJJ00 00000000
 299                // 0000hhhh II000000 GGGGGG00 00000000
 300                // 0000eeee FF000000 DDDDDD00 00000000
 301                // 0000bbbb CC000000 AAAAAA00 00000000
 302
 923028303                Vector256<sbyte> t2 = str & maskBB;
 304                // 00000000 00xxxxxx 000000vv WWWW0000
 305                // 00000000 00uuuuuu 000000ss TTTT0000
 306                // 00000000 00rrrrrr 000000pp QQQQ0000
 307                // 00000000 00oooooo 000000mm NNNN0000
 308                // 00000000 00llllll 000000jj KKKK0000
 309                // 00000000 00iiiiii 000000gg HHHH0000
 310                // 00000000 00ffffff 000000dd EEEE0000
 311                // 00000000 00cccccc 000000aa BBBB0000
 312
 923028313                Vector256<ushort> t1 = Avx2.MultiplyHigh(t0.AsUInt16(), shiftAC);
 314                // 00000000 00wwwwXX 00000000 00VVVVVV
 315                // 00000000 00ttttUU 00000000 00SSSSSS
 316                // 00000000 00qqqqRR 00000000 00PPPPPP
 317                // 00000000 00nnnnOO 00000000 00MMMMMM
 318                // 00000000 00kkkkLL 00000000 00JJJJJJ
 319                // 00000000 00hhhhII 00000000 00GGGGGG
 320                // 00000000 00eeeeFF 00000000 00DDDDDD
 321                // 00000000 00bbbbCC 00000000 00AAAAAA
 322
 923028323                Vector256<short> t3 = t2.AsInt16() * shiftBB;
 324                // 00xxxxxx 00000000 00vvWWWW 00000000
 325                // 00uuuuuu 00000000 00ssTTTT 00000000
 326                // 00rrrrrr 00000000 00ppQQQQ 00000000
 327                // 00oooooo 00000000 00mmNNNN 00000000
 328                // 00llllll 00000000 00jjKKKK 00000000
 329                // 00iiiiii 00000000 00ggHHHH 00000000
 330                // 00ffffff 00000000 00ddEEEE 00000000
 331                // 00cccccc 00000000 00aaBBBB 00000000
 332
 923028333                str = t1.AsSByte() | t3.AsSByte();
 334                // 00xxxxxx 00wwwwXX 00vvWWWW 00VVVVVV
 335                // 00uuuuuu 00ttttUU 00ssTTTT 00SSSSSS
 336                // 00rrrrrr 00qqqqRR 00ppQQQQ 00PPPPPP
 337                // 00oooooo 00nnnnOO 00mmNNNN 00MMMMMM
 338                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 339                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 340                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 341                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 342
 343                // Translation
 344                // LUT contains Absolute offset for all ranges:
 345                // Translate values 0..63 to the Base64 alphabet. There are five sets:
 346                // #  From      To         Abs    Index  Characters
 347                // 0  [0..25]   [65..90]   +65        0  ABCDEFGHIJKLMNOPQRSTUVWXYZ
 348                // 1  [26..51]  [97..122]  +71        1  abcdefghijklmnopqrstuvwxyz
 349                // 2  [52..61]  [48..57]    -4  [2..11]  0123456789
 350                // 3  [62]      [43]       -19       12  +
 351                // 4  [63]      [47]       -16       13  /
 352
 353                // Create LUT indices from input:
 354                // the index for range #0 is right, others are 1 less than expected:
 923028355                Vector256<byte> indices = Avx2.SubtractSaturate(str.AsByte(), const51);
 356
 357                // mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0:
 923028358                Vector256<sbyte> mask = Avx2.CompareGreaterThan(str, const25);
 359
 360                // subtract -1, so add 1 to indices for range #[1..4], All indices are now correct:
 923028361                Vector256<sbyte> tmp = indices.AsSByte() - mask;
 362
 363                // Add offsets to input values:
 923028364                str += Avx2.Shuffle(lut, tmp);
 365
 923028366                encoder.StoreVector256ToDestination(dest, destStart, destLength, str.AsByte());
 367
 923028368                src += 24;
 923028369                dest += 32;
 370
 923028371                if (src > srcEnd)
 372                    break;
 373
 374                // Load at src-4, as required by Reshuffle (already shifted by -4)
 839442375                AssertRead<Vector256<sbyte>>(src, srcStart, sourceLength);
 839442376                str = Avx.LoadVector256(src).AsSByte();
 377            }
 378
 83586379            srcBytes = src + 4;
 83586380            destBytes = dest;
 83586381        }
 382
 383        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 384        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 385        private static unsafe void AdvSimdEncode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T* d
 386            where TBase64Encoder : IBase64Encoder<T>
 387            where T : unmanaged
 388        {
 389            // C# implementation of https://github.com/aklomp/base64/blob/3a5add8652076612a8407627a42c768736a4263f/lib/a
 390            Vector128<byte> str1;
 391            Vector128<byte> str2;
 392            Vector128<byte> str3;
 393            Vector128<byte> res1;
 394            Vector128<byte> res2;
 395            Vector128<byte> res3;
 396            Vector128<byte> res4;
 397            Vector128<byte> tblEnc1 = Vector128.Create("ABCDEFGHIJKLMNOP"u8).AsByte();
 398            Vector128<byte> tblEnc2 = Vector128.Create("QRSTUVWXYZabcdef"u8).AsByte();
 399            Vector128<byte> tblEnc3 = Vector128.Create("ghijklmnopqrstuv"u8).AsByte();
 400            Vector128<byte> tblEnc4 = Vector128.Create(encoder.AdvSimdLut4).AsByte();
 401            byte* src = srcBytes;
 402            T* dest = destBytes;
 403
 404            // If we have Neon support, pick off 48 bytes at a time for as long as we can.
 405            do
 406            {
 407                // Load 48 bytes and deinterleave:
 408                AssertRead<Vector128<byte>>(src, srcStart, sourceLength);
 409                (str1, str2, str3) = AdvSimd.Arm64.Load3xVector128AndUnzip(src);
 410
 411                // Divide bits of three input bytes over four output bytes:
 412                res1 = str1 >>> 2;
 413                res2 = str2 >>> 4;
 414                res3 = str3 >>> 6;
 415                res2 = AdvSimd.ShiftLeftAndInsert(res2, str1, 4);
 416                res3 = AdvSimd.ShiftLeftAndInsert(res3, str2, 2);
 417
 418                // Clear top two bits:
 419                res2 &= AdvSimd.DuplicateToVector128((byte)0x3F);
 420                res3 &= AdvSimd.DuplicateToVector128((byte)0x3F);
 421                res4 = str3 & AdvSimd.DuplicateToVector128((byte)0x3F);
 422
 423                // The bits have now been shifted to the right locations;
 424                // translate their values 0..63 to the Base64 alphabet.
 425                // Use a 64-byte table lookup:
 426                res1 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res1);
 427                res2 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res2);
 428                res3 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res3);
 429                res4 = AdvSimd.Arm64.VectorTableLookup((tblEnc1, tblEnc2, tblEnc3, tblEnc4), res4);
 430
 431                // Interleave and store result:
 432                encoder.StoreArmVector128x4ToDestination(dest, destStart, destLength, res1, res2, res3, res4);
 433
 434                src += 48;
 435                dest += 64;
 436            } while (src <= srcEnd);
 437
 438            srcBytes = src;
 439            destBytes = dest;
 440        }
 441
 442        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 443        [CompExactlyDependsOn(typeof(Ssse3))]
 444        [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 445        [CompExactlyDependsOn(typeof(PackedSimd))]
 446        private static unsafe void Vector128Encode<TBase64Encoder, T>(TBase64Encoder encoder, ref byte* srcBytes, ref T*
 447            where TBase64Encoder : IBase64Encoder<T>
 448            where T : unmanaged
 449        {
 450            // If we have SSSE3 support, pick off 12 bytes at a time for as long as we can.
 451            // But because we read 16 bytes at a time, ensure we have enough room to do a
 452            // full 16-byte read without segfaulting.
 453
 454            // srcBytes, bytes MSB to LSB:
 455            // 0 0 0 0 l k j i h g f e d c b a
 456
 457            // The JIT won't hoist these "constants", so help it
 458            Vector128<byte> shuffleVec = Vector128.Create(0x01020001, 0x04050304, 0x07080607, 0x0A0B090A).AsByte();
 23964459            Vector128<byte> lut = Vector128.Create(0xFCFC4741, 0xFCFCFCFC, 0xFCFCFCFC, encoder.Ssse3AdvSimdLutE3).AsByte
 23964460            Vector128<byte> maskAC = Vector128.Create(0x0fc0fc00).AsByte();
 23964461            Vector128<byte> maskBB = Vector128.Create(0x003f03f0).AsByte();
 23964462            Vector128<ushort> shiftAC = Vector128.Create(0x04000040).AsUInt16();
 23964463            Vector128<short> shiftBB = Vector128.Create(0x01000010).AsInt16();
 23964464            Vector128<byte> const51 = Vector128.Create((byte)51);
 23964465            Vector128<sbyte> const25 = Vector128.Create((sbyte)25);
 23964466            Vector128<byte> mask8F = Vector128.Create((byte)0x8F);
 467
 23964468            byte* src = srcBytes;
 23964469            T* dest = destBytes;
 470
 471            //while (remaining >= 16)
 472            do
 473            {
 25452474                AssertRead<Vector128<sbyte>>(src, srcStart, sourceLength);
 25452475                Vector128<byte> str = Vector128.LoadUnsafe(ref *src);
 476
 477                // Reshuffle
 25452478                str = SimdShuffle(str, shuffleVec, mask8F);
 479                // str, bytes MSB to LSB:
 480                // k l j k
 481                // h i g h
 482                // e f d e
 483                // b c a b
 484
 25452485                Vector128<byte> t0 = str & maskAC;
 486                // bits, upper case are most significant bits, lower case are least significant bits
 487                // 0000kkkk LL000000 JJJJJJ00 00000000
 488                // 0000hhhh II000000 GGGGGG00 00000000
 489                // 0000eeee FF000000 DDDDDD00 00000000
 490                // 0000bbbb CC000000 AAAAAA00 00000000
 491
 25452492                Vector128<byte> t2 = str & maskBB;
 493                // 00000000 00llllll 000000jj KKKK0000
 494                // 00000000 00iiiiii 000000gg HHHH0000
 495                // 00000000 00ffffff 000000dd EEEE0000
 496                // 00000000 00cccccc 000000aa BBBB0000
 497
 498                Vector128<ushort> t1;
 25452499                if (Ssse3.IsSupported)
 500                {
 25452501                    t1 = Sse2.MultiplyHigh(t0.AsUInt16(), shiftAC);
 502                }
 503                else if (AdvSimd.Arm64.IsSupported)
 504                {
 505                    Vector128<ushort> odd = Vector128.ShiftRightLogical(AdvSimd.Arm64.UnzipOdd(t0.AsUInt16(), t0.AsUInt1
 506                    Vector128<ushort> even = Vector128.ShiftRightLogical(AdvSimd.Arm64.UnzipEven(t0.AsUInt16(), t0.AsUIn
 507                    t1 = AdvSimd.Arm64.ZipLow(even, odd);
 508                }
 509                else if (PackedSimd.IsSupported)
 510                {
 511                    // MultiplyHigh by {2^6, 2^10} is a right shift of the even u16 lanes by 10 and the odd lanes by 6.
 512                    Vector128<ushort> shr6 = Vector128.ShiftRightLogical(t0.AsUInt16(), 6);
 513                    Vector128<ushort> shr10 = Vector128.ShiftRightLogical(t0.AsUInt16(), 10);
 514                    t1 = Vector128.ConditionalSelect(Vector128.Create(0x0000FFFFu).AsUInt16(), shr10, shr6);
 515                }
 516                else
 517                {
 518                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 0519                    ThrowUnreachableException();
 520                    t1 = default;
 521                }
 522                // 00000000 00kkkkLL 00000000 00JJJJJJ
 523                // 00000000 00hhhhII 00000000 00GGGGGG
 524                // 00000000 00eeeeFF 00000000 00DDDDDD
 525                // 00000000 00bbbbCC 00000000 00AAAAAA
 526
 25452527                Vector128<short> t3 = t2.AsInt16() * shiftBB;
 528                // 00llllll 00000000 00jjKKKK 00000000
 529                // 00iiiiii 00000000 00ggHHHH 00000000
 530                // 00ffffff 00000000 00ddEEEE 00000000
 531                // 00cccccc 00000000 00aaBBBB 00000000
 532
 25452533                str = t1.AsByte() | t3.AsByte();
 534                // 00llllll 00kkkkLL 00jjKKKK 00JJJJJJ
 535                // 00iiiiii 00hhhhII 00ggHHHH 00GGGGGG
 536                // 00ffffff 00eeeeFF 00ddEEEE 00DDDDDD
 537                // 00cccccc 00bbbbCC 00aaBBBB 00AAAAAA
 538
 539                // Translation
 540                // LUT contains Absolute offset for all ranges:
 541                // Translate values 0..63 to the Base64 alphabet. There are five sets:
 542                // #  From      To         Abs    Index  Characters
 543                // 0  [0..25]   [65..90]   +65        0  ABCDEFGHIJKLMNOPQRSTUVWXYZ
 544                // 1  [26..51]  [97..122]  +71        1  abcdefghijklmnopqrstuvwxyz
 545                // 2  [52..61]  [48..57]    -4  [2..11]  0123456789
 546                // 3  [62]      [43]       -19       12  +
 547                // 4  [63]      [47]       -16       13  /
 548
 549                // Create LUT indices from input:
 550                // the index for range #0 is right, others are 1 less than expected:
 551                Vector128<byte> indices;
 25452552                if (Ssse3.IsSupported)
 553                {
 25452554                    indices = Sse2.SubtractSaturate(str.AsByte(), const51);
 555                }
 556                else if (AdvSimd.IsSupported)
 557                {
 558                    indices = AdvSimd.SubtractSaturate(str.AsByte(), const51);
 559                }
 560                else if (PackedSimd.IsSupported)
 561                {
 562                    indices = PackedSimd.SubtractSaturate(str.AsByte(), const51);
 563                }
 564                else
 565                {
 566                    // We explicitly recheck each IsSupported query to ensure that the trimmer can see which paths are l
 0567                    ThrowUnreachableException();
 568                    indices = default;
 569                }
 570
 571                // mask is 0xFF (-1) for range #[1..4] and 0x00 for range #0:
 25452572                Vector128<sbyte> mask = Vector128.GreaterThan(str.AsSByte(), const25);
 573
 574                // subtract -1, so add 1 to indices for range #[1..4], All indices are now correct:
 25452575                Vector128<sbyte> tmp = indices.AsSByte() - mask;
 576
 577                // Add offsets to input values:
 25452578                str += SimdShuffle(lut, tmp.AsByte(), mask8F);
 579
 25452580                encoder.StoreVector128ToDestination(dest, destStart, destLength, str);
 581
 25452582                src += 12;
 25452583                dest += 16;
 584            }
 25452585            while (src <= srcEnd);
 586
 23964587            srcBytes = src;
 23964588            destBytes = dest;
 23964589        }
 590#endif
 591
 592        internal static unsafe OperationStatus EncodeToUtf8InPlace<TBase64Encoder>(TBase64Encoder encoder, Span<byte> bu
 593            where TBase64Encoder : IBase64Encoder<byte>
 594        {
 7028595            if (buffer.IsEmpty)
 596            {
 0597                bytesWritten = 0;
 0598                return OperationStatus.Done;
 599            }
 600
 7028601            fixed (byte* bufferBytes = &MemoryMarshal.GetReference(buffer))
 602            {
 7028603                int encodedLength = encoder.GetMaxEncodedLength(dataLength);
 7028604                if (buffer.Length < encodedLength)
 605                {
 0606                    bytesWritten = 0;
 0607                    return OperationStatus.DestinationTooSmall;
 608                }
 609
 7028610                int leftover = (int)((uint)dataLength % 3); // how many bytes after packs of 3
 611
 7028612                uint destinationIndex = encoder.GetInPlaceDestinationLength(encodedLength, leftover);
 7028613                uint sourceIndex = (uint)(dataLength - leftover);
 7028614                ref byte encodingMap = ref MemoryMarshal.GetReference(encoder.EncodingMap);
 615
 616                // encode last pack to avoid conditional in the main loop
 7028617                if (leftover != 0)
 618                {
 5068619                    if (leftover == 1)
 620                    {
 2880621                        encoder.EncodeOneOptionallyPadTwo(bufferBytes + sourceIndex, bufferBytes + destinationIndex, ref
 622                    }
 623                    else
 624                    {
 2188625                        encoder.EncodeTwoOptionallyPadOne(bufferBytes + sourceIndex, bufferBytes + destinationIndex, ref
 626                    }
 627
 5068628                    destinationIndex -= 4;
 629                }
 630
 7028631                sourceIndex -= 3;
 1042992632                while ((int)sourceIndex >= 0)
 633                {
 1035964634                    uint result = Encode(bufferBytes + sourceIndex, ref encodingMap);
 1035964635                    Unsafe.WriteUnaligned(bufferBytes + destinationIndex, result);
 1035964636                    destinationIndex -= 4;
 1035964637                    sourceIndex -= 3;
 638                }
 639
 7028640                bytesWritten = encodedLength;
 7028641                return OperationStatus.Done;
 642            }
 643        }
 644
 645        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 646        private static unsafe uint Encode(byte* threeBytes, ref byte encodingMap)
 647        {
 1079074648            uint t0 = threeBytes[0];
 1079074649            uint t1 = threeBytes[1];
 1079074650            uint t2 = threeBytes[2];
 651
 1079074652            uint i = (t0 << 16) | (t1 << 8) | t2;
 653
 1079074654            uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 1079074655            uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 1079074656            uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 1079074657            uint i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F));
 658
 1079074659            return ConstructResult(i0, i1, i2, i3);
 660        }
 661
 662        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 663        private static uint ConstructResult(uint i0, uint i1, uint i2, uint i3)
 664        {
 665            if (BitConverter.IsLittleEndian)
 666            {
 1094278667                return i0 | (i1 << 8) | (i2 << 16) | (i3 << 24);
 668            }
 669            else
 670            {
 671                return (i0 << 24) | (i1 << 16) | (i2 << 8) | i3;
 672            }
 673        }
 674
 675        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 676        public static unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, ushort* dest, ref byte encodingMap)
 677        {
 678            uint t0 = oneByte[0];
 679
 12960680            uint i = t0 << 8;
 681
 12960682            uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10));
 12960683            uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F));
 684
 685            uint result;
 686
 12960687            if (BitConverter.IsLittleEndian)
 688            {
 12960689                result = (i0 | (i1 << 16));
 690            }
 691            else
 692            {
 693                result = ((i0 << 16) | i1);
 694            }
 695
 12960696            Unsafe.WriteUnaligned(dest, result);
 12960697        }
 698
 699        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 700        public static unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, ushort* dest, ref byte encodingMap)
 701        {
 9846702            uint t0 = twoBytes[0];
 9846703            uint t1 = twoBytes[1];
 704
 9846705            uint i = (t0 << 16) | (t1 << 8);
 706
 9846707            ushort i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 9846708            ushort i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 9846709            ushort i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 710
 9846711            dest[0] = i0;
 9846712            dest[1] = i1;
 9846713            dest[2] = i2;
 9846714        }
 715
 716        internal const uint EncodingPad = '='; // '=', for padding
 717
 718        internal const int MaximumEncodeLength = (int.MaxValue / 4) * 3; // 1610612733
 719
 720        internal readonly struct Base64EncoderByte : IBase64Encoder<byte>
 721        {
 111787722            public ReadOnlySpan<byte> EncodingMap => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"u
 723
 167172724            public sbyte Avx2LutChar62 => -19;  // char '+' diff
 725
 167172726            public sbyte Avx2LutChar63 => -16;   // char '/' diff
 727
 0728            public ReadOnlySpan<byte> AdvSimdLut4 => "wxyz0123456789+/"u8;
 729
 23964730            public uint Ssse3AdvSimdLutE3 => 0x0000F0ED;
 731
 18720732            public int IncrementPadTwo => 4;
 733
 14222734            public int IncrementPadOne => 4;
 735
 736            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 737            public int GetMaxSrcLength(int srcLength, int destLength) =>
 104758738                srcLength <= MaximumEncodeLength && destLength >= Base64.GetMaxEncodedToUtf8Length(srcLength) ?
 104758739                srcLength : (destLength >> 2) * 3;
 740
 7028741            public uint GetInPlaceDestinationLength(int encodedLength, int _) => (uint)(encodedLength - 4);
 742
 7028743            public int GetMaxEncodedLength(int srcLength) => Base64.GetMaxEncodedToUtf8Length(srcLength);
 744
 745            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 746            public unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, byte* dest, ref byte encodingMap)
 747            {
 8640748                uint t0 = oneByte[0];
 749
 8640750                uint i = t0 << 8;
 751
 8640752                uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 10));
 8640753                uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 4) & 0x3F));
 754
 8640755                uint result = ConstructResult(i0, i1, EncodingPad, EncodingPad);
 8640756                Unsafe.WriteUnaligned(dest, result);
 8640757            }
 758
 759            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 760            public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, byte* dest, ref byte encodingMap)
 761            {
 6564762                uint t0 = twoBytes[0];
 6564763                uint t1 = twoBytes[1];
 764
 6564765                uint i = (t0 << 16) | (t1 << 8);
 766
 6564767                uint i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 6564768                uint i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 6564769                uint i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 770
 6564771                uint result = ConstructResult(i0, i1, i2, EncodingPad);
 6564772                Unsafe.WriteUnaligned(dest, result);
 6564773            }
 774
 775#if NET
 776            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 777            public unsafe void StoreVector512ToDestination(byte* dest, byte* destStart, int destLength, Vector512<byte> 
 778            {
 0779                AssertWrite<Vector512<sbyte>>(dest, destStart, destLength);
 0780                str.Store(dest);
 0781            }
 782
 783            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 784            [CompExactlyDependsOn(typeof(Avx2))]
 785            public unsafe void StoreVector256ToDestination(byte* dest, byte* destStart, int destLength, Vector256<byte> 
 786            {
 314060787                AssertWrite<Vector256<sbyte>>(dest, destStart, destLength);
 314060788                Avx.Store(dest, str.AsByte());
 314060789            }
 790
 791            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 792            public unsafe void StoreVector128ToDestination(byte* dest, byte* destStart, int destLength, Vector128<byte> 
 793            {
 8580794                AssertWrite<Vector128<sbyte>>(dest, destStart, destLength);
 8580795                str.Store(dest);
 8580796            }
 797
 798            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 799            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 800            public unsafe void StoreArmVector128x4ToDestination(byte* dest, byte* destStart, int destLength,
 801                Vector128<byte> res1, Vector128<byte> res2, Vector128<byte> res3, Vector128<byte> res4)
 802            {
 0803                AssertWrite<Vector128<byte>>(dest, destStart, destLength);
 0804                AdvSimd.Arm64.StoreVectorAndZip(dest, (res1, res2, res3, res4));
 0805            }
 806#endif // NET
 807
 808            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 809            public unsafe void EncodeThreeAndWrite(byte* threeBytes, byte* destination, ref byte encodingMap)
 810            {
 43110811                uint result = Encode(threeBytes, ref encodingMap);
 43110812                Unsafe.WriteUnaligned(destination, result);
 43110813            }
 814        }
 815
 816        internal readonly struct Base64EncoderChar : IBase64Encoder<ushort>
 817        {
 87188818            public ReadOnlySpan<byte> EncodingMap => default(Base64EncoderByte).EncodingMap;
 819
 144432820            public sbyte Avx2LutChar62 => default(Base64EncoderByte).Avx2LutChar62;
 821
 144432822            public sbyte Avx2LutChar63 => default(Base64EncoderByte).Avx2LutChar63;
 823
 0824            public ReadOnlySpan<byte> AdvSimdLut4 => default(Base64EncoderByte).AdvSimdLut4;
 825
 15804826            public uint Ssse3AdvSimdLutE3 => default(Base64EncoderByte).Ssse3AdvSimdLutE3;
 827
 12960828            public int IncrementPadTwo => default(Base64EncoderByte).IncrementPadTwo;
 829
 9846830            public int IncrementPadOne => default(Base64EncoderByte).IncrementPadOne;
 831
 832            public int GetMaxSrcLength(int srcLength, int destLength) =>
 87188833                default(Base64EncoderByte).GetMaxSrcLength(srcLength, destLength);
 834
 0835            public uint GetInPlaceDestinationLength(int encodedLength, int _) => 0; // not used for char encoding
 836
 0837            public int GetMaxEncodedLength(int _) => 0;  // not used for char encoding
 838
 839            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 840            public unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, ushort* dest, ref byte encodingMap)
 841            {
 12960842                Base64Helper.EncodeOneOptionallyPadTwo(oneByte, dest, ref encodingMap);
 12960843                dest[2] = (ushort)EncodingPad;
 12960844                dest[3] = (ushort)EncodingPad;
 12960845            }
 846
 847            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 848            public unsafe void EncodeTwoOptionallyPadOne(byte* twoBytes, ushort* dest, ref byte encodingMap)
 849            {
 9846850                Base64Helper.EncodeTwoOptionallyPadOne(twoBytes, dest, ref encodingMap);
 9846851                dest[3] = (ushort)EncodingPad;
 9846852            }
 853
 854#if NET
 855            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 856            public unsafe void StoreVector512ToDestination(ushort* dest, ushort* destStart, int destLength, Vector512<by
 857            {
 0858                AssertWrite<Vector512<short>>(dest, destStart, destLength);
 0859                (Vector512<ushort> utf16LowVector, Vector512<ushort> utf16HighVector) = Vector512.Widen(str);
 0860                utf16LowVector.Store(dest);
 0861                utf16HighVector.Store(dest + 32);
 0862            }
 863
 864            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 865            public unsafe void StoreVector256ToDestination(ushort* dest, ushort* destStart, int destLength, Vector256<by
 866            {
 608968867                AssertWrite<Vector256<short>>(dest, destStart, destLength);
 608968868                (Vector256<ushort> utf16LowVector, Vector256<ushort> utf16HighVector) = Vector256.Widen(str);
 608968869                utf16LowVector.Store(dest);
 608968870                utf16HighVector.Store(dest + 16);
 608968871            }
 872
 873            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 874            public unsafe void StoreVector128ToDestination(ushort* dest, ushort* destStart, int destLength, Vector128<by
 875            {
 16872876                AssertWrite<Vector128<short>>(dest, destStart, destLength);
 16872877                (Vector128<ushort> utf16LowVector, Vector128<ushort> utf16HighVector) = Vector128.Widen(str);
 16872878                utf16LowVector.Store(dest);
 16872879                utf16HighVector.Store(dest + 8);
 16872880            }
 881
 882            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 883            [CompExactlyDependsOn(typeof(AdvSimd.Arm64))]
 884            public unsafe void StoreArmVector128x4ToDestination(ushort* dest, ushort* destStart, int destLength,
 885                Vector128<byte> res1, Vector128<byte> res2, Vector128<byte> res3, Vector128<byte> res4)
 886            {
 0887                AssertWrite<Vector128<short>>(dest, destStart, destLength);
 0888                (Vector128<ushort> utf16LowVector1, Vector128<ushort> utf16HighVector1) = Vector128.Widen(res1);
 0889                (Vector128<ushort> utf16LowVector2, Vector128<ushort> utf16HighVector2) = Vector128.Widen(res2);
 0890                (Vector128<ushort> utf16LowVector3, Vector128<ushort> utf16HighVector3) = Vector128.Widen(res3);
 0891                (Vector128<ushort> utf16LowVector4, Vector128<ushort> utf16HighVector4) = Vector128.Widen(res4);
 0892                AdvSimd.Arm64.StoreVectorAndZip(dest, (utf16LowVector1, utf16LowVector2, utf16LowVector3, utf16LowVector
 0893                AdvSimd.Arm64.StoreVectorAndZip(dest + 32, (utf16HighVector1, utf16HighVector2, utf16HighVector3, utf16H
 0894            }
 895#endif // NET
 896
 897            [MethodImpl(MethodImplOptions.AggressiveInlining)]
 898            public unsafe void EncodeThreeAndWrite(byte* threeBytes, ushort* destination, ref byte encodingMap)
 899            {
 900                uint t0 = threeBytes[0];
 240588901                uint t1 = threeBytes[1];
 240588902                uint t2 = threeBytes[2];
 903
 240588904                uint i = (t0 << 16) | (t1 << 8) | t2;
 905
 240588906                ulong i0 = Unsafe.Add(ref encodingMap, (IntPtr)(i >> 18));
 240588907                ulong i1 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 12) & 0x3F));
 240588908                ulong i2 = Unsafe.Add(ref encodingMap, (IntPtr)((i >> 6) & 0x3F));
 240588909                ulong i3 = Unsafe.Add(ref encodingMap, (IntPtr)(i & 0x3F));
 910
 911                ulong result;
 240588912                if (BitConverter.IsLittleEndian)
 913                {
 240588914                    result = i0 | (i1 << 16) | (i2 << 32) | (i3 << 48);
 915                }
 916                else
 917                {
 918                    result = (i0 << 48) | (i1 << 32) | (i2 << 16) | i3;
 919                }
 920
 240588921                Unsafe.WriteUnaligned(destination, result);
 240588922            }
 923        }
 924    }
 925}

D:\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        {
 135625618            int vectorElements = sizeof(TVector);
 135625619            byte* readEnd = src + vectorElements;
 135625620            byte* srcEnd = srcStart + srcLength;
 21
 135625622            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            }
 135625627        }
 28
 29        [Conditional("DEBUG")]
 30        internal static unsafe void AssertWrite<TVector>(byte* dest, byte* destStart, int destLength)
 31        {
 127994232            int vectorElements = sizeof(TVector);
 127994233            byte* writeEnd = dest + vectorElements;
 127994234            byte* destEnd = destStart + destLength;
 35
 127994236            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            }
 127994241        }
 42
 43        [Conditional("DEBUG")]
 44        internal static unsafe void AssertRead<TVector>(ushort* src, ushort* srcStart, int srcLength)
 45        {
 67096846            int vectorElements = sizeof(TVector);
 67096847            ushort* readEnd = src + vectorElements;
 67096848            ushort* srcEnd = srcStart + srcLength;
 49
 67096850            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            }
 67096855        }
 56
 57        [Conditional("DEBUG")]
 58        internal static unsafe void AssertWrite<TVector>(ushort* dest, ushort* destStart, int destLength)
 59        {
 62584060            int vectorElements = sizeof(TVector);
 62584061            ushort* writeEnd = dest + vectorElements;
 62584062            ushort* destEnd = destStart + destLength;
 63
 62584064            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            }
 62584069        }
 70
 71        [DoesNotReturn]
 72        internal static void ThrowUnreachableException()
 73        {
 74#if NET
 075            throw new UnreachableException();
 76#else
 77            throw new Exception("Unreachable");
 78#endif
 79        }
 80
 81        internal interface IBase64Encoder<T> where T : unmanaged
 82        {
 83            ReadOnlySpan<byte> EncodingMap { get; }
 84            sbyte Avx2LutChar62 { get; }
 85            sbyte Avx2LutChar63 { get; }
 86            ReadOnlySpan<byte> AdvSimdLut4 { get; }
 87            uint Ssse3AdvSimdLutE3 { get; }
 88            int GetMaxSrcLength(int srcLength, int destLength);
 89            int GetMaxEncodedLength(int srcLength);
 90            uint GetInPlaceDestinationLength(int encodedLength, int leftOver);
 91            unsafe void EncodeOneOptionallyPadTwo(byte* oneByte, T* dest, ref byte encodingMap);
 92            unsafe void EncodeTwoOptionallyPadOne(byte* oneByte, T* dest, ref byte encodingMap);
 93            unsafe void EncodeThreeAndWrite(byte* threeBytes, T* destination, ref byte encodingMap);
 94            int IncrementPadTwo { get; }
 95            int IncrementPadOne { get; }
 96#if NET
 97            unsafe void StoreVector512ToDestination(T* dest, T* destStart, int destLength, Vector512<byte> str);
 98            unsafe void StoreVector256ToDestination(T* dest, T* destStart, int destLength, Vector256<byte> str);
 99            unsafe void StoreVector128ToDestination(T* dest, T* destStart, int destLength, Vector128<byte> str);
 100            unsafe void StoreArmVector128x4ToDestination(T* dest, T* destStart, int destLength, Vector128<byte> res1,
 101                Vector128<byte> res2, Vector128<byte> res3, Vector128<byte> res4);
 102#endif // NET
 103        }
 104
 105        internal interface IBase64Decoder<T> where T : unmanaged
 106        {
 107            ReadOnlySpan<sbyte> DecodingMap { get; }
 108            ReadOnlySpan<uint> VbmiLookup0 { get; }
 109            ReadOnlySpan<uint> VbmiLookup1 { get; }
 110            ReadOnlySpan<sbyte> Avx2LutHigh { get; }
 111            ReadOnlySpan<sbyte> Avx2LutLow { get; }
 112            ReadOnlySpan<sbyte> Avx2LutShift { get; }
 113            byte MaskSlashOrUnderscore { get; }
 114            ReadOnlySpan<int> Vector128LutHigh { get; }
 115            ReadOnlySpan<int> Vector128LutLow { get; }
 116            ReadOnlySpan<uint> Vector128LutShift { get; }
 117            ReadOnlySpan<uint> AdvSimdLutOne3 { get; }
 118            uint AdvSimdLutTwo3Uint1 { get; }
 119            int SrcLength(bool isFinalBlock, int sourceLength);
 120            int GetMaxDecodedLength(int sourceLength);
 121            bool IsInvalidLength(int bufferLength);
 122            bool IsValidPadding(uint padChar);
 123#if NET
 124            bool TryDecode128Core(
 125                Vector128<byte> str,
 126                Vector128<byte> hiNibbles,
 127                Vector128<byte> maskSlashOrUnderscore,
 128                Vector128<byte> mask8F,
 129                Vector128<byte> lutLow,
 130                Vector128<byte> lutHigh,
 131                Vector128<sbyte> lutShift,
 132                Vector128<byte> shiftForUnderscore,
 133                out Vector128<byte> result);
 134            bool TryDecode256Core(
 135                Vector256<sbyte> str,
 136                Vector256<sbyte> hiNibbles,
 137                Vector256<sbyte> maskSlashOrUnderscore,
 138                Vector256<sbyte> lutLow,
 139                Vector256<sbyte> lutHigh,
 140                Vector256<sbyte> lutShift,
 141                Vector256<sbyte> shiftForUnderscore,
 142                out Vector256<sbyte> result);
 143            unsafe bool TryLoadVector512(T* src, T* srcStart, int sourceLength, out Vector512<sbyte> str);
 144            unsafe bool TryLoadAvxVector256(T* src, T* srcStart, int sourceLength, out Vector256<sbyte> str);
 145            unsafe bool TryLoadVector128(T* src, T* srcStart, int sourceLength, out Vector128<byte> str);
 146            unsafe bool TryLoadArmVector128x4(T* src, T* srcStart, int sourceLength,
 147                out Vector128<byte> str1, out Vector128<byte> str2, out Vector128<byte> str3, out Vector128<byte> str4);
 148#endif // NET
 149            unsafe int DecodeFourElements(T* source, ref sbyte decodingMap);
 150            unsafe int DecodeRemaining(T* srcEnd, ref sbyte decodingMap, long remaining, out uint t2, out uint t3);
 151            int IndexOfAnyExceptWhiteSpace(ReadOnlySpan<T> span);
 152            OperationStatus DecodeWithWhiteSpaceBlockwiseWrapper<TTBase64Decoder>(TTBase64Decoder decoder, ReadOnlySpan<
 153                Span<byte> bytes, ref int bytesConsumed, ref int bytesWritten, bool isFinalBlock = true)
 154                where TTBase64Decoder : IBase64Decoder<T>;
 155        }
 156    }
 157}

D:\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        {
 702814            int length = 0, paddingCount = 0;
 351415            T lastChar = default;
 16
 351417            if (!base64Text.IsEmpty)
 18            {
 19#if NET
 1280620                while (!base64Text.IsEmpty)
 21                {
 1267422                    int index = validatable.IndexOfAnyExcept(base64Text);
 1267423                    if ((uint)index >= (uint)base64Text.Length)
 24                    {
 39425                        length += base64Text.Length;
 39426                        lastChar = base64Text[base64Text.Length - 1];
 39427                        break;
 28                    }
 29
 1228030                    length += index;
 1228031                    if (index != 0)
 32                    {
 938433                        lastChar = base64Text[index - 1];
 34                    }
 35
 1228036                    T charToValidate = base64Text[index];
 1228037                    base64Text = base64Text.Slice(index + 1);
 38
 1228039                    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.
 3372243                        while (!base64Text.IsEmpty && validatable.IsWhiteSpace(base64Text[0]))
 44                        {
 2443045                            base64Text = base64Text.Slice(1);
 46                        }
 916047                        continue;
 48                    }
 49
 298850                    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.
 51057                    paddingCount = 1;
 1426858                    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
 682094                        if (validatable.IsEncodingPad(charToValidateInPadding))
 95                        {
 96                            // There can be at most 2 padding chars.
 30897                            if (paddingCount >= 2)
 98                            {
 99                                goto Fail;
 100                            }
 101
 250102                            paddingCount++;
 103                        }
 6512104                        else if (!validatable.IsWhiteSpace(charToValidateInPadding))
 105                        {
 106                            // Invalid char was found.
 107                            goto Fail;
 108                        }
 109                    }
 110
 118111                    length += paddingCount;
 118112                    break;
 113                }
 114
 644115                if (!validatable.ValidateAndDecodeLength(lastChar, length, paddingCount, out decodedLength))
 116                {
 117                    goto Fail;
 118                }
 119
 248120                return true;
 121            }
 122
 0123            decodedLength = 0;
 0124            return true;
 125
 126        Fail:
 3266127            decodedLength = 0;
 3266128            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
 12674173            public int IndexOfAnyExcept(ReadOnlySpan<byte> span) => span.IndexOfAnyExcept(s_validBase64Chars);
 174#else
 175            public int DecodeValue(byte value) => default(Base64DecoderByte).DecodingMap[value];
 176#endif
 52382177            public bool IsWhiteSpace(byte value) => Base64Helper.IsWhiteSpace(value);
 9808178            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            {
 644182                if (length % 4 == 0)
 183                {
 272184                    int decoded = default(Base64DecoderByte).DecodingMap[lastChar];
 272185                    if ((paddingCount == 1 && (decoded & 0x03) != 0) ||
 272186                        (paddingCount == 2 && (decoded & 0x0F) != 0))
 187                    {
 188                        // unused lower bits are not 0, reject input
 24189                        decodedLength = 0;
 24190                        return false;
 191                    }
 192
 193                    // Remove padding to get exact length.
 248194                    decodedLength = (int)((uint)length / 4 * 3) - paddingCount;
 248195                    return true;
 196                }
 197
 372198                decodedLength = 0;
 372199                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&)