File: System\Net\Mime\ByteEncoder.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;
using System.Text;
 
namespace System.Net.Mime
{
    internal abstract class ByteEncoder : IByteEncoder
    {
        internal abstract WriteStateInfoBase WriteState { get; }
 
        protected abstract bool HasSpecialEncodingForCRLF { get; }
 
        public string GetEncodedString() => Encoding.ASCII.GetString(WriteState.Buffer, 0, WriteState.Length);
 
        public int EncodeBytes(byte[] buffer, int offset, int count, bool dontDeferFinalBytes, bool shouldAppendSpaceToCRLF)
        {
            // Add Encoding header, if any. e.g. =?encoding?b?
            WriteState.AppendHeader();
 
            bool _hasSpecialEncodingForCRLF = HasSpecialEncodingForCRLF;
 
            int cur = offset;
            for (; cur < count + offset; cur++)
            {
                if (LineBreakNeeded(buffer[cur]))
                {
                    AppendPadding();
                    WriteState.AppendCRLF(shouldAppendSpaceToCRLF);
                }
 
                if (_hasSpecialEncodingForCRLF && IsCRLF(buffer, cur, count + offset))
                {
                    AppendEncodedCRLF();
                    cur++;  // Transformed two chars, so shift the index to account for that
                }
                else
                {
                    ApppendEncodedByte(buffer[cur]);
                }
            }
 
            if (dontDeferFinalBytes)
            {
                AppendPadding();
            }
 
            // Write out the last footer, if any.  e.g. ?=
            WriteState.AppendFooter();
            return cur - offset;
        }
 
        public int EncodeString(string value, Encoding encoding)
        {
            Debug.Assert(value != null, "value was null");
            Debug.Assert(WriteState != null, "writestate was null");
            Debug.Assert(WriteState.Buffer != null, "writestate.buffer was null");
 
            if (encoding == Encoding.Latin1) // we don't need to check for codepoints
            {
                byte[] buffer = encoding.GetBytes(value);
                return EncodeBytes(buffer, 0, buffer.Length, true, true);
            }
 
            // Add Encoding header, if any. e.g. =?encoding?b?
            WriteState.AppendHeader();
 
            bool _hasSpecialEncodingForCRLF = HasSpecialEncodingForCRLF;
 
            int totalBytesCount = 0;
            byte[] bytes = new byte[encoding.GetMaxByteCount(2)];
            for (int i = 0; i < value.Length; ++i)
            {
                int codepointSize = GetCodepointSize(value, i);
                Debug.Assert(codepointSize == 1 || codepointSize == 2, "codepointSize was not 1 or 2");
 
                int bytesCount = encoding.GetBytes(value, i, codepointSize, bytes, 0);
                if (codepointSize == 2)
                {
                    ++i; // Transformed two chars, so shift the index to account for that
                }
 
                if (LineBreakNeeded(bytes, bytesCount))
                {
                    AppendPadding();
                    WriteState.AppendCRLF(true);
                }
 
                if (_hasSpecialEncodingForCRLF && IsCRLF(bytes, bytesCount))
                {
                    AppendEncodedCRLF();
                }
                else
                {
                    AppendEncodedCodepoint(bytes, bytesCount);
                }
                totalBytesCount += bytesCount;
            }
 
            AppendPadding();
 
            // Write out the last footer, if any.  e.g. ?=
            WriteState.AppendFooter();
 
            return totalBytesCount;
        }
 
        protected abstract void AppendEncodedCRLF();
 
        protected abstract bool LineBreakNeeded(byte b);
        protected abstract bool LineBreakNeeded(byte[] bytes, int count);
 
        protected abstract int GetCodepointSize(string value, int i);
 
        public abstract void AppendPadding();
 
        protected abstract void ApppendEncodedByte(byte b);
 
        private void AppendEncodedCodepoint(byte[] bytes, int count)
        {
            for (int i = 0; i < count; ++i)
            {
                ApppendEncodedByte(bytes[i]);
            }
        }
 
        protected static bool IsSurrogatePair(string value, int i)
        {
            return char.IsSurrogate(value[i]) && i + 1 < value.Length && char.IsSurrogatePair(value[i], value[i + 1]);
        }
 
        protected static bool IsCRLF(byte[] bytes, int count)
        {
            return count == 2 && IsCRLF(bytes, 0, count);
        }
 
        private static bool IsCRLF(byte[] buffer, int i, int bufferSize)
        {
            return buffer[i] == '\r' && i + 1 < bufferSize && buffer[i + 1] == '\n';
        }
    }
}