Summary

Class:System.Buffers.Text.Base64
Assembly:System.Memory
File(s):C:\GitHub\corefx\src\System.Memory\src\System\Buffers\Text\Base64Decoder.cs
C:\GitHub\corefx\src\System.Memory\src\System\Buffers\Text\Base64Encoder.cs
Covered lines:338
Uncovered lines:0
Coverable lines:338
Total lines:573
Line coverage:100%
Branch coverage:100%

Metrics

MethodCyclomatic complexity NPath complexity Sequence coverage Branch coverage
DecodeFromUtf8(...)20262144100100
GetMaxDecodedFromUtf8Length(...)22100100
DecodeFromUtf8InPlace(...)10512100100
Decode(...)10100100
WriteThreeLowOrderBytes(...)10100100
.cctor()10100100
EncodeToUtf8(...)864100100
GetMaxEncodedToUtf8Length(...)32100100
EncodeToUtf8InPlace(...)516100100
Encode(...)10100100
EncodeAndPadOne(...)10100100
EncodeAndPadTwo(...)10100100

File(s)

C:\GitHub\corefx\src\System.Memory\src\System\Buffers\Text\Base64Decoder.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// See the LICENSE file in the project root for more information.
 4
 5using System.Runtime.CompilerServices;
 6using System.Runtime.InteropServices;
 7
 8#if !netstandard
 9using Internal.Runtime.CompilerServices;
 10#endif
 11
 12namespace System.Buffers.Text
 13{
 14    public static partial class Base64
 15    {
 16        /// <summary>
 17        /// Decode the span of UTF-8 encoded text represented as base 64 into binary data.
 18        /// If the input is not a multiple of 4, it will decode as much as it can, to the closest multiple of 4.
 19        ///
 20        /// <param name="utf8">The input span which contains UTF-8 encoded text in base 64 that needs to be decoded.</pa
 21        /// <param name="bytes">The output span which contains the result of the operation, i.e. the decoded binary data
 22        /// <param name="consumed">The number of input bytes consumed during the operation. This can be used to slice th
 23        /// <param name="written">The number of bytes written into the output span. This can be used to slice the output
 24        /// <param name="isFinalBlock">True (default) when the input span contains the entire data to decode.
 25        /// Set to false only if it is known that the input span contains partial data with more data to follow.</param>
 26        /// <returns>It returns the OperationStatus enum values:
 27        /// - Done - on successful processing of the entire input span
 28        /// - DestinationTooSmall - if there is not enough space in the output span to fit the decoded input
 29        /// - NeedMoreData - only if isFinalBlock is false and the input is not a multiple of 4, otherwise the partial i
 30        /// - InvalidData - if the input contains bytes outside of the expected base 64 range, or if it contains invalid
 31        ///   or if the input is incomplete (i.e. not a multiple of 4) and isFinalBlock is true.</returns>
 32        /// </summary>
 33        public static OperationStatus DecodeFromUtf8(ReadOnlySpan<byte> utf8, Span<byte> bytes, out int consumed, out in
 134        {
 135            ref byte srcBytes = ref MemoryMarshal.GetReference(utf8);
 136            ref byte destBytes = ref MemoryMarshal.GetReference(bytes);
 37
 138            int srcLength = utf8.Length & ~0x3;  // only decode input up to the closest multiple of 4.
 139            int destLength = bytes.Length;
 40
 141            int sourceIndex = 0;
 142            int destIndex = 0;
 43
 144            if (utf8.Length == 0)
 145                goto DoneExit;
 46
 147            ref sbyte decodingMap = ref s_decodingMap[0];
 48
 49            // Last bytes could have padding characters, so process them separately and treat them as valid only if isFi
 50            // if isFinalBlock is false, padding characters are considered invalid
 151            int skipLastChunk = isFinalBlock ? 4 : 0;
 52
 153            int maxSrcLength = 0;
 154            if (destLength >= GetMaxDecodedFromUtf8Length(srcLength))
 155            {
 156                maxSrcLength = srcLength - skipLastChunk;
 157            }
 58            else
 159            {
 60                // This should never overflow since destLength here is less than int.MaxValue / 4 * 3 (i.e. 1610612733)
 61                // Therefore, (destLength / 3) * 4 will always be less than 2147483641
 162                maxSrcLength = (destLength / 3) * 4;
 163            }
 64
 165            while (sourceIndex < maxSrcLength)
 166            {
 167                int result = Decode(ref Unsafe.Add(ref srcBytes, sourceIndex), ref decodingMap);
 168                if (result < 0)
 169                    goto InvalidExit;
 170                WriteThreeLowOrderBytes(ref Unsafe.Add(ref destBytes, destIndex), result);
 171                destIndex += 3;
 172                sourceIndex += 4;
 173            }
 74
 175            if (maxSrcLength != srcLength - skipLastChunk)
 176                goto DestinationSmallExit;
 77
 78            // If input is less than 4 bytes, srcLength == sourceIndex == 0
 79            // If input is not a multiple of 4, sourceIndex == srcLength != 0
 180            if (sourceIndex == srcLength)
 181            {
 182                if (isFinalBlock)
 183                    goto InvalidExit;
 184                goto NeedMoreExit;
 85            }
 86
 87            // if isFinalBlock is false, we will never reach this point
 88
 189            int i0 = Unsafe.Add(ref srcBytes, srcLength - 4);
 190            int i1 = Unsafe.Add(ref srcBytes, srcLength - 3);
 191            int i2 = Unsafe.Add(ref srcBytes, srcLength - 2);
 192            int i3 = Unsafe.Add(ref srcBytes, srcLength - 1);
 93
 194            i0 = Unsafe.Add(ref decodingMap, i0);
 195            i1 = Unsafe.Add(ref decodingMap, i1);
 96
 197            i0 <<= 18;
 198            i1 <<= 12;
 99
 1100            i0 |= i1;
 101
 1102            if (i3 != EncodingPad)
 1103            {
 1104                i2 = Unsafe.Add(ref decodingMap, i2);
 1105                i3 = Unsafe.Add(ref decodingMap, i3);
 106
 1107                i2 <<= 6;
 108
 1109                i0 |= i3;
 1110                i0 |= i2;
 111
 1112                if (i0 < 0)
 1113                    goto InvalidExit;
 1114                if (destIndex > destLength - 3)
 1115                    goto DestinationSmallExit;
 1116                WriteThreeLowOrderBytes(ref Unsafe.Add(ref destBytes, destIndex), i0);
 1117                destIndex += 3;
 1118            }
 1119            else if (i2 != EncodingPad)
 1120            {
 1121                i2 = Unsafe.Add(ref decodingMap, i2);
 122
 1123                i2 <<= 6;
 124
 1125                i0 |= i2;
 126
 1127                if (i0 < 0)
 1128                    goto InvalidExit;
 1129                if (destIndex > destLength - 2)
 1130                    goto DestinationSmallExit;
 1131                Unsafe.Add(ref destBytes, destIndex) = (byte)(i0 >> 16);
 1132                Unsafe.Add(ref destBytes, destIndex + 1) = (byte)(i0 >> 8);
 1133                destIndex += 2;
 1134            }
 135            else
 1136            {
 1137                if (i0 < 0)
 1138                    goto InvalidExit;
 1139                if (destIndex > destLength - 1)
 1140                    goto DestinationSmallExit;
 1141                Unsafe.Add(ref destBytes, destIndex) = (byte)(i0 >> 16);
 1142                destIndex += 1;
 1143            }
 144
 1145            sourceIndex += 4;
 146
 1147            if (srcLength != utf8.Length)
 1148                goto InvalidExit;
 149
 1150        DoneExit:
 1151            consumed = sourceIndex;
 1152            written = destIndex;
 1153            return OperationStatus.Done;
 154
 1155        DestinationSmallExit:
 1156            if (srcLength != utf8.Length && isFinalBlock)
 1157                goto InvalidExit; // if input is not a multiple of 4, and there is no more data, return invalid data ins
 1158            consumed = sourceIndex;
 1159            written = destIndex;
 1160            return OperationStatus.DestinationTooSmall;
 161
 1162        NeedMoreExit:
 1163            consumed = sourceIndex;
 1164            written = destIndex;
 1165            return OperationStatus.NeedMoreData;
 166
 1167        InvalidExit:
 1168            consumed = sourceIndex;
 1169            written = destIndex;
 1170            return OperationStatus.InvalidData;
 1171        }
 172
 173        /// <summary>
 174        /// Returns the maximum length (in bytes) of the result if you were to deocde base 64 encoded text within a byte
 175        /// </summary>
 176        /// <exception cref="System.ArgumentOutOfRangeException">
 177        /// Thrown when the specified <paramref name="length"/> is less than 0.
 178        /// </exception>
 179        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 180        public static int GetMaxDecodedFromUtf8Length(int length)
 1181        {
 1182            if (length < 0)
 1183                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
 184
 1185            return (length >> 2) * 3;
 1186        }
 187
 188        /// <summary>
 189        /// Decode the span of UTF-8 encoded text in base 64 (in-place) into binary data.
 190        /// The decoded binary output is smaller than the text data contained in the input (the operation deflates the d
 191        /// If the input is not a multiple of 4, it will not decode any.
 192        ///
 193        /// <param name="buffer">The input span which contains the base 64 text data that needs to be decoded.</param>
 194        /// <param name="written">The number of bytes written into the buffer.</param>
 195        /// <returns>It returns the OperationStatus enum values:
 196        /// - Done - on successful processing of the entire input span
 197        /// - InvalidData - if the input contains bytes outside of the expected base 64 range, or if it contains invalid
 198        ///   or if the input is incomplete (i.e. not a multiple of 4).
 199        /// It does not return DestinationTooSmall since that is not possible for base 64 decoding.
 200        /// It does not return NeedMoreData since this method tramples the data in the buffer and
 201        /// hence can only be called once with all the data in the buffer.</returns>
 202        /// </summary>
 203        public static OperationStatus DecodeFromUtf8InPlace(Span<byte> buffer, out int written)
 1204        {
 1205            int bufferLength = buffer.Length;
 1206            int sourceIndex = 0;
 1207            int destIndex = 0;
 208
 209            // only decode input if it is a multiple of 4
 1210            if (bufferLength != ((bufferLength >> 2) * 4))
 1211                goto InvalidExit;
 1212            if (bufferLength == 0)
 1213                goto DoneExit;
 214
 1215            ref byte bufferBytes = ref MemoryMarshal.GetReference(buffer);
 216
 1217            ref sbyte decodingMap = ref s_decodingMap[0];
 218
 1219            while (sourceIndex < bufferLength - 4)
 1220            {
 1221                int result = Decode(ref Unsafe.Add(ref bufferBytes, sourceIndex), ref decodingMap);
 1222                if (result < 0)
 1223                    goto InvalidExit;
 1224                WriteThreeLowOrderBytes(ref Unsafe.Add(ref bufferBytes, destIndex), result);
 1225                destIndex += 3;
 1226                sourceIndex += 4;
 1227            }
 228
 1229            int i0 = Unsafe.Add(ref bufferBytes, bufferLength - 4);
 1230            int i1 = Unsafe.Add(ref bufferBytes, bufferLength - 3);
 1231            int i2 = Unsafe.Add(ref bufferBytes, bufferLength - 2);
 1232            int i3 = Unsafe.Add(ref bufferBytes, bufferLength - 1);
 233
 1234            i0 = Unsafe.Add(ref decodingMap, i0);
 1235            i1 = Unsafe.Add(ref decodingMap, i1);
 236
 1237            i0 <<= 18;
 1238            i1 <<= 12;
 239
 1240            i0 |= i1;
 241
 1242            if (i3 != EncodingPad)
 1243            {
 1244                i2 = Unsafe.Add(ref decodingMap, i2);
 1245                i3 = Unsafe.Add(ref decodingMap, i3);
 246
 1247                i2 <<= 6;
 248
 1249                i0 |= i3;
 1250                i0 |= i2;
 251
 1252                if (i0 < 0)
 1253                    goto InvalidExit;
 1254                WriteThreeLowOrderBytes(ref Unsafe.Add(ref bufferBytes, destIndex), i0);
 1255                destIndex += 3;
 1256            }
 1257            else if (i2 != EncodingPad)
 1258            {
 1259                i2 = Unsafe.Add(ref decodingMap, i2);
 260
 1261                i2 <<= 6;
 262
 1263                i0 |= i2;
 264
 1265                if (i0 < 0)
 1266                    goto InvalidExit;
 1267                Unsafe.Add(ref bufferBytes, destIndex) = (byte)(i0 >> 16);
 1268                Unsafe.Add(ref bufferBytes, destIndex + 1) = (byte)(i0 >> 8);
 1269                destIndex += 2;
 1270            }
 271            else
 1272            {
 1273                if (i0 < 0)
 1274                    goto InvalidExit;
 1275                Unsafe.Add(ref bufferBytes, destIndex) = (byte)(i0 >> 16);
 1276                destIndex += 1;
 1277            }
 278
 1279        DoneExit:
 1280            written = destIndex;
 1281            return OperationStatus.Done;
 282
 1283        InvalidExit:
 1284            written = destIndex;
 1285            return OperationStatus.InvalidData;
 1286        }
 287
 288        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 289        private static int Decode(ref byte encodedBytes, ref sbyte decodingMap)
 1290        {
 1291            int i0 = encodedBytes;
 1292            int i1 = Unsafe.Add(ref encodedBytes, 1);
 1293            int i2 = Unsafe.Add(ref encodedBytes, 2);
 1294            int i3 = Unsafe.Add(ref encodedBytes, 3);
 295
 1296            i0 = Unsafe.Add(ref decodingMap, i0);
 1297            i1 = Unsafe.Add(ref decodingMap, i1);
 1298            i2 = Unsafe.Add(ref decodingMap, i2);
 1299            i3 = Unsafe.Add(ref decodingMap, i3);
 300
 1301            i0 <<= 18;
 1302            i1 <<= 12;
 1303            i2 <<= 6;
 304
 1305            i0 |= i3;
 1306            i1 |= i2;
 307
 1308            i0 |= i1;
 1309            return i0;
 1310        }
 311
 312        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 313        private static void WriteThreeLowOrderBytes(ref byte destination, int value)
 1314        {
 1315            destination = (byte)(value >> 16);
 1316            Unsafe.Add(ref destination, 1) = (byte)(value >> 8);
 1317            Unsafe.Add(ref destination, 2) = (byte)value;
 1318        }
 319
 320        // Pre-computing this table using a custom string(s_characters) and GenerateDecodingMapAndVerify (found in tests
 1321        private static readonly sbyte[] s_decodingMap = {
 1322            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1323            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1324            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,         //62 is placed at index 43 (for +), 
 1325            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,         //52-61 are placed at index 48-57 (f
 1326            -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
 1327            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,         //0-25 are placed at index 65-90 (fo
 1328            -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
 1329            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,         //26-51 are placed at index 97-122 (
 1330            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,         // Bytes over 122 ('z') are invalid 
 1331            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,         // Hence, padding the map with 255, 
 1332            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1333            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1334            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1335            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1336            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1337            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 1338        };
 339    }
 340}

C:\GitHub\corefx\src\System.Memory\src\System\Buffers\Text\Base64Encoder.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// See the LICENSE file in the project root for more information.
 4
 5using System.Runtime.CompilerServices;
 6using System.Runtime.InteropServices;
 7
 8#if !netstandard
 9using Internal.Runtime.CompilerServices;
 10#endif
 11
 12namespace System.Buffers.Text
 13{
 14    /// <summary>
 15    /// Convert between binary data and UTF-8 encoded text that is represented in base 64.
 16    /// </summary>
 17    public static partial class Base64
 18    {
 19        /// <summary>
 20        /// Encode the span of binary data into UTF-8 encoded text represented as base 64.
 21        ///
 22        /// <param name="bytes">The input span which contains binary data that needs to be encoded.</param>
 23        /// <param name="utf8">The output span which contains the result of the operation, i.e. the UTF-8 encoded text i
 24        /// <param name="consumed">The number of input bytes consumed during the operation. This can be used to slice th
 25        /// <param name="written">The number of bytes written into the output span. This can be used to slice the output
 26        /// <param name="isFinalBlock">True (default) when the input span contains the entire data to decode.
 27        /// Set to false only if it is known that the input span contains partial data with more data to follow.</param>
 28        /// <returns>It returns the OperationStatus enum values:
 29        /// - Done - on successful processing of the entire input span
 30        /// - DestinationTooSmall - if there is not enough space in the output span to fit the encoded input
 31        /// - NeedMoreData - only if isFinalBlock is false, otherwise the output is padded if the input is not a multipl
 32        /// It does not return InvalidData since that is not possible for base 64 encoding.</returns>
 33        /// </summary>
 34        public static OperationStatus EncodeToUtf8(ReadOnlySpan<byte> bytes, Span<byte> utf8, out int consumed, out int 
 135        {
 136            ref byte srcBytes = ref MemoryMarshal.GetReference(bytes);
 137            ref byte destBytes = ref MemoryMarshal.GetReference(utf8);
 38
 139            int srcLength = bytes.Length;
 140            int destLength = utf8.Length;
 41
 142            int maxSrcLength = 0;
 143            if (srcLength <= MaximumEncodeLength && destLength >= GetMaxEncodedToUtf8Length(srcLength))
 144            {
 145                maxSrcLength = srcLength - 2;
 146            }
 47            else
 148            {
 149                maxSrcLength = (destLength >> 2) * 3 - 2;
 150            }
 51
 152            int sourceIndex = 0;
 153            int destIndex = 0;
 154            int result = 0;
 55
 156            ref byte encodingMap = ref s_encodingMap[0];
 57
 158            while (sourceIndex < maxSrcLength)
 159            {
 160                result = Encode(ref Unsafe.Add(ref srcBytes, sourceIndex), ref encodingMap);
 161                Unsafe.WriteUnaligned(ref Unsafe.Add(ref destBytes, destIndex), result);
 162                destIndex += 4;
 163                sourceIndex += 3;
 164            }
 65
 166            if (maxSrcLength != srcLength - 2)
 167                goto DestinationSmallExit;
 68
 169            if (isFinalBlock != true)
 170                goto NeedMoreDataExit;
 71
 172            if (sourceIndex == srcLength - 1)
 173            {
 174                result = EncodeAndPadTwo(ref Unsafe.Add(ref srcBytes, sourceIndex), ref encodingMap);
 175                Unsafe.WriteUnaligned(ref Unsafe.Add(ref destBytes, destIndex), result);
 176                destIndex += 4;
 177                sourceIndex += 1;
 178            }
 179            else if (sourceIndex == srcLength - 2)
 180            {
 181                result = EncodeAndPadOne(ref Unsafe.Add(ref srcBytes, sourceIndex), ref encodingMap);
 182                Unsafe.WriteUnaligned(ref Unsafe.Add(ref destBytes, destIndex), result);
 183                destIndex += 4;
 184                sourceIndex += 2;
 185            }
 86
 187            consumed = sourceIndex;
 188            written = destIndex;
 189            return OperationStatus.Done;
 90
 191        NeedMoreDataExit:
 192            consumed = sourceIndex;
 193            written = destIndex;
 194            return OperationStatus.NeedMoreData;
 95
 196        DestinationSmallExit:
 197            consumed = sourceIndex;
 198            written = destIndex;
 199            return OperationStatus.DestinationTooSmall;
 1100        }
 101
 102        /// <summary>
 103        /// Returns the maximum length (in bytes) of the result if you were to encode binary data within a byte span of 
 104        /// </summary>
 105        /// <exception cref="System.ArgumentOutOfRangeException">
 106        /// Thrown when the specified <paramref name="length"/> is less than 0 or larger than 1610612733 (since encode i
 107        /// </exception>
 108        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 109        public static int GetMaxEncodedToUtf8Length(int length)
 1110        {
 1111            if (length < 0 || length > MaximumEncodeLength)
 1112                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length);
 113
 1114            return (((length + 2) / 3) * 4);
 1115        }
 116
 117        /// <summary>
 118        /// Encode the span of binary data (in-place) into UTF-8 encoded text represented as base 64.
 119        /// The encoded text output is larger than the binary data contained in the input (the operation inflates the da
 120        ///
 121        /// <param name="buffer">The input span which contains binary data that needs to be encoded.
 122        /// It needs to be large enough to fit the result of the operation.</param>
 123        /// <param name="dataLength">The amount of binary data contained within the buffer that needs to be encoded
 124        /// (and needs to be smaller than the buffer length).</param>
 125        /// <param name="written">The number of bytes written into the buffer.</param>
 126        /// <returns>It returns the OperationStatus enum values:
 127        /// - Done - on successful processing of the entire buffer
 128        /// - DestinationTooSmall - if there is not enough space in the buffer beyond dataLength to fit the result of en
 129        /// It does not return NeedMoreData since this method tramples the data in the buffer and hence can only be call
 130        /// It does not return InvalidData since that is not possible for base 64 encoding.</returns>
 131        /// </summary>
 132        public static OperationStatus EncodeToUtf8InPlace(Span<byte> buffer, int dataLength, out int written)
 1133        {
 1134            int encodedLength = GetMaxEncodedToUtf8Length(dataLength);
 1135            if (buffer.Length < encodedLength)
 1136                goto FalseExit;
 137
 1138            int leftover = dataLength - dataLength / 3 * 3; // how many bytes after packs of 3
 139
 1140            int destinationIndex = encodedLength - 4;
 1141            int sourceIndex = dataLength - leftover;
 1142            int result = 0;
 143
 1144            ref byte encodingMap = ref s_encodingMap[0];
 1145            ref byte bufferBytes = ref MemoryMarshal.GetReference(buffer);
 146
 147            // encode last pack to avoid conditional in the main loop
 1148            if (leftover != 0)
 1149            {
 1150                if (leftover == 1)
 1151                {
 1152                    result = EncodeAndPadTwo(ref Unsafe.Add(ref bufferBytes, sourceIndex), ref encodingMap);
 1153                    Unsafe.WriteUnaligned(ref Unsafe.Add(ref bufferBytes, destinationIndex), result);
 1154                    destinationIndex -= 4;
 1155                }
 156                else
 1157                {
 1158                    result = EncodeAndPadOne(ref Unsafe.Add(ref bufferBytes, sourceIndex), ref encodingMap);
 1159                    Unsafe.WriteUnaligned(ref Unsafe.Add(ref bufferBytes, destinationIndex), result);
 1160                    destinationIndex -= 4;
 1161                }
 1162            }
 163
 1164            sourceIndex -= 3;
 1165            while (sourceIndex >= 0)
 1166            {
 1167                result = Encode(ref Unsafe.Add(ref bufferBytes, sourceIndex), ref encodingMap);
 1168                Unsafe.WriteUnaligned(ref Unsafe.Add(ref bufferBytes, destinationIndex), result);
 1169                destinationIndex -= 4;
 1170                sourceIndex -= 3;
 1171            }
 172
 1173            written = encodedLength;
 1174            return OperationStatus.Done;
 175
 1176        FalseExit:
 1177            written = 0;
 1178            return OperationStatus.DestinationTooSmall;
 1179        }
 180
 181        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 182        private static int Encode(ref byte threeBytes, ref byte encodingMap)
 1183        {
 1184            int i = (threeBytes << 16) | (Unsafe.Add(ref threeBytes, 1) << 8) | Unsafe.Add(ref threeBytes, 2);
 185
 1186            int i0 = Unsafe.Add(ref encodingMap, i >> 18);
 1187            int i1 = Unsafe.Add(ref encodingMap, (i >> 12) & 0x3F);
 1188            int i2 = Unsafe.Add(ref encodingMap, (i >> 6) & 0x3F);
 1189            int i3 = Unsafe.Add(ref encodingMap, i & 0x3F);
 190
 1191            return i0 | (i1 << 8) | (i2 << 16) | (i3 << 24);
 1192        }
 193
 194        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 195        private static int EncodeAndPadOne(ref byte twoBytes, ref byte encodingMap)
 1196        {
 1197            int i = (twoBytes << 16) | (Unsafe.Add(ref twoBytes, 1) << 8);
 198
 1199            int i0 = Unsafe.Add(ref encodingMap, i >> 18);
 1200            int i1 = Unsafe.Add(ref encodingMap, (i >> 12) & 0x3F);
 1201            int i2 = Unsafe.Add(ref encodingMap, (i >> 6) & 0x3F);
 202
 1203            return i0 | (i1 << 8) | (i2 << 16) | (EncodingPad << 24);
 1204        }
 205
 206        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 207        private static int EncodeAndPadTwo(ref byte oneByte, ref byte encodingMap)
 1208        {
 1209            int i = (oneByte << 8);
 210
 1211            int i0 = Unsafe.Add(ref encodingMap, i >> 10);
 1212            int i1 = Unsafe.Add(ref encodingMap, (i >> 4) & 0x3F);
 213
 1214            return i0 | (i1 << 8) | (EncodingPad << 16) | (EncodingPad << 24);
 1215        }
 216
 217        // Pre-computing this table using a custom string(s_characters) and GenerateEncodingMapAndVerify (found in tests
 1218        private static readonly byte[] s_encodingMap = {
 1219            65, 66, 67, 68, 69, 70, 71, 72,         //A..H
 1220            73, 74, 75, 76, 77, 78, 79, 80,         //I..P
 1221            81, 82, 83, 84, 85, 86, 87, 88,         //Q..X
 1222            89, 90, 97, 98, 99, 100, 101, 102,      //Y..Z, a..f
 1223            103, 104, 105, 106, 107, 108, 109, 110, //g..n
 1224            111, 112, 113, 114, 115, 116, 117, 118, //o..v
 1225            119, 120, 121, 122, 48, 49, 50, 51,     //w..z, 0..3
 1226            52, 53, 54, 55, 56, 57, 43, 47          //4..9, +, /
 1227        };
 228
 229        private const byte EncodingPad = (byte)'='; // '=', for padding
 230
 231        private const int MaximumEncodeLength = (int.MaxValue >> 2) * 3; // 1610612733
 232    }
 233}