File: Internals\System\Xml\XmlMtomWriter.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Xml;
using System.Xml.XPath;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Threading.Tasks;
 
namespace System.Xml
{
    internal interface IXmlMtomWriterInitializer
    {
        void SetOutput(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream);
    }
 
    internal class XmlMtomWriter : XmlDictionaryWriter, IXmlMtomWriterInitializer
    {
        public static XmlDictionaryWriter Create(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo)
        {
            return Create(stream, encoding, maxSizeInBytes, startInfo, null, null, true, true);
        }
 
        public static XmlDictionaryWriter Create(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream)
        {
            XmlMtomWriter writer = new XmlMtomWriter();
            writer.SetOutput(stream, encoding, maxSizeInBytes, startInfo, boundary, startUri, writeMessageHeaders, ownsStream);
            return writer;
        }
 
        // Maximum number of bytes that are inlined as base64 data without being MTOM-optimized as xop:Include
        private const int MaxInlinedBytes = 767;  // 768 will be the first MIMEd length
 
        private int _maxSizeInBytes;
        private XmlDictionaryWriter _writer;
        private XmlDictionaryWriter _infosetWriter;
        private MimeWriter _mimeWriter;
        private Encoding _encoding;
        private bool _isUTF8;
        private string _contentID;
        private string _contentType;
        private string _initialContentTypeForRootPart;
        private string _initialContentTypeForMimeMessage;
        private MemoryStream _contentTypeStream;
        private List<MimePart> _mimeParts;
        private IList<MtomBinaryData> _binaryDataChunks;
        private int _depth;
        private int _totalSizeOfMimeParts;
        private int _sizeOfBufferedBinaryData;
        private char[] _chars;
        private byte[] _bytes;
        private bool _isClosed;
        private bool _ownsStream;
 
        public XmlMtomWriter()
        {
        }
 
        public void SetOutput(Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream)
        {
            if (encoding == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(encoding));
            if (maxSizeInBytes < 0)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(maxSizeInBytes), SRP.ValueMustBeNonNegative));
            _maxSizeInBytes = maxSizeInBytes;
            _encoding = encoding;
            _isUTF8 = IsUTF8Encoding(encoding);
            Initialize(stream, startInfo, boundary, startUri, writeMessageHeaders, ownsStream);
        }
 
        private XmlDictionaryWriter Writer
        {
            get
            {
                if (!IsInitialized)
                {
                    Initialize();
                }
                return _writer;
            }
        }
 
        private bool IsInitialized
        {
            get { return (_initialContentTypeForRootPart == null); }
        }
 
        private void Initialize(Stream stream, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream)
        {
            if (stream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream));
            if (startInfo == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(startInfo));
            if (boundary == null)
                boundary = GetBoundaryString();
            if (startUri == null)
                startUri = GenerateUriForMimePart(0);
            if (!MailBnfHelper.IsValidMimeBoundary(boundary))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SRP.Format(SRP.MtomBoundaryInvalid, boundary), nameof(boundary)));
 
            _ownsStream = ownsStream;
            _isClosed = false;
            _depth = 0;
            _totalSizeOfMimeParts = 0;
            _sizeOfBufferedBinaryData = 0;
            _binaryDataChunks = null;
            _contentType = null;
            _contentTypeStream = null;
            _contentID = startUri;
            if (_mimeParts != null)
                _mimeParts.Clear();
            _mimeWriter = new MimeWriter(stream, boundary);
            _initialContentTypeForRootPart = GetContentTypeForRootMimePart(_encoding, startInfo);
            if (writeMessageHeaders)
                _initialContentTypeForMimeMessage = GetContentTypeForMimeMessage(boundary, startUri, startInfo);
        }
 
        private void Initialize()
        {
            if (_isClosed)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.XmlWriterClosed));
 
            if (_initialContentTypeForRootPart != null)
            {
                if (_initialContentTypeForMimeMessage != null)
                {
                    _mimeWriter.StartPreface();
                    _mimeWriter.WriteHeader(MimeGlobals.MimeVersionHeader, MimeGlobals.DefaultVersion);
                    _mimeWriter.WriteHeader(MimeGlobals.ContentTypeHeader, _initialContentTypeForMimeMessage);
                    _initialContentTypeForMimeMessage = null;
                }
 
                WriteMimeHeaders(_contentID, _initialContentTypeForRootPart, _isUTF8 ? MimeGlobals.Encoding8bit : MimeGlobals.EncodingBinary);
 
                Stream infosetContentStream = _mimeWriter.GetContentStream();
                IXmlTextWriterInitializer initializer = _writer as IXmlTextWriterInitializer;
                if (initializer == null)
                    _writer = XmlDictionaryWriter.CreateTextWriter(infosetContentStream, _encoding, _ownsStream);
                else
                    initializer.SetOutput(infosetContentStream, _encoding, _ownsStream);
 
                _contentID = null;
                _initialContentTypeForRootPart = null;
            }
        }
 
        private static string GetBoundaryString()
        {
            return MimeBoundaryGenerator.Next();
        }
 
        internal static bool IsUTF8Encoding(Encoding encoding)
        {
            return encoding.WebName == "utf-8";
        }
 
        private static string GetContentTypeForMimeMessage(string boundary, string startUri, string startInfo)
        {
            StringBuilder contentTypeBuilder = new StringBuilder(
                string.Format(CultureInfo.InvariantCulture, "{0}/{1};{2}=\"{3}\";{4}=\"{5}\"",
                    MtomGlobals.MediaType, MtomGlobals.MediaSubtype,
                    MtomGlobals.TypeParam, MtomGlobals.XopType,
                    MtomGlobals.BoundaryParam, boundary));
 
            if (startUri != null && startUri.Length > 0)
                contentTypeBuilder.AppendFormat(CultureInfo.InvariantCulture, ";{0}=\"<{1}>\"", MtomGlobals.StartParam, startUri);
 
            if (startInfo != null && startInfo.Length > 0)
                contentTypeBuilder.AppendFormat(CultureInfo.InvariantCulture, ";{0}=\"{1}\"", MtomGlobals.StartInfoParam, startInfo);
 
            return contentTypeBuilder.ToString();
        }
 
        private static string GetContentTypeForRootMimePart(Encoding encoding, string startInfo)
        {
            string contentType = string.Format(CultureInfo.InvariantCulture, "{0};{1}={2}", MtomGlobals.XopType, MtomGlobals.CharsetParam, CharSet(encoding));
 
            if (startInfo != null)
                contentType = string.Format(CultureInfo.InvariantCulture, "{0};{1}=\"{2}\"", contentType, MtomGlobals.TypeParam, startInfo);
 
            return contentType;
        }
 
        private static string CharSet(Encoding enc)
        {
            string name = enc.WebName;
            if (string.Compare(name, Encoding.UTF8.WebName, StringComparison.OrdinalIgnoreCase) == 0)
                return name;
            if (string.Compare(name, Encoding.Unicode.WebName, StringComparison.OrdinalIgnoreCase) == 0)
                return "utf-16LE";
            if (string.Compare(name, Encoding.BigEndianUnicode.WebName, StringComparison.OrdinalIgnoreCase) == 0)
                return "utf-16BE";
            return name;
        }
 
        public override void WriteStartElement(string prefix, string localName, string ns)
        {
            WriteBase64InlineIfPresent();
            ThrowIfElementIsXOPInclude(prefix, localName, ns);
            Writer.WriteStartElement(prefix, localName, ns);
            _depth++;
        }
 
        public override async Task WriteStartElementAsync(string prefix, string localName, string ns)
        {
            await WriteBase64InlineIfPresentAsync();
            ThrowIfElementIsXOPInclude(prefix, localName, ns);
            await Writer.WriteStartElementAsync(prefix, localName, ns);
            _depth++;
        }
 
        public override void WriteStartElement(string prefix, XmlDictionaryString localName, XmlDictionaryString ns)
        {
            if (localName == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(localName));
 
            WriteBase64InlineIfPresent();
            ThrowIfElementIsXOPInclude(prefix, localName.Value, ns == null ? null : ns.Value);
            Writer.WriteStartElement(prefix, localName, ns);
            _depth++;
        }
 
        private void ThrowIfElementIsXOPInclude(string prefix, string localName, string ns)
        {
            if (ns == null)
            {
                // This check isn't as thorough as on .NET Framework due to use of internal api's.
                // The scenario that this won't catch is when the XopIncludeNamespace has been added
                // multiple times with different prefixes. I don't believe that will be the case as
                // any attempt to add it with a different prefix would hit this code path anyway.
                var xopPrefix = Writer.LookupPrefix(MtomGlobals.XopIncludeNamespace);
                if (xopPrefix != null && xopPrefix == prefix)
                    ns = MtomGlobals.XopIncludeNamespace;
            }
 
            if (localName == MtomGlobals.XopIncludeLocalName && ns == MtomGlobals.XopIncludeNamespace)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SRP.Format(SRP.MtomDataMustNotContainXopInclude, MtomGlobals.XopIncludeLocalName, MtomGlobals.XopIncludeNamespace)));
        }
 
        public override void WriteEndElement()
        {
            WriteXOPInclude();
            Writer.WriteEndElement();
            _depth--;
            WriteXOPBinaryParts();
        }
 
        public override async Task WriteEndElementAsync()
        {
            await WriteXOPIncludeAsync();
            await Writer.WriteEndElementAsync();
            _depth--;
            await WriteXOPBinaryPartsAsync();
        }
 
        public override void WriteFullEndElement()
        {
            WriteXOPInclude();
            Writer.WriteFullEndElement();
            _depth--;
            WriteXOPBinaryParts();
        }
 
        public override void WriteValue(IStreamProvider value)
        {
            if (value == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
 
            if (Writer.WriteState == WriteState.Element)
            {
                if (_binaryDataChunks == null)
                {
                    _binaryDataChunks = new List<MtomBinaryData>();
                    _contentID = GenerateUriForMimePart((_mimeParts == null) ? 1 : _mimeParts.Count + 1);
                }
                _binaryDataChunks.Add(new MtomBinaryData(value));
            }
            else
                Writer.WriteValue(value);
        }
 
        public override Task WriteValueAsync(IStreamProvider value)
        {
            if (value == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
 
            if (Writer.WriteState == WriteState.Element)
            {
                if (_binaryDataChunks == null)
                {
                    _binaryDataChunks = new List<MtomBinaryData>();
                    _contentID = GenerateUriForMimePart((_mimeParts == null) ? 1 : _mimeParts.Count + 1);
                }
                _binaryDataChunks.Add(new MtomBinaryData(value));
                return Task.CompletedTask;
            }
            else
                return Writer.WriteValueAsync(value);
        }
 
        public override void WriteBase64(byte[] buffer, int index, int count)
        {
            if (Writer.WriteState == WriteState.Element)
            {
                if (buffer == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("buffer"));
 
                // Not checking upper bound because it will be caught by "count".  This is what XmlTextWriter does.
                if (index < 0)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(index), SRP.ValueMustBeNonNegative));
 
                if (count < 0)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SRP.ValueMustBeNonNegative));
                if (count > buffer.Length - index)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(count), SRP.Format(SRP.SizeExceedsRemainingBufferSpace, buffer.Length - index)));
 
                if (_binaryDataChunks == null)
                {
                    _binaryDataChunks = new List<MtomBinaryData>();
                    _contentID = GenerateUriForMimePart((_mimeParts == null) ? 1 : _mimeParts.Count + 1);
                }
 
                int totalSize = ValidateSizeOfMessage(_maxSizeInBytes, 0, _totalSizeOfMimeParts);
                totalSize += ValidateSizeOfMessage(_maxSizeInBytes, totalSize, _sizeOfBufferedBinaryData);
                totalSize += ValidateSizeOfMessage(_maxSizeInBytes, totalSize, count);
                _sizeOfBufferedBinaryData += count;
                _binaryDataChunks.Add(new MtomBinaryData(buffer, index, count));
            }
            else
                Writer.WriteBase64(buffer, index, count);
        }
 
        internal static int ValidateSizeOfMessage(int maxSize, int offset, int size)
        {
            if (size > (maxSize - offset))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SRP.Format(SRP.MtomExceededMaxSizeInBytes, maxSize)));
            return size;
        }
 
        private void WriteBase64InlineIfPresent()
        {
            if (_binaryDataChunks != null)
            {
                WriteBase64Inline();
            }
        }
 
        private Task WriteBase64InlineIfPresentAsync()
        {
            if (_binaryDataChunks != null)
            {
                return WriteBase64InlineAsync();
            }
            else
            {
                return Task.CompletedTask;
            }
        }
 
        private void WriteBase64Inline()
        {
            foreach (MtomBinaryData data in _binaryDataChunks)
            {
                if (data.type == MtomBinaryDataType.Provider)
                {
                    Writer.WriteValue(data.provider);
                }
                else
                {
                    Writer.WriteBase64(data.chunk, 0, data.chunk.Length);
                }
            }
            _sizeOfBufferedBinaryData = 0;
            _binaryDataChunks = null;
            _contentType = null;
            _contentID = null;
        }
 
        private async Task WriteBase64InlineAsync()
        {
            foreach (MtomBinaryData data in _binaryDataChunks)
            {
                if (data.type == MtomBinaryDataType.Provider)
                {
                    await Writer.WriteValueAsync(data.provider);
                }
                else
                {
                    await Writer.WriteBase64Async(data.chunk, 0, data.chunk.Length);
                }
            }
            _sizeOfBufferedBinaryData = 0;
            _binaryDataChunks = null;
            _contentType = null;
            _contentID = null;
        }
 
        private void WriteXOPInclude()
        {
            if (_binaryDataChunks == null)
                return;
 
            bool inline = true;
            long size = 0;
            foreach (MtomBinaryData data in _binaryDataChunks)
            {
                long len = data.Length;
                if (len < 0 || len > (MaxInlinedBytes - size))
                {
                    inline = false;
                    break;
                }
                size += len;
            }
 
            if (inline)
                WriteBase64Inline();
            else
            {
                if (_mimeParts == null)
                    _mimeParts = new List<MimePart>();
 
                MimePart mimePart = new MimePart(_binaryDataChunks, _contentID, _contentType, MimeGlobals.EncodingBinary, _sizeOfBufferedBinaryData, _maxSizeInBytes);
                _mimeParts.Add(mimePart);
 
                _totalSizeOfMimeParts += ValidateSizeOfMessage(_maxSizeInBytes, _totalSizeOfMimeParts, mimePart.sizeInBytes);
                _totalSizeOfMimeParts += ValidateSizeOfMessage(_maxSizeInBytes, _totalSizeOfMimeParts, _mimeWriter.GetBoundarySize());
 
                Writer.WriteStartElement(MtomGlobals.XopIncludePrefix, MtomGlobals.XopIncludeLocalName, MtomGlobals.XopIncludeNamespace);
                Writer.WriteStartAttribute(MtomGlobals.XopIncludeHrefLocalName, MtomGlobals.XopIncludeHrefNamespace);
                Writer.WriteValue(string.Format(CultureInfo.InvariantCulture, "{0}{1}", MimeGlobals.ContentIDScheme, _contentID));
                Writer.WriteEndAttribute();
                Writer.WriteEndElement();
                _binaryDataChunks = null;
                _sizeOfBufferedBinaryData = 0;
                _contentType = null;
                _contentID = null;
            }
        }
 
        private async Task WriteXOPIncludeAsync()
        {
            if (_binaryDataChunks == null)
                return;
 
            bool inline = true;
            long size = 0;
            foreach (MtomBinaryData data in _binaryDataChunks)
            {
                long len = data.Length;
                if (len < 0 || len > (MaxInlinedBytes - size))
                {
                    inline = false;
                    break;
                }
                size += len;
            }
 
            if (inline)
                await WriteBase64InlineAsync();
            else
            {
                if (_mimeParts == null)
                    _mimeParts = new List<MimePart>();
 
                MimePart mimePart = new MimePart(_binaryDataChunks, _contentID, _contentType, MimeGlobals.EncodingBinary, _sizeOfBufferedBinaryData, _maxSizeInBytes);
                _mimeParts.Add(mimePart);
 
                _totalSizeOfMimeParts += ValidateSizeOfMessage(_maxSizeInBytes, _totalSizeOfMimeParts, mimePart.sizeInBytes);
                _totalSizeOfMimeParts += ValidateSizeOfMessage(_maxSizeInBytes, _totalSizeOfMimeParts, _mimeWriter.GetBoundarySize());
 
                await Writer.WriteStartElementAsync(MtomGlobals.XopIncludePrefix, MtomGlobals.XopIncludeLocalName, MtomGlobals.XopIncludeNamespace);
                Writer.WriteStartAttribute(MtomGlobals.XopIncludeHrefLocalName, MtomGlobals.XopIncludeHrefNamespace);
                Writer.WriteString(string.Format(CultureInfo.InvariantCulture, "{0}{1}", MimeGlobals.ContentIDScheme, _contentID));
                Writer.WriteEndAttribute();
                await Writer.WriteEndElementAsync();
                _binaryDataChunks = null;
                _sizeOfBufferedBinaryData = 0;
                _contentType = null;
                _contentID = null;
            }
        }
 
        public static string GenerateUriForMimePart(int index)
        {
            return string.Format(CultureInfo.InvariantCulture, "http://tempuri.org/{0}/{1}", index, DateTime.Now.Ticks);
        }
 
        private void WriteXOPBinaryParts()
        {
            if (_depth > 0 || _mimeWriter.WriteState == MimeWriterState.Closed)
                return;
 
            if (Writer.WriteState != WriteState.Closed)
                Writer.Flush();
 
            if (_mimeParts != null)
            {
                foreach (MimePart part in _mimeParts)
                {
                    WriteMimeHeaders(part.contentID, part.contentType, part.contentTransferEncoding);
                    Stream s = _mimeWriter.GetContentStream();
                    int bufferSize = 65536;
                    Stream stream = null;
                    foreach (MtomBinaryData data in part.binaryData)
                    {
                        if (data.type == MtomBinaryDataType.Provider)
                        {
                            stream = data.provider.GetStream();
                            if (stream == null)
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SRP.XmlInvalidStream));
 
                            stream.CopyTo(s, bufferSize);
                            
                            data.provider.ReleaseStream(stream);
                        }
                        else
                        {
                            s.Write(data.chunk, 0, data.chunk.Length);
                        }
                    }
                }
                _mimeParts.Clear();
            }
            _mimeWriter.Close();
        }
 
        private async Task WriteXOPBinaryPartsAsync()
        {
            if (_depth > 0 || _mimeWriter.WriteState == MimeWriterState.Closed)
                return;
 
            if (Writer.WriteState != WriteState.Closed)
                await Writer.FlushAsync();
 
            if (_mimeParts != null)
            {
                foreach (MimePart part in _mimeParts)
                {
                    await WriteMimeHeadersAsync(part.contentID, part.contentType, part.contentTransferEncoding);
                    Stream s = await _mimeWriter.GetContentStreamAsync();
                    int bufferSize = 65536;
                    Stream stream = null;
                    foreach (MtomBinaryData data in part.binaryData)
                    {
                        if (data.type == MtomBinaryDataType.Provider)
                        {
                            stream = data.provider.GetStream();
                            if (stream == null)
                                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SRP.XmlInvalidStream));
 
                            await stream.CopyToAsync(s, bufferSize);
 
                            data.provider.ReleaseStream(stream);
                        }
                        else
                        {
                            await s.WriteAsync(data.chunk, 0, data.chunk.Length);
                        }
                    }
                }
                _mimeParts.Clear();
            }
            await _mimeWriter.CloseAsync();
        }
 
        private void WriteMimeHeaders(string contentID, string contentType, string contentTransferEncoding)
        {
            _mimeWriter.StartPart();
            if (contentID != null)
                _mimeWriter.WriteHeader(MimeGlobals.ContentIDHeader, string.Format(CultureInfo.InvariantCulture, "<{0}>", contentID));
            if (contentTransferEncoding != null)
                _mimeWriter.WriteHeader(MimeGlobals.ContentTransferEncodingHeader, contentTransferEncoding);
            if (contentType != null)
                _mimeWriter.WriteHeader(MimeGlobals.ContentTypeHeader, contentType);
        }
 
        private async Task WriteMimeHeadersAsync(string contentID, string contentType, string contentTransferEncoding)
        {
            await _mimeWriter.StartPartAsync();
            if (contentID != null)
                _mimeWriter.WriteHeader(MimeGlobals.ContentIDHeader, string.Format(CultureInfo.InvariantCulture, "<{0}>", contentID));
            if (contentTransferEncoding != null)
                _mimeWriter.WriteHeader(MimeGlobals.ContentTransferEncodingHeader, contentTransferEncoding);
            if (contentType != null)
                _mimeWriter.WriteHeader(MimeGlobals.ContentTypeHeader, contentType);
        }
#if NO
        public override bool CanSubsetElements
        {
            get { return Writer.CanSubsetElements; }
        }
#endif
        public override void Close()
        {
            if (!_isClosed)
            {
                _isClosed = true;
                if (IsInitialized)
                {
                    WriteXOPInclude();
                    if (Writer.WriteState == WriteState.Element ||
                        Writer.WriteState == WriteState.Attribute ||
                        Writer.WriteState == WriteState.Content)
                    {
                        Writer.WriteEndDocument();
                    }
                    Writer.Flush();
                    _depth = 0;
                    WriteXOPBinaryParts();
                    Writer.Close();
                }
            }
        }
 
        private void CheckIfStartContentTypeAttribute(string localName, string ns)
        {
            if (localName != null && localName == MtomGlobals.MimeContentTypeLocalName
                && ns != null && (ns == MtomGlobals.MimeContentTypeNamespace200406 || ns == MtomGlobals.MimeContentTypeNamespace200505))
            {
                _contentTypeStream = new MemoryStream();
                this._infosetWriter = Writer;
                _writer = XmlDictionaryWriter.CreateBinaryWriter(_contentTypeStream);
                Writer.WriteStartElement("Wrapper");
                Writer.WriteStartAttribute(localName, ns);
            }
        }
 
        private void CheckIfEndContentTypeAttribute()
        {
            if (_contentTypeStream != null)
            {
                Writer.WriteEndAttribute();
                Writer.WriteEndElement();
                Writer.Flush();
                _contentTypeStream.Position = 0;
                XmlReader contentTypeReader = XmlDictionaryReader.CreateBinaryReader(_contentTypeStream, null, XmlDictionaryReaderQuotas.Max, null, null);
                while (contentTypeReader.Read())
                {
                    if (contentTypeReader.IsStartElement("Wrapper"))
                    {
                        _contentType = contentTypeReader.GetAttribute(MtomGlobals.MimeContentTypeLocalName, MtomGlobals.MimeContentTypeNamespace200406);
                        if (_contentType == null)
                        {
                            _contentType = contentTypeReader.GetAttribute(MtomGlobals.MimeContentTypeLocalName, MtomGlobals.MimeContentTypeNamespace200505);
                        }
                        break;
                    }
                }
                _writer = _infosetWriter;
                this._infosetWriter = null;
                _contentTypeStream = null;
                if (_contentType != null)
                    Writer.WriteString(_contentType);
            }
        }
 
#if NO
        public override bool ElementSubsetting
        {
            get
            {
                return Writer.ElementSubsetting;
            }
            set
            {
                Writer.ElementSubsetting = value;
            }
        }
#endif
        public override void Flush()
        {
            if (IsInitialized)
                Writer.Flush();
        }
 
        public override Task FlushAsync()
        {
            if (IsInitialized)
                return Writer.FlushAsync();
            else
                return Task.CompletedTask;
        }
 
        public override string LookupPrefix(string ns)
        {
            return Writer.LookupPrefix(ns);
        }
 
        public override XmlWriterSettings Settings
        {
            get
            {
                return Writer.Settings;
            }
        }
 
        public override void WriteAttributes(XmlReader reader, bool defattr)
        {
            Writer.WriteAttributes(reader, defattr);
        }
 
        public override void WriteBinHex(byte[] buffer, int index, int count)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteBinHex(buffer, index, count);
        }
 
        public override void WriteCData(string text)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteCData(text);
        }
 
        public override void WriteCharEntity(char ch)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteCharEntity(ch);
        }
 
        public override void WriteChars(char[] buffer, int index, int count)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteChars(buffer, index, count);
        }
 
        public override void WriteComment(string text)
        {
            // Don't write comments after the document element
            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed)
                return;
 
            WriteBase64InlineIfPresent();
            Writer.WriteComment(text);
        }
 
        public override void WriteDocType(string name, string pubid, string sysid, string subset)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteDocType(name, pubid, sysid, subset);
        }
#if NO
        public override void WriteElementSubset(ArraySegment<byte> buffer)
        {
            Writer.WriteElementSubset(buffer);
        }
#endif
        public override void WriteEndAttribute()
        {
            CheckIfEndContentTypeAttribute();
            Writer.WriteEndAttribute();
        }
 
        public override void WriteEndDocument()
        {
            WriteXOPInclude();
            Writer.WriteEndDocument();
            _depth = 0;
            WriteXOPBinaryParts();
        }
 
        public override void WriteEntityRef(string name)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteEntityRef(name);
        }
 
        public override void WriteName(string name)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteName(name);
        }
 
        public override void WriteNmToken(string name)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteNmToken(name);
        }
 
        protected override void WriteTextNode(XmlDictionaryReader reader, bool attribute)
        {
            Type type = reader.ValueType;
            if (type == typeof(string))
            {
                if (reader.CanReadValueChunk)
                {
                    if (_chars == null)
                    {
                        _chars = new char[256];
                    }
                    int count;
                    while ((count = reader.ReadValueChunk(_chars, 0, _chars.Length)) > 0)
                    {
                        WriteChars(_chars, 0, count);
                    }
                }
                else
                {
                    WriteString(reader.Value);
                }
                if (!attribute)
                {
                    reader.Read();
                }
            }
            else if (type == typeof(byte[]))
            {
                if (reader.CanReadBinaryContent)
                {
                    // Its best to read in buffers that are a multiple of 3 so we don't break base64 boundaries when converting text
                    if (_bytes == null)
                    {
                        _bytes = new byte[384];
                    }
                    int count;
                    while ((count = reader.ReadValueAsBase64(_bytes, 0, _bytes.Length)) > 0)
                    {
                        WriteBase64(_bytes, 0, count);
                    }
                }
                else
                {
                    WriteString(reader.Value);
                }
                if (!attribute)
                {
                    reader.Read();
                }
            }
            else
            {
                base.WriteTextNode(reader, attribute);
            }
        }
 
        public override void WriteNode(XPathNavigator navigator, bool defattr)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteNode(navigator, defattr);
        }
 
        public override void WriteProcessingInstruction(string name, string text)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteProcessingInstruction(name, text);
        }
 
        public override void WriteQualifiedName(string localName, string namespaceUri)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteQualifiedName(localName, namespaceUri);
        }
 
        public override void WriteRaw(char[] buffer, int index, int count)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteRaw(buffer, index, count);
        }
 
        public override void WriteRaw(string data)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteRaw(data);
        }
 
        public override void WriteStartAttribute(string prefix, string localName, string ns)
        {
            Writer.WriteStartAttribute(prefix, localName, ns);
            CheckIfStartContentTypeAttribute(localName, ns);
        }
 
        public override void WriteStartAttribute(string prefix, XmlDictionaryString localName, XmlDictionaryString ns)
        {
            Writer.WriteStartAttribute(prefix, localName, ns);
            if (localName != null && ns != null)
                CheckIfStartContentTypeAttribute(localName.Value, ns.Value);
        }
 
        public override void WriteStartDocument()
        {
            Writer.WriteStartDocument();
        }
 
        public override void WriteStartDocument(bool standalone)
        {
            Writer.WriteStartDocument(standalone);
        }
 
        public override WriteState WriteState
        {
            get
            {
                return Writer.WriteState;
            }
        }
 
        public override void WriteString(string text)
        {
            // Don't write whitespace after the document element
            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(text))
                return;
 
            WriteBase64InlineIfPresent();
            Writer.WriteString(text);
        }
 
        public override void WriteString(XmlDictionaryString value)
        {
            // Don't write whitespace after the document element
            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value.Value))
                return;
 
            WriteBase64InlineIfPresent();
            Writer.WriteString(value);
        }
 
        public override void WriteSurrogateCharEntity(char lowChar, char highChar)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteSurrogateCharEntity(lowChar, highChar);
        }
 
        public override void WriteWhitespace(string whitespace)
        {
            // Don't write whitespace after the document element
            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed)
                return;
 
            WriteBase64InlineIfPresent();
            Writer.WriteWhitespace(whitespace);
        }
 
        public override void WriteValue(object value)
        {
            IStreamProvider sp = value as IStreamProvider;
            if (sp != null)
            {
                WriteValue(sp);
            }
            else
            {
                WriteBase64InlineIfPresent();
                Writer.WriteValue(value);
            }
        }
 
        public override void WriteValue(string value)
        {
            // Don't write whitespace after the document element
            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value))
                return;
 
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
        public override void WriteValue(bool value)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
        public override void WriteValue(DateTime value)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
        public override void WriteValue(double value)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
        public override void WriteValue(int value)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
        public override void WriteValue(long value)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
#if DECIMAL_FLOAT_API
        public override void WriteValue(decimal value)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
        public override void WriteValue(float value)
        {
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
#endif
        public override void WriteValue(XmlDictionaryString value)
        {
            // Don't write whitespace after the document element
            if (_depth == 0 && _mimeWriter.WriteState == MimeWriterState.Closed && XmlConverter.IsWhitespace(value.Value))
                return;
 
            WriteBase64InlineIfPresent();
            Writer.WriteValue(value);
        }
 
        public override void WriteXmlnsAttribute(string prefix, string ns)
        {
            Writer.WriteXmlnsAttribute(prefix, ns);
        }
 
        public override void WriteXmlnsAttribute(string prefix, XmlDictionaryString ns)
        {
            Writer.WriteXmlnsAttribute(prefix, ns);
        }
 
        public override string XmlLang
        {
            get
            {
                return Writer.XmlLang;
            }
        }
 
        public override XmlSpace XmlSpace
        {
            get
            {
                return Writer.XmlSpace;
            }
        }
 
        private static class MimeBoundaryGenerator
        {
            private static long id;
            private static string prefix;
 
            static MimeBoundaryGenerator()
            {
                prefix = string.Concat(Guid.NewGuid().ToString(), "+id=");
            }
 
            internal static string Next()
            {
                long nextId = Interlocked.Increment(ref id);
                return string.Format(CultureInfo.InvariantCulture, "{0}{1}", prefix, nextId);
            }
        }
 
        private class MimePart
        {
            internal IList<MtomBinaryData> binaryData;
            internal string contentID;
            internal string contentType;
            internal string contentTransferEncoding;
            internal int sizeInBytes;
 
            internal MimePart(IList<MtomBinaryData> binaryData, string contentID, string contentType, string contentTransferEncoding, int sizeOfBufferedBinaryData, int maxSizeInBytes)
            {
                this.binaryData = binaryData;
                this.contentID = contentID;
                this.contentType = contentType ?? MtomGlobals.DefaultContentTypeForBinary;
                this.contentTransferEncoding = contentTransferEncoding;
                sizeInBytes = GetSize(contentID, contentType, contentTransferEncoding, sizeOfBufferedBinaryData, maxSizeInBytes);
            }
 
            private static int GetSize(string contentID, string contentType, string contentTransferEncoding, int sizeOfBufferedBinaryData, int maxSizeInBytes)
            {
                int size = XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, 0, MimeGlobals.CRLF.Length * 3);
                if (contentTransferEncoding != null)
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGlobals.ContentTransferEncodingHeader, contentTransferEncoding, maxSizeInBytes));
                if (contentType != null)
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGlobals.ContentTypeHeader, contentType, maxSizeInBytes));
                if (contentID != null)
                {
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, MimeWriter.GetHeaderSize(MimeGlobals.ContentIDHeader, contentID, maxSizeInBytes));
                    size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, 2); // include '<' and '>'
                }
                size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, sizeOfBufferedBinaryData);
                return size;
            }
        }
    }
 
 
    internal static class MtomGlobals
    {
        internal static string XopIncludeLocalName = "Include";
        internal static string XopIncludeNamespace = "http://www.w3.org/2004/08/xop/include";
        internal static string XopIncludePrefix = "xop";
        internal static string XopIncludeHrefLocalName = "href";
        internal static string XopIncludeHrefNamespace = string.Empty;
        internal static string MediaType = "multipart";
        internal static string MediaSubtype = "related";
        internal static string BoundaryParam = "boundary";
        internal static string TypeParam = "type";
        internal static string XopMediaType = "application";
        internal static string XopMediaSubtype = "xop+xml";
        internal static string XopType = "application/xop+xml";
        internal static string StartParam = "start";
        internal static string StartInfoParam = "start-info";
        internal static string ActionParam = "action";
        internal static string CharsetParam = "charset";
        internal static string MimeContentTypeLocalName = "contentType";
        internal static string MimeContentTypeNamespace200406 = "http://www.w3.org/2004/06/xmlmime";
        internal static string MimeContentTypeNamespace200505 = "http://www.w3.org/2005/05/xmlmime";
        internal static string DefaultContentTypeForBinary = "application/octet-stream";
    }
 
    internal static class MimeGlobals
    {
        internal static string MimeVersionHeader = "MIME-Version";
        internal static string DefaultVersion = "1.0";
        internal static string ContentIDScheme = "cid:";
        internal static string ContentIDHeader = "Content-ID";
        internal static string ContentTypeHeader = "Content-Type";
        internal static string ContentTransferEncodingHeader = "Content-Transfer-Encoding";
        internal static string EncodingBinary = "binary";
        internal static string Encoding8bit = "8bit";
        internal static byte[] COLONSPACE = new byte[] { (byte)':', (byte)' ' };
        internal static byte[] DASHDASH = new byte[] { (byte)'-', (byte)'-' };
        internal static byte[] CRLF = new byte[] { (byte)'\r', (byte)'\n' };
        // Per RFC2045, preceding CRLF sequence is part of the boundary. MIME boundary tags begin with --
        internal static byte[] BoundaryPrefix = new byte[] { (byte)'\r', (byte)'\n', (byte)'-', (byte)'-' };
    }
 
    internal enum MimeWriterState
    {
        Start,
        StartPreface,
        StartPart,
        Header,
        Content,
        Closed,
    }
 
    internal class MimeWriter
    {
        private Stream stream;
        private byte[] boundaryBytes;
        private MimeWriterState state;
        private BufferedWrite bufferedWrite;
        private Stream contentStream;
 
        internal MimeWriter(Stream stream, string boundary)
        {
            if (stream == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(stream));
            if (boundary == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(boundary));
 
            this.stream = stream;
            boundaryBytes = MimeWriter.GetBoundaryBytes(boundary);
            state = MimeWriterState.Start;
            bufferedWrite = new BufferedWrite();
        }
 
        internal static int GetHeaderSize(string name, string value, int maxSizeInBytes)
        {
            if (name == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name));
            if (value == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value));
 
            int size = XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, 0, MimeGlobals.COLONSPACE.Length + MimeGlobals.CRLF.Length);
            size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, name.Length);
            size += XmlMtomWriter.ValidateSizeOfMessage(maxSizeInBytes, size, value.Length);
            return size;
        }
 
        internal static byte[] GetBoundaryBytes(string boundary)
        {
            byte[] boundaryBytes = new byte[boundary.Length + MimeGlobals.BoundaryPrefix.Length];
            for (int i = 0; i < MimeGlobals.BoundaryPrefix.Length; i++)
                boundaryBytes[i] = MimeGlobals.BoundaryPrefix[i];
            Encoding.ASCII.GetBytes(boundary, 0, boundary.Length, boundaryBytes, MimeGlobals.BoundaryPrefix.Length);
            return boundaryBytes;
        }
 
        internal MimeWriterState WriteState
        {
            get
            {
                return state;
            }
        }
 
        internal int GetBoundarySize()
        {
            return boundaryBytes.Length;
        }
 
        internal void StartPreface()
        {
            if (state != MimeWriterState.Start)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.MimeWriterInvalidStateForStartPreface, state.ToString())));
 
            state = MimeWriterState.StartPreface;
        }
 
        internal void StartPart()
        {
            switch (state)
            {
                case MimeWriterState.StartPart:
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.MimeWriterInvalidStateForStartPart, state.ToString())));
                default:
                    break;
            }
 
            state = MimeWriterState.StartPart;
 
            if (contentStream != null)
            {
                contentStream.Flush();
                contentStream = null;
            }
 
            bufferedWrite.Write(boundaryBytes);
            bufferedWrite.Write(MimeGlobals.CRLF);
        }
 
 
        internal async Task StartPartAsync()
        {
            switch (state)
            {
                case MimeWriterState.StartPart:
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.MimeWriterInvalidStateForStartPart, state.ToString())));
                default:
                    break;
            }
 
            state = MimeWriterState.StartPart;
 
            if (contentStream != null)
            {
                await contentStream.FlushAsync();
                contentStream = null;
            }
 
            bufferedWrite.Write(boundaryBytes);
            bufferedWrite.Write(MimeGlobals.CRLF);
        }
 
        internal void Close()
        {
            switch (state)
            {
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.MimeWriterInvalidStateForClose, state.ToString())));
                default:
                    break;
            }
 
            state = MimeWriterState.Closed;
 
            if (contentStream != null)
            {
                contentStream.Flush();
                contentStream = null;
            }
 
            bufferedWrite.Write(boundaryBytes);
            bufferedWrite.Write(MimeGlobals.DASHDASH);
            bufferedWrite.Write(MimeGlobals.CRLF);
 
            Flush();
        }
 
        internal async Task CloseAsync()
        {
            switch (state)
            {
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.MimeWriterInvalidStateForClose, state.ToString())));
                default:
                    break;
            }
 
            state = MimeWriterState.Closed;
 
            if (contentStream != null)
            {
                await contentStream.FlushAsync();
                contentStream = null;
            }
 
            bufferedWrite.Write(boundaryBytes);
            bufferedWrite.Write(MimeGlobals.DASHDASH);
            bufferedWrite.Write(MimeGlobals.CRLF);
 
            await FlushAsync();
        }
 
        private void Flush()
        {
            if (bufferedWrite.Length > 0)
            {
                stream.Write(bufferedWrite.GetBuffer(), 0, bufferedWrite.Length);
                bufferedWrite.Reset();
            }
        }
 
        private async Task FlushAsync()
        {
            if (bufferedWrite.Length > 0)
            {
                await stream.WriteAsync(bufferedWrite.GetBuffer(), 0, bufferedWrite.Length);
                bufferedWrite.Reset();
            }
        }
 
        internal void WriteHeader(string name, string value)
        {
            if (name == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(name));
            if (value == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(value));
 
            switch (state)
            {
                case MimeWriterState.Start:
                case MimeWriterState.Content:
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.MimeWriterInvalidStateForHeader, state.ToString())));
                default:
                    break;
            }
 
            state = MimeWriterState.Header;
 
            bufferedWrite.Write(name);
            bufferedWrite.Write(MimeGlobals.COLONSPACE);
            bufferedWrite.Write(value);
            bufferedWrite.Write(MimeGlobals.CRLF);
        }
 
        internal Stream GetContentStream()
        {
            switch (state)
            {
                case MimeWriterState.Start:
                case MimeWriterState.Content:
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.MimeWriterInvalidStateForContent, state.ToString())));
                default:
                    break;
            }
 
            state = MimeWriterState.Content;
            bufferedWrite.Write(MimeGlobals.CRLF);
            Flush();
            contentStream = stream;
            return contentStream;
        }
 
        internal async Task<Stream> GetContentStreamAsync()
        {
            switch (state)
            {
                case MimeWriterState.Start:
                case MimeWriterState.Content:
                case MimeWriterState.Closed:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.MimeWriterInvalidStateForContent, state.ToString())));
                default:
                    break;
            }
 
            state = MimeWriterState.Content;
            bufferedWrite.Write(MimeGlobals.CRLF);
            await FlushAsync();
            contentStream = stream;
            return contentStream;
        }
    }
 
    internal class BufferedWrite
    {
        private byte[] buffer;
        private int offset;
 
        internal BufferedWrite()
            : this(256)
        {
        }
 
        internal BufferedWrite(int initialSize)
        {
            buffer = new byte[initialSize];
        }
 
        private void EnsureBuffer(int count)
        {
            int currSize = buffer.Length;
            if (count > currSize - offset)
            {
                int newSize = currSize;
                do
                {
                    if (newSize == int.MaxValue)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new XmlException(SRP.WriteBufferOverflow));
                    newSize = (newSize < int.MaxValue / 2) ? newSize * 2 : int.MaxValue;
                }
                while (count > newSize - offset);
                byte[] newBuffer = new byte[newSize];
                Buffer.BlockCopy(buffer, 0, newBuffer, 0, offset);
                buffer = newBuffer;
            }
        }
 
        internal int Length
        {
            get
            {
                return offset;
            }
        }
 
        internal byte[] GetBuffer()
        {
            return buffer;
        }
 
        internal void Reset()
        {
            offset = 0;
        }
 
        internal void Write(byte[] value)
        {
            Write(value, 0, value.Length);
        }
 
        internal void Write(byte[] value, int index, int count)
        {
            EnsureBuffer(count);
            Buffer.BlockCopy(value, index, buffer, offset, count);
            offset += count;
        }
 
        internal void Write(string value)
        {
            Write(value, 0, value.Length);
        }
 
        internal void Write(string value, int index, int count)
        {
            EnsureBuffer(count);
            for (int i = 0; i < count; i++)
            {
                char c = value[index + i];
                if ((ushort)c > 0xFF)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SRP.Format(SRP.MimeHeaderInvalidCharacter, c, ((int)c).ToString("X", CultureInfo.InvariantCulture))));
                buffer[offset + i] = (byte)c;
            }
            offset += count;
        }
 
#if NO
        internal void Write(byte value)
        {
            EnsureBuffer(1);
            buffer[offset++] = value;
        }
 
        internal void Write(char value)
        {
            EnsureBuffer(1);
            if ((ushort)value > 0xFF)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SRP.Format(SRP.MimeHeaderInvalidCharacter, value, ((int)value).ToString("X", CultureInfo.InvariantCulture)))));
            buffer[offset++] = (byte)value;
        }
 
        internal void Write(int value)
        {
            Write(value.ToString());
        }
 
        internal void Write(char[] value)
        {
            Write(value, 0, value.Length);
        }
 
        internal void Write(char[] value, int index, int count)
        {
            EnsureBuffer(count);
            for (int i = 0; i < count; i++)
            {
                char c = value[index + i];
                if ((ushort)c > 0xFF)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new FormatException(SRP.Format(SRP.MimeHeaderInvalidCharacter, c, ((int)c).ToString("X", CultureInfo.InvariantCulture)))));
                buffer[offset + i] = (byte)c;
            }
            offset += count;
        }
 
#endif
 
    }
 
    internal enum MtomBinaryDataType { Provider, Segment }
 
    internal class MtomBinaryData
    {
        internal MtomBinaryDataType type;
 
        internal IStreamProvider provider;
        internal byte[] chunk;
 
        internal MtomBinaryData(IStreamProvider provider)
        {
            type = MtomBinaryDataType.Provider;
            this.provider = provider;
        }
 
        internal MtomBinaryData(byte[] buffer, int offset, int count)
        {
            type = MtomBinaryDataType.Segment;
            chunk = new byte[count];
            Buffer.BlockCopy(buffer, offset, chunk, 0, count);
        }
 
        internal long Length
        {
            get
            {
                if (type == MtomBinaryDataType.Segment)
                    return chunk.Length;
 
                return -1;
            }
        }
    }
}