File: System\Net\Mime\Base64Encoder.cs
Web Access
Project: src\src\libraries\System.Net.Mail\src\System.Net.Mail.csproj (System.Net.Mail)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
 
namespace System.Net.Mime
{
    internal sealed class Base64Encoder : ByteEncoder
    {
        private static ReadOnlySpan<byte> Base64EncodeMap => "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="u8;
 
        //the number of bytes needed to encode three bytes
        private const int SizeOfBase64EncodedBlock = 4;
 
        private readonly int _lineLength;
        private readonly Base64WriteStateInfo _writeState;
 
        internal override WriteStateInfoBase WriteState
        {
            get
            {
                Debug.Assert(_writeState != null, "_writeState was null");
                return _writeState;
            }
        }
 
        internal Base64Encoder(Base64WriteStateInfo writeStateInfo, int lineLength)
        {
            _writeState = writeStateInfo;
            _lineLength = lineLength;
        }
        protected override bool HasSpecialEncodingForCRLF => false;
 
        // no special encoding of CRLF in Base64. this method will not be used
        protected override void AppendEncodedCRLF()
        {
            throw new InvalidOperationException();
        }
 
        protected override bool LineBreakNeeded(byte b)
        {
            return LineBreakNeeded(1);
        }
 
        protected override bool LineBreakNeeded(byte[] bytes, int count)
        {
            return LineBreakNeeded(count);
        }
 
        private bool LineBreakNeeded(int numberOfBytesToAppend)
        {
            if (_lineLength == -1)
            {
                return false;
            }
 
            int bytesLeftInCurrentBlock;
            int numberOfCharsLeftInCurrentBlock;
            switch (_writeState.Padding)
            {
                case 2: // 1 byte was encoded from 3
                    bytesLeftInCurrentBlock = 2;
                    numberOfCharsLeftInCurrentBlock = 3;
                    break;
                case 1: // 2 bytes were encoded from 3
                    bytesLeftInCurrentBlock = 1;
                    numberOfCharsLeftInCurrentBlock = 2;
                    break;
                case 0: // all 3 bytes were encoded
                    bytesLeftInCurrentBlock = 0;
                    numberOfCharsLeftInCurrentBlock = 0;
                    break;
                default:
                    Debug.Fail("paddind was not in range [0,2]");
                    bytesLeftInCurrentBlock = 0;
                    numberOfCharsLeftInCurrentBlock = 0;
                    break;
            }
 
            int numberOfBytesInNewBlock = numberOfBytesToAppend - bytesLeftInCurrentBlock;
            if (numberOfBytesInNewBlock <= 0)
            {
                return false;
            }
 
            int numberOfBlocksToAppend = numberOfBytesInNewBlock / 3 + (numberOfBytesInNewBlock % 3 == 0 ? 0 : 1);
            int numberOfCharsToAppend = numberOfCharsLeftInCurrentBlock + numberOfBlocksToAppend * SizeOfBase64EncodedBlock;
 
            return WriteState.CurrentLineLength + numberOfCharsToAppend + _writeState.FooterLength > _lineLength;
        }
 
        protected override int GetCodepointSize(string value, int i)
        {
            return IsSurrogatePair(value, i) ? 2 : 1;
        }
 
        public override void AppendPadding()
        {
            switch (_writeState.Padding)
            {
                case 0: // No padding needed
                    break;
                case 2: // 2 character padding needed (1 byte was encoded instead of 3)
                    _writeState.Append(Base64EncodeMap[_writeState.LastBits]);
                    _writeState.Append(Base64EncodeMap[64]);
                    _writeState.Append(Base64EncodeMap[64]);
                    _writeState.Padding = 0;
                    break;
                case 1: // 1 character padding needed (2 bytes were encoded instead of 3)
                    _writeState.Append(Base64EncodeMap[_writeState.LastBits]);
                    _writeState.Append(Base64EncodeMap[64]);
                    _writeState.Padding = 0;
                    break;
                default:
                    Debug.Fail("paddind was not in range [0,2]");
                    break;
            }
        }
 
        protected override void ApppendEncodedByte(byte b)
        {
            // Base64 encoding transforms a group of 3 bytes into a group of 4 Base64 characters
            switch (_writeState.Padding)
            {
                case 0: // Add first byte of 3
                    _writeState.Append(Base64EncodeMap[(b & 0xfc) >> 2]);
                    _writeState.LastBits = (byte)((b & 0x03) << 4);
                    _writeState.Padding = 2;
                    break;
                case 2: // Add second byte of 3
                    _writeState.Append(Base64EncodeMap[_writeState.LastBits | ((b & 0xf0) >> 4)]);
                    _writeState.LastBits = (byte)((b & 0x0f) << 2);
                    _writeState.Padding = 1;
                    break;
                case 1: // Add third byte of 3
                    _writeState.Append(Base64EncodeMap[_writeState.LastBits | ((b & 0xc0) >> 6)]);
                    _writeState.Append(Base64EncodeMap[(b & 0x3f)]);
                    _writeState.Padding = 0;
                    break;
                default:
                    Debug.Fail("paddind was not in range [0,2]");
                    break;
            }
        }
    }
}