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 
5 // Note: Following comment is fairly old - generator might not actually work
6 // WARNING: This file is generated and should not be modified directly.  Instead,
7 // modify XmlTextWriterGenerator.cxx and run gen.bat in the same directory.
8 // This batch file will execute the following commands:
9 //
10 //   cl.exe /C /EP /D _XML_UTF8_TEXT_WRITER XmlRawTextWriterGenerator.cxx > XmlUtf8RawTextWriter.cs
11 //
12 // Because these two implementations of XmlTextWriter are so similar, the C++ preprocessor
13 // is used to generate each implementation from one template file, using macros and ifdefs.
14 
15 // Note: This file was generated without #define SILVERLIGHT
16 
17 using System;
18 using System.IO;
19 using System.Xml;
20 using System.Text;
21 using System.Diagnostics;
22 using System.Globalization;
23 
24 namespace System.Xml
25 {
26     // Concrete implementation of XmlWriter abstract class that serializes events as encoded XML
27     // text.  The general-purpose XmlEncodedTextWriter uses the Encoder class to output to any
28     // encoding.  The XmlUtf8TextWriter class combined the encoding operation with serialization
29     // in order to achieve better performance.
30     internal partial class XmlEncodedRawTextWriter : XmlRawWriter
31     {
32         //
33         // Fields
34         //
35         private readonly bool _useAsync;
36 
37         // main buffer
38         protected byte[] bufBytes;
39 
40         // output stream
41         protected Stream stream;
42 
43         // encoding of the stream or text writer
44         protected Encoding encoding;
45 
46         // char type tables
47         protected XmlCharType xmlCharType = XmlCharType.Instance;
48 
49         // buffer positions
50         protected int bufPos = 1;     // buffer position starts at 1, because we need to be able to safely step back -1 in case we need to
51                                       // close an empty element or in CDATA section detection of double ]; _BUFFER[0] will always be 0
52         protected int textPos = 1;    // text end position; don't indent first element, pi, or comment
53         protected int contentPos;     // element content end position
54         protected int cdataPos;       // cdata end position
55         protected int attrEndPos;     // end of the last attribute
56         protected int bufLen = BUFSIZE;
57 
58         // flags
59         protected bool writeToNull;
60         protected bool hadDoubleBracket;
61         protected bool inAttributeValue;
62 
63         protected int bufBytesUsed;
64         protected char[] bufChars;
65 
66         // encoder for encoding chars in specified encoding when writing to stream
67         protected Encoder encoder;
68 
69         // output text writer
70         protected TextWriter writer;
71 
72         // escaping of characters invalid in the output encoding
73         protected bool trackTextContent;
74         protected bool inTextContent;
75         private int _lastMarkPos;
76         private int[] _textContentMarks;   // even indices contain text content start positions
77                                            // odd indices contain markup start positions
78         private CharEntityEncoderFallback _charEntityFallback;
79 
80         // writer settings
81         protected NewLineHandling newLineHandling;
82         protected bool closeOutput;
83         protected bool omitXmlDeclaration;
84         protected string newLineChars;
85         protected bool checkCharacters;
86 
87         protected XmlStandalone standalone;
88         protected XmlOutputMethod outputMethod;
89 
90         protected bool autoXmlDeclaration;
91         protected bool mergeCDataSections;
92 
93         //
94         // Constants
95         //
96         private const int BUFSIZE = 2048 * 3;       // Should be greater than default FileStream size (4096), otherwise the FileStream will try to cache the data
97         private const int ASYNCBUFSIZE = 64 * 1024; // Set async buffer size to 64KB
98         private const int OVERFLOW = 32;            // Allow overflow in order to reduce checks when writing out constant size markup
99         private const int INIT_MARKS_COUNT = 64;
100 
101         //
102         // Constructors
103         //
104         // Construct and initialize an instance of this class.
XmlEncodedRawTextWriter(XmlWriterSettings settings)105         protected XmlEncodedRawTextWriter(XmlWriterSettings settings)
106         {
107             _useAsync = settings.Async;
108 
109             // copy settings
110             newLineHandling = settings.NewLineHandling;
111             omitXmlDeclaration = settings.OmitXmlDeclaration;
112             newLineChars = settings.NewLineChars;
113             checkCharacters = settings.CheckCharacters;
114             closeOutput = settings.CloseOutput;
115 
116             standalone = settings.Standalone;
117             outputMethod = settings.OutputMethod;
118             mergeCDataSections = settings.MergeCDataSections;
119 
120             if (checkCharacters && newLineHandling == NewLineHandling.Replace)
121             {
122                 ValidateContentChars(newLineChars, "NewLineChars", false);
123             }
124         }
125 
126         // Construct an instance of this class that outputs text to the TextWriter interface.
XmlEncodedRawTextWriter(TextWriter writer, XmlWriterSettings settings)127         public XmlEncodedRawTextWriter(TextWriter writer, XmlWriterSettings settings) : this(settings)
128         {
129             Debug.Assert(writer != null && settings != null);
130 
131             this.writer = writer;
132             this.encoding = writer.Encoding;
133             // the buffer is allocated will OVERFLOW in order to reduce checks when writing out constant size markup
134             if (settings.Async)
135             {
136                 bufLen = ASYNCBUFSIZE;
137             }
138             this.bufChars = new char[bufLen + OVERFLOW];
139 
140             // Write the xml declaration
141             if (settings.AutoXmlDeclaration)
142             {
143                 WriteXmlDeclaration(standalone);
144                 autoXmlDeclaration = true;
145             }
146         }
147 
148         // Construct an instance of this class that serializes to a Stream interface.
XmlEncodedRawTextWriter(Stream stream, XmlWriterSettings settings)149         public XmlEncodedRawTextWriter(Stream stream, XmlWriterSettings settings) : this(settings)
150         {
151             Debug.Assert(stream != null && settings != null);
152 
153             this.stream = stream;
154             this.encoding = settings.Encoding;
155 
156             // the buffer is allocated will OVERFLOW in order to reduce checks when writing out constant size markup
157             if (settings.Async)
158             {
159                 bufLen = ASYNCBUFSIZE;
160             }
161             bufChars = new char[bufLen + OVERFLOW];
162 
163             bufBytes = new byte[bufChars.Length];
164             bufBytesUsed = 0;
165 
166             // Init escaping of characters not fitting into the target encoding
167             trackTextContent = true;
168             inTextContent = false;
169             _lastMarkPos = 0;
170             _textContentMarks = new int[INIT_MARKS_COUNT];
171             _textContentMarks[0] = 1;
172 
173             _charEntityFallback = new CharEntityEncoderFallback();
174             this.encoding = Encoding.GetEncoding(
175                 settings.Encoding.CodePage,
176                 _charEntityFallback,
177                 settings.Encoding.DecoderFallback);
178 
179             encoder = encoding.GetEncoder();
180 
181             if (!stream.CanSeek || stream.Position == 0)
182             {
183                 ReadOnlySpan<byte> bom = encoding.Preamble;
184                 if (bom.Length != 0)
185                 {
186                     this.stream.Write(bom);
187                 }
188             }
189 
190             // Write the xml declaration
191             if (settings.AutoXmlDeclaration)
192             {
193                 WriteXmlDeclaration(standalone);
194                 autoXmlDeclaration = true;
195             }
196         }
197 
198         //
199         // XmlWriter implementation
200         //
201         // Returns settings the writer currently applies.
202         public override XmlWriterSettings Settings
203         {
204             get
205             {
206                 XmlWriterSettings settings = new XmlWriterSettings();
207 
208                 settings.Encoding = this.encoding;
209                 settings.OmitXmlDeclaration = this.omitXmlDeclaration;
210                 settings.NewLineHandling = this.newLineHandling;
211                 settings.NewLineChars = this.newLineChars;
212                 settings.CloseOutput = this.closeOutput;
213                 settings.ConformanceLevel = ConformanceLevel.Auto;
214                 settings.CheckCharacters = checkCharacters;
215 
216                 settings.AutoXmlDeclaration = autoXmlDeclaration;
217                 settings.Standalone = standalone;
218                 settings.OutputMethod = outputMethod;
219 
220                 settings.ReadOnly = true;
221                 return settings;
222             }
223         }
224 
225         // Write the xml declaration.  This must be the first call.
WriteXmlDeclaration(XmlStandalone standalone)226         internal override void WriteXmlDeclaration(XmlStandalone standalone)
227         {
228             // Output xml declaration only if user allows it and it was not already output
229             if (!omitXmlDeclaration && !autoXmlDeclaration)
230             {
231                 if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
232 
233                 RawText("<?xml version=\"");
234 
235                 // Version
236                 RawText("1.0");
237 
238                 // Encoding
239                 if (encoding != null)
240                 {
241                     RawText("\" encoding=\"");
242                     RawText(encoding.WebName);
243                 }
244 
245                 // Standalone
246                 if (standalone != XmlStandalone.Omit)
247                 {
248                     RawText("\" standalone=\"");
249                     RawText(standalone == XmlStandalone.Yes ? "yes" : "no");
250                 }
251 
252                 RawText("\"?>");
253             }
254         }
255 
WriteXmlDeclaration(string xmldecl)256         internal override void WriteXmlDeclaration(string xmldecl)
257         {
258             // Output xml declaration only if user allows it and it was not already output
259             if (!omitXmlDeclaration && !autoXmlDeclaration)
260             {
261                 WriteProcessingInstruction("xml", xmldecl);
262             }
263         }
264 
265         // Serialize the document type declaration.
WriteDocType(string name, string pubid, string sysid, string subset)266         public override void WriteDocType(string name, string pubid, string sysid, string subset)
267         {
268             Debug.Assert(name != null && name.Length > 0);
269 
270             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
271 
272             RawText("<!DOCTYPE ");
273             RawText(name);
274             if (pubid != null)
275             {
276                 RawText(" PUBLIC \"");
277                 RawText(pubid);
278                 RawText("\" \"");
279                 if (sysid != null)
280                 {
281                     RawText(sysid);
282                 }
283                 bufChars[bufPos++] = (char)'"';
284             }
285             else if (sysid != null)
286             {
287                 RawText(" SYSTEM \"");
288                 RawText(sysid);
289                 bufChars[bufPos++] = (char)'"';
290             }
291             else
292             {
293                 bufChars[bufPos++] = (char)' ';
294             }
295 
296             if (subset != null)
297             {
298                 bufChars[bufPos++] = (char)'[';
299                 RawText(subset);
300                 bufChars[bufPos++] = (char)']';
301             }
302 
303             bufChars[this.bufPos++] = (char)'>';
304         }
305 
306         // Serialize the beginning of an element start tag: "<prefix:localName"
WriteStartElement(string prefix, string localName, string ns)307         public override void WriteStartElement(string prefix, string localName, string ns)
308         {
309             Debug.Assert(localName != null && localName.Length > 0);
310             Debug.Assert(prefix != null);
311 
312             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
313 
314             bufChars[bufPos++] = (char)'<';
315             if (prefix != null && prefix.Length != 0)
316             {
317                 RawText(prefix);
318                 bufChars[this.bufPos++] = (char)':';
319             }
320 
321             RawText(localName);
322 
323             attrEndPos = bufPos;
324         }
325 
326         // Serialize the end of an element start tag in preparation for content serialization: ">"
StartElementContent()327         internal override void StartElementContent()
328         {
329             bufChars[bufPos++] = (char)'>';
330 
331             // StartElementContent is always called; therefore, in order to allow shortcut syntax, we save the
332             // position of the '>' character.  If WriteEndElement is called and no other characters have been
333             // output, then the '>' character can be overwritten with the shortcut syntax " />".
334             contentPos = bufPos;
335         }
336 
337         // Serialize an element end tag: "</prefix:localName>", if content was output.  Otherwise, serialize
338         // the shortcut syntax: " />".
WriteEndElement(string prefix, string localName, string ns)339         internal override void WriteEndElement(string prefix, string localName, string ns)
340         {
341             Debug.Assert(localName != null && localName.Length > 0);
342             Debug.Assert(prefix != null);
343 
344             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
345 
346             if (contentPos != bufPos)
347             {
348                 // Content has been output, so can't use shortcut syntax
349                 bufChars[bufPos++] = (char)'<';
350                 bufChars[bufPos++] = (char)'/';
351 
352                 if (prefix != null && prefix.Length != 0)
353                 {
354                     RawText(prefix);
355                     bufChars[bufPos++] = (char)':';
356                 }
357                 RawText(localName);
358                 bufChars[bufPos++] = (char)'>';
359             }
360             else
361             {
362                 // Use shortcut syntax; overwrite the already output '>' character
363                 bufPos--;
364                 bufChars[bufPos++] = (char)' ';
365                 bufChars[bufPos++] = (char)'/';
366                 bufChars[bufPos++] = (char)'>';
367             }
368         }
369 
370         // Serialize a full element end tag: "</prefix:localName>"
WriteFullEndElement(string prefix, string localName, string ns)371         internal override void WriteFullEndElement(string prefix, string localName, string ns)
372         {
373             Debug.Assert(localName != null && localName.Length > 0);
374             Debug.Assert(prefix != null);
375 
376             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
377 
378             bufChars[bufPos++] = (char)'<';
379             bufChars[bufPos++] = (char)'/';
380 
381             if (prefix != null && prefix.Length != 0)
382             {
383                 RawText(prefix);
384                 bufChars[bufPos++] = (char)':';
385             }
386             RawText(localName);
387             bufChars[bufPos++] = (char)'>';
388         }
389 
390         // Serialize an attribute tag using double quotes around the attribute value: 'prefix:localName="'
WriteStartAttribute(string prefix, string localName, string ns)391         public override void WriteStartAttribute(string prefix, string localName, string ns)
392         {
393             Debug.Assert(localName != null && localName.Length > 0);
394             Debug.Assert(prefix != null);
395 
396             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
397 
398             if (attrEndPos == bufPos)
399             {
400                 bufChars[bufPos++] = (char)' ';
401             }
402 
403             if (prefix != null && prefix.Length > 0)
404             {
405                 RawText(prefix);
406                 bufChars[bufPos++] = (char)':';
407             }
408             RawText(localName);
409             bufChars[bufPos++] = (char)'=';
410             bufChars[bufPos++] = (char)'"';
411 
412             inAttributeValue = true;
413         }
414 
415         // Serialize the end of an attribute value using double quotes: '"'
WriteEndAttribute()416         public override void WriteEndAttribute()
417         {
418             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
419             bufChars[bufPos++] = (char)'"';
420             inAttributeValue = false;
421             attrEndPos = bufPos;
422         }
423 
WriteNamespaceDeclaration(string prefix, string namespaceName)424         internal override void WriteNamespaceDeclaration(string prefix, string namespaceName)
425         {
426             Debug.Assert(prefix != null && namespaceName != null);
427 
428             this.WriteStartNamespaceDeclaration(prefix);
429             this.WriteString(namespaceName);
430             this.WriteEndNamespaceDeclaration();
431         }
432 
433         internal override bool SupportsNamespaceDeclarationInChunks
434         {
435             get
436             {
437                 return true;
438             }
439         }
440 
WriteStartNamespaceDeclaration(string prefix)441         internal override void WriteStartNamespaceDeclaration(string prefix)
442         {
443             Debug.Assert(prefix != null);
444 
445             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
446 
447             if (prefix.Length == 0)
448             {
449                 RawText(" xmlns=\"");
450             }
451             else
452             {
453                 RawText(" xmlns:");
454                 RawText(prefix);
455                 bufChars[bufPos++] = (char)'=';
456                 bufChars[bufPos++] = (char)'"';
457             }
458 
459             inAttributeValue = true;
460             if (trackTextContent && inTextContent != true) { ChangeTextContentMark(true); }
461         }
462 
WriteEndNamespaceDeclaration()463         internal override void WriteEndNamespaceDeclaration()
464         {
465             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
466             inAttributeValue = false;
467 
468             bufChars[bufPos++] = (char)'"';
469             attrEndPos = bufPos;
470         }
471 
472         // Serialize a CData section.  If the "]]>" pattern is found within
473         // the text, replace it with "]]><![CDATA[>".
WriteCData(string text)474         public override void WriteCData(string text)
475         {
476             Debug.Assert(text != null);
477 
478             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
479 
480             if (mergeCDataSections && bufPos == cdataPos)
481             {
482                 // Merge adjacent cdata sections - overwrite the "]]>" characters
483                 Debug.Assert(bufPos >= 4);
484                 bufPos -= 3;
485             }
486             else
487             {
488                 // Start a new cdata section
489                 bufChars[bufPos++] = (char)'<';
490                 bufChars[bufPos++] = (char)'!';
491                 bufChars[bufPos++] = (char)'[';
492                 bufChars[bufPos++] = (char)'C';
493                 bufChars[bufPos++] = (char)'D';
494                 bufChars[bufPos++] = (char)'A';
495                 bufChars[bufPos++] = (char)'T';
496                 bufChars[bufPos++] = (char)'A';
497                 bufChars[bufPos++] = (char)'[';
498             }
499 
500             WriteCDataSection(text);
501 
502             bufChars[bufPos++] = (char)']';
503             bufChars[bufPos++] = (char)']';
504             bufChars[bufPos++] = (char)'>';
505 
506             textPos = bufPos;
507             cdataPos = bufPos;
508         }
509 
510         // Serialize a comment.
WriteComment(string text)511         public override void WriteComment(string text)
512         {
513             Debug.Assert(text != null);
514 
515             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
516 
517             bufChars[bufPos++] = (char)'<';
518             bufChars[bufPos++] = (char)'!';
519             bufChars[bufPos++] = (char)'-';
520             bufChars[bufPos++] = (char)'-';
521 
522             WriteCommentOrPi(text, '-');
523 
524             bufChars[bufPos++] = (char)'-';
525             bufChars[bufPos++] = (char)'-';
526             bufChars[bufPos++] = (char)'>';
527         }
528 
529         // Serialize a processing instruction.
WriteProcessingInstruction(string name, string text)530         public override void WriteProcessingInstruction(string name, string text)
531         {
532             Debug.Assert(name != null && name.Length > 0);
533             Debug.Assert(text != null);
534 
535             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
536 
537             bufChars[bufPos++] = (char)'<';
538             bufChars[bufPos++] = (char)'?';
539             RawText(name);
540 
541             if (text.Length > 0)
542             {
543                 bufChars[bufPos++] = (char)' ';
544                 WriteCommentOrPi(text, '?');
545             }
546 
547             bufChars[bufPos++] = (char)'?';
548             bufChars[bufPos++] = (char)'>';
549         }
550 
551         // Serialize an entity reference.
WriteEntityRef(string name)552         public override void WriteEntityRef(string name)
553         {
554             Debug.Assert(name != null && name.Length > 0);
555 
556             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
557 
558             bufChars[bufPos++] = (char)'&';
559             RawText(name);
560             bufChars[bufPos++] = (char)';';
561 
562             if (bufPos > bufLen)
563             {
564                 FlushBuffer();
565             }
566 
567             textPos = bufPos;
568         }
569 
570         // Serialize a character entity reference.
WriteCharEntity(char ch)571         public override void WriteCharEntity(char ch)
572         {
573             string strVal = ((int)ch).ToString("X", NumberFormatInfo.InvariantInfo);
574 
575             if (checkCharacters && !xmlCharType.IsCharData(ch))
576             {
577                 // we just have a single char, not a surrogate, therefore we have to pass in '\0' for the second char
578                 throw XmlConvert.CreateInvalidCharException(ch, '\0');
579             }
580 
581             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
582 
583             bufChars[bufPos++] = (char)'&';
584             bufChars[bufPos++] = (char)'#';
585             bufChars[bufPos++] = (char)'x';
586             RawText(strVal);
587             bufChars[bufPos++] = (char)';';
588 
589             if (bufPos > bufLen)
590             {
591                 FlushBuffer();
592             }
593 
594             textPos = bufPos;
595         }
596 
597         // Serialize a whitespace node.
598 
WriteWhitespace(string ws)599         public override unsafe void WriteWhitespace(string ws)
600         {
601             Debug.Assert(ws != null);
602             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
603 
604             fixed (char* pSrc = ws)
605             {
606                 char* pSrcEnd = pSrc + ws.Length;
607                 if (inAttributeValue)
608                 {
609                     WriteAttributeTextBlock(pSrc, pSrcEnd);
610                 }
611                 else
612                 {
613                     WriteElementTextBlock(pSrc, pSrcEnd);
614                 }
615             }
616         }
617 
618         // Serialize either attribute or element text using XML rules.
619 
WriteString(string text)620         public override unsafe void WriteString(string text)
621         {
622             Debug.Assert(text != null);
623             if (trackTextContent && inTextContent != true) { ChangeTextContentMark(true); }
624 
625             fixed (char* pSrc = text)
626             {
627                 char* pSrcEnd = pSrc + text.Length;
628                 if (inAttributeValue)
629                 {
630                     WriteAttributeTextBlock(pSrc, pSrcEnd);
631                 }
632                 else
633                 {
634                     WriteElementTextBlock(pSrc, pSrcEnd);
635                 }
636             }
637         }
638 
639         // Serialize surrogate character entity.
WriteSurrogateCharEntity(char lowChar, char highChar)640         public override void WriteSurrogateCharEntity(char lowChar, char highChar)
641         {
642             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
643             int surrogateChar = XmlCharType.CombineSurrogateChar(lowChar, highChar);
644 
645             bufChars[bufPos++] = (char)'&';
646             bufChars[bufPos++] = (char)'#';
647             bufChars[bufPos++] = (char)'x';
648             RawText(surrogateChar.ToString("X", NumberFormatInfo.InvariantInfo));
649             bufChars[bufPos++] = (char)';';
650             textPos = bufPos;
651         }
652 
653         // Serialize either attribute or element text using XML rules.
654         // Arguments are validated in the XmlWellformedWriter layer.
655 
WriteChars(char[] buffer, int index, int count)656         public override unsafe void WriteChars(char[] buffer, int index, int count)
657         {
658             Debug.Assert(buffer != null);
659             Debug.Assert(index >= 0);
660             Debug.Assert(count >= 0 && index + count <= buffer.Length);
661 
662             if (trackTextContent && inTextContent != true) { ChangeTextContentMark(true); }
663 
664             fixed (char* pSrcBegin = &buffer[index])
665             {
666                 if (inAttributeValue)
667                 {
668                     WriteAttributeTextBlock(pSrcBegin, pSrcBegin + count);
669                 }
670                 else
671                 {
672                     WriteElementTextBlock(pSrcBegin, pSrcBegin + count);
673                 }
674             }
675         }
676 
677         // Serialize raw data.
678         // Arguments are validated in the XmlWellformedWriter layer
679 
WriteRaw(char[] buffer, int index, int count)680         public override unsafe void WriteRaw(char[] buffer, int index, int count)
681         {
682             Debug.Assert(buffer != null);
683             Debug.Assert(index >= 0);
684             Debug.Assert(count >= 0 && index + count <= buffer.Length);
685 
686             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
687 
688             fixed (char* pSrcBegin = &buffer[index])
689             {
690                 WriteRawWithCharChecking(pSrcBegin, pSrcBegin + count);
691             }
692 
693             textPos = bufPos;
694         }
695 
696         // Serialize raw data.
697 
WriteRaw(string data)698         public override unsafe void WriteRaw(string data)
699         {
700             Debug.Assert(data != null);
701 
702             if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
703 
704             fixed (char* pSrcBegin = data)
705             {
706                 WriteRawWithCharChecking(pSrcBegin, pSrcBegin + data.Length);
707             }
708 
709             textPos = bufPos;
710         }
711 
712         // Flush all bytes in the buffer to output and close the output stream or writer.
Close()713         public override void Close()
714         {
715             try
716             {
717                 FlushBuffer();
718                 FlushEncoder();
719             }
720             finally
721             {
722                 // Future calls to Close or Flush shouldn't write to Stream or Writer
723                 writeToNull = true;
724 
725                 if (stream != null)
726                 {
727                     try
728                     {
729                         stream.Flush();
730                     }
731                     finally
732                     {
733                         try
734                         {
735                             if (closeOutput)
736                             {
737                                 stream.Dispose();
738                             }
739                         }
740                         finally
741                         {
742                             stream = null;
743                         }
744                     }
745                 }
746 
747                 else if (writer != null)
748                 {
749                     try
750                     {
751                         writer.Flush();
752                     }
753                     finally
754                     {
755                         try
756                         {
757                             if (closeOutput)
758                             {
759                                 writer.Dispose();
760                             }
761                         }
762                         finally
763                         {
764                             writer = null;
765                         }
766                     }
767                 }
768             }
769         }
770 
771         // Flush all characters in the buffer to output and call Flush() on the output object.
Flush()772         public override void Flush()
773         {
774             FlushBuffer();
775             FlushEncoder();
776 
777             if (stream != null)
778             {
779                 stream.Flush();
780             }
781             else if (writer != null)
782             {
783                 writer.Flush();
784             }
785         }
786 
787         //
788         // Implementation methods
789         //
790         // Flush all characters in the buffer to output.  Do not flush the output object.
FlushBuffer()791         protected virtual void FlushBuffer()
792         {
793             try
794             {
795                 // Output all characters (except for previous characters stored at beginning of buffer)
796                 if (!writeToNull)
797                 {
798                     Debug.Assert(stream != null || writer != null);
799 
800                     if (stream != null)
801                     {
802                         if (trackTextContent)
803                         {
804                             _charEntityFallback.Reset(_textContentMarks, _lastMarkPos);
805                             // reset text content tracking
806 
807                             if ((_lastMarkPos & 1) != 0)
808                             {
809                                 // If the previous buffer ended inside a text content we need to preserve that info
810                                 //   which means the next index to which we write has to be even
811                                 _textContentMarks[1] = 1;
812                                 _lastMarkPos = 1;
813                             }
814                             else
815                             {
816                                 _lastMarkPos = 0;
817                             }
818                             Debug.Assert(_textContentMarks[0] == 1);
819                         }
820                         EncodeChars(1, bufPos, true);
821                     }
822                     else
823                     {
824                         // Write text to TextWriter
825                         writer.Write(bufChars, 1, bufPos - 1);
826                     }
827                 }
828             }
829             catch
830             {
831                 // Future calls to flush (i.e. when Close() is called) don't attempt to write to stream
832                 writeToNull = true;
833                 throw;
834             }
835             finally
836             {
837                 // Move last buffer character to the beginning of the buffer (so that previous character can always be determined)
838                 bufChars[0] = bufChars[bufPos - 1];
839 
840                 // Reset buffer position
841                 textPos = (textPos == bufPos) ? 1 : 0;
842                 attrEndPos = (attrEndPos == bufPos) ? 1 : 0;
843                 contentPos = 0;    // Needs to be zero, since overwriting '>' character is no longer possible
844                 cdataPos = 0;      // Needs to be zero, since overwriting ']]>' characters is no longer possible
845                 bufPos = 1;        // Buffer position starts at 1, because we need to be able to safely step back -1 in case we need to
846                                    // close an empty element or in CDATA section detection of double ]; _BUFFER[0] will always be 0
847             }
848         }
849 
EncodeChars(int startOffset, int endOffset, bool writeAllToStream)850         private void EncodeChars(int startOffset, int endOffset, bool writeAllToStream)
851         {
852             // Write encoded text to stream
853             int chEnc;
854             int bEnc;
855             bool completed;
856             while (startOffset < endOffset)
857             {
858                 if (_charEntityFallback != null)
859                 {
860                     _charEntityFallback.StartOffset = startOffset;
861                 }
862                 encoder.Convert(bufChars, startOffset, endOffset - startOffset, bufBytes, bufBytesUsed, bufBytes.Length - bufBytesUsed, false, out chEnc, out bEnc, out completed);
863                 startOffset += chEnc;
864                 bufBytesUsed += bEnc;
865                 if (bufBytesUsed >= (bufBytes.Length - 16))
866                 {
867                     stream.Write(bufBytes, 0, bufBytesUsed);
868                     bufBytesUsed = 0;
869                 }
870             }
871             if (writeAllToStream && bufBytesUsed > 0)
872             {
873                 stream.Write(bufBytes, 0, bufBytesUsed);
874                 bufBytesUsed = 0;
875             }
876         }
877 
FlushEncoder()878         private void FlushEncoder()
879         {
880             Debug.Assert(bufPos == 1);
881             if (stream != null)
882             {
883                 int chEnc;
884                 int bEnc;
885                 bool completed;
886                 // decode no chars, just flush
887                 encoder.Convert(bufChars, 1, 0, bufBytes, 0, bufBytes.Length, true, out chEnc, out bEnc, out completed);
888                 if (bEnc != 0)
889                 {
890                     stream.Write(bufBytes, 0, bEnc);
891                 }
892             }
893         }
894 
895         // Serialize text that is part of an attribute value.  The '&', '<', '>', and '"' characters
896         // are entitized.
897 
WriteAttributeTextBlock(char* pSrc, char* pSrcEnd)898         protected unsafe void WriteAttributeTextBlock(char* pSrc, char* pSrcEnd)
899         {
900             fixed (char* pDstBegin = bufChars)
901             {
902                 char* pDst = pDstBegin + this.bufPos;
903 
904                 int ch = 0;
905                 for (;;)
906                 {
907                     char* pDstEnd = pDst + (pSrcEnd - pSrc);
908                     if (pDstEnd > pDstBegin + bufLen)
909                     {
910                         pDstEnd = pDstBegin + bufLen;
911                     }
912 
913                     while (pDst < pDstEnd && xmlCharType.IsAttributeValueChar((char)(ch = *pSrc)))
914                     {
915                         *pDst = (char)ch;
916                         pDst++;
917                         pSrc++;
918                     }
919                     Debug.Assert(pSrc <= pSrcEnd);
920 
921                     // end of value
922                     if (pSrc >= pSrcEnd)
923                     {
924                         break;
925                     }
926 
927                     // end of buffer
928                     if (pDst >= pDstEnd)
929                     {
930                         bufPos = (int)(pDst - pDstBegin);
931                         FlushBuffer();
932                         pDst = pDstBegin + 1;
933                         continue;
934                     }
935 
936                     // some character needs to be escaped
937                     switch (ch)
938                     {
939                         case '&':
940                             pDst = AmpEntity(pDst);
941                             break;
942                         case '<':
943                             pDst = LtEntity(pDst);
944                             break;
945                         case '>':
946                             pDst = GtEntity(pDst);
947                             break;
948                         case '"':
949                             pDst = QuoteEntity(pDst);
950                             break;
951                         case '\'':
952                             *pDst = (char)ch;
953                             pDst++;
954                             break;
955                         case (char)0x9:
956                             if (newLineHandling == NewLineHandling.None)
957                             {
958                                 *pDst = (char)ch;
959                                 pDst++;
960                             }
961                             else
962                             {
963                                 // escape tab in attributes
964                                 pDst = TabEntity(pDst);
965                             }
966                             break;
967                         case (char)0xD:
968                             if (newLineHandling == NewLineHandling.None)
969                             {
970                                 *pDst = (char)ch;
971                                 pDst++;
972                             }
973                             else
974                             {
975                                 // escape new lines in attributes
976                                 pDst = CarriageReturnEntity(pDst);
977                             }
978                             break;
979                         case (char)0xA:
980                             if (newLineHandling == NewLineHandling.None)
981                             {
982                                 *pDst = (char)ch;
983                                 pDst++;
984                             }
985                             else
986                             {
987                                 // escape new lines in attributes
988                                 pDst = LineFeedEntity(pDst);
989                             }
990                             break;
991                         default:
992                             if (XmlCharType.IsSurrogate(ch)) { pDst = EncodeSurrogate(pSrc, pSrcEnd, pDst); pSrc += 2; } else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, true); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
993                             continue;
994                     }
995                     pSrc++;
996                 }
997                 bufPos = (int)(pDst - pDstBegin);
998             }
999         }
1000 
1001         // Serialize text that is part of element content.  The '&', '<', and '>' characters
1002         // are entitized.
1003 
WriteElementTextBlock(char* pSrc, char* pSrcEnd)1004         protected unsafe void WriteElementTextBlock(char* pSrc, char* pSrcEnd)
1005         {
1006             fixed (char* pDstBegin = bufChars)
1007             {
1008                 char* pDst = pDstBegin + this.bufPos;
1009 
1010                 int ch = 0;
1011                 for (;;)
1012                 {
1013                     char* pDstEnd = pDst + (pSrcEnd - pSrc);
1014                     if (pDstEnd > pDstBegin + bufLen)
1015                     {
1016                         pDstEnd = pDstBegin + bufLen;
1017                     }
1018 
1019                     while (pDst < pDstEnd && xmlCharType.IsAttributeValueChar((char)(ch = *pSrc)))
1020                     {
1021                         *pDst = (char)ch;
1022                         pDst++;
1023                         pSrc++;
1024                     }
1025                     Debug.Assert(pSrc <= pSrcEnd);
1026 
1027                     // end of value
1028                     if (pSrc >= pSrcEnd)
1029                     {
1030                         break;
1031                     }
1032 
1033                     // end of buffer
1034                     if (pDst >= pDstEnd)
1035                     {
1036                         bufPos = (int)(pDst - pDstBegin);
1037                         FlushBuffer();
1038                         pDst = pDstBegin + 1;
1039                         continue;
1040                     }
1041 
1042                     // some character needs to be escaped
1043                     switch (ch)
1044                     {
1045                         case '&':
1046                             pDst = AmpEntity(pDst);
1047                             break;
1048                         case '<':
1049                             pDst = LtEntity(pDst);
1050                             break;
1051                         case '>':
1052                             pDst = GtEntity(pDst);
1053                             break;
1054                         case '"':
1055                         case '\'':
1056                         case (char)0x9:
1057                             *pDst = (char)ch;
1058                             pDst++;
1059                             break;
1060                         case (char)0xA:
1061                             if (newLineHandling == NewLineHandling.Replace)
1062                             {
1063                                 pDst = WriteNewLine(pDst);
1064                             }
1065                             else
1066                             {
1067                                 *pDst = (char)ch;
1068                                 pDst++;
1069                             }
1070                             break;
1071                         case (char)0xD:
1072                             switch (newLineHandling)
1073                             {
1074                                 case NewLineHandling.Replace:
1075                                     // Replace "\r\n", or "\r" with NewLineChars
1076                                     if (pSrc[1] == '\n')
1077                                     {
1078                                         pSrc++;
1079                                     }
1080 
1081                                     pDst = WriteNewLine(pDst);
1082                                     break;
1083 
1084                                 case NewLineHandling.Entitize:
1085                                     // Entitize 0xD
1086                                     pDst = CarriageReturnEntity(pDst);
1087                                     break;
1088                                 case NewLineHandling.None:
1089                                     *pDst = (char)ch;
1090                                     pDst++;
1091                                     break;
1092                             }
1093                             break;
1094                         default:
1095                             if (XmlCharType.IsSurrogate(ch)) { pDst = EncodeSurrogate(pSrc, pSrcEnd, pDst); pSrc += 2; } else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, true); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1096                             continue;
1097                     }
1098                     pSrc++;
1099                 }
1100                 bufPos = (int)(pDst - pDstBegin);
1101                 textPos = bufPos;
1102                 contentPos = 0;
1103             }
1104         }
1105 
RawText(string s)1106         protected unsafe void RawText(string s)
1107         {
1108             Debug.Assert(s != null);
1109 
1110             fixed (char* pSrcBegin = s)
1111             {
1112                 RawText(pSrcBegin, pSrcBegin + s.Length);
1113             }
1114         }
1115 
RawText(char* pSrcBegin, char* pSrcEnd)1116         protected unsafe void RawText(char* pSrcBegin, char* pSrcEnd)
1117         {
1118             fixed (char* pDstBegin = bufChars)
1119             {
1120                 char* pDst = pDstBegin + this.bufPos;
1121                 char* pSrc = pSrcBegin;
1122 
1123                 int ch = 0;
1124                 for (;;)
1125                 {
1126                     char* pDstEnd = pDst + (pSrcEnd - pSrc);
1127                     if (pDstEnd > pDstBegin + this.bufLen)
1128                     {
1129                         pDstEnd = pDstBegin + this.bufLen;
1130                     }
1131 
1132                     while (pDst < pDstEnd && ((ch = *pSrc) < XmlCharType.SurHighStart))
1133                     {
1134                         pSrc++;
1135                         *pDst = (char)ch;
1136                         pDst++;
1137                     }
1138                     Debug.Assert(pSrc <= pSrcEnd);
1139 
1140                     // end of value
1141                     if (pSrc >= pSrcEnd)
1142                     {
1143                         break;
1144                     }
1145 
1146                     // end of buffer
1147                     if (pDst >= pDstEnd)
1148                     {
1149                         bufPos = (int)(pDst - pDstBegin);
1150                         FlushBuffer();
1151                         pDst = pDstBegin + 1;
1152                         continue;
1153                     }
1154 
1155                     if (XmlCharType.IsSurrogate(ch)) { pDst = EncodeSurrogate(pSrc, pSrcEnd, pDst); pSrc += 2; } else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1156                 }
1157 
1158                 bufPos = (int)(pDst - pDstBegin);
1159             }
1160         }
1161 
WriteRawWithCharChecking(char* pSrcBegin, char* pSrcEnd)1162         protected unsafe void WriteRawWithCharChecking(char* pSrcBegin, char* pSrcEnd)
1163         {
1164             fixed (char* pDstBegin = bufChars)
1165             {
1166                 char* pSrc = pSrcBegin;
1167                 char* pDst = pDstBegin + bufPos;
1168 
1169                 int ch = 0;
1170                 for (;;)
1171                 {
1172                     char* pDstEnd = pDst + (pSrcEnd - pSrc);
1173                     if (pDstEnd > pDstBegin + bufLen)
1174                     {
1175                         pDstEnd = pDstBegin + bufLen;
1176                     }
1177 
1178                     while (pDst < pDstEnd && xmlCharType.IsTextChar((char)(ch = *pSrc)))
1179                     {
1180                         *pDst = (char)ch;
1181                         pDst++;
1182                         pSrc++;
1183                     }
1184 
1185                     Debug.Assert(pSrc <= pSrcEnd);
1186 
1187                     // end of value
1188                     if (pSrc >= pSrcEnd)
1189                     {
1190                         break;
1191                     }
1192 
1193                     // end of buffer
1194                     if (pDst >= pDstEnd)
1195                     {
1196                         bufPos = (int)(pDst - pDstBegin);
1197                         FlushBuffer();
1198                         pDst = pDstBegin + 1;
1199                         continue;
1200                     }
1201 
1202                     // handle special characters
1203                     switch (ch)
1204                     {
1205                         case ']':
1206                         case '<':
1207                         case '&':
1208                         case (char)0x9:
1209                             *pDst = (char)ch;
1210                             pDst++;
1211                             break;
1212                         case (char)0xD:
1213                             if (newLineHandling == NewLineHandling.Replace)
1214                             {
1215                                 // Normalize "\r\n", or "\r" to NewLineChars
1216                                 if (pSrc[1] == '\n')
1217                                 {
1218                                     pSrc++;
1219                                 }
1220 
1221                                 pDst = WriteNewLine(pDst);
1222                             }
1223                             else
1224                             {
1225                                 *pDst = (char)ch;
1226                                 pDst++;
1227                             }
1228                             break;
1229                         case (char)0xA:
1230                             if (newLineHandling == NewLineHandling.Replace)
1231                             {
1232                                 pDst = WriteNewLine(pDst);
1233                             }
1234                             else
1235                             {
1236                                 *pDst = (char)ch;
1237                                 pDst++;
1238                             }
1239                             break;
1240                         default:
1241                             if (XmlCharType.IsSurrogate(ch)) { pDst = EncodeSurrogate(pSrc, pSrcEnd, pDst); pSrc += 2; } else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1242                             continue;
1243                     }
1244                     pSrc++;
1245                 }
1246                 bufPos = (int)(pDst - pDstBegin);
1247             }
1248         }
1249 
WriteCommentOrPi(string text, int stopChar)1250         protected unsafe void WriteCommentOrPi(string text, int stopChar)
1251         {
1252             if (text.Length == 0)
1253             {
1254                 if (bufPos >= bufLen)
1255                 {
1256                     FlushBuffer();
1257                 }
1258                 return;
1259             }
1260             // write text
1261             fixed (char* pSrcBegin = text)
1262 
1263             fixed (char* pDstBegin = bufChars)
1264             {
1265                 char* pSrc = pSrcBegin;
1266 
1267                 char* pSrcEnd = pSrcBegin + text.Length;
1268 
1269                 char* pDst = pDstBegin + bufPos;
1270 
1271                 int ch = 0;
1272                 for (;;)
1273                 {
1274                     char* pDstEnd = pDst + (pSrcEnd - pSrc);
1275                     if (pDstEnd > pDstBegin + bufLen)
1276                     {
1277                         pDstEnd = pDstBegin + bufLen;
1278                     }
1279 
1280                     while (pDst < pDstEnd && (xmlCharType.IsTextChar((char)(ch = *pSrc)) && ch != stopChar))
1281                     {
1282                         *pDst = (char)ch;
1283                         pDst++;
1284                         pSrc++;
1285                     }
1286 
1287                     Debug.Assert(pSrc <= pSrcEnd);
1288 
1289                     // end of value
1290                     if (pSrc >= pSrcEnd)
1291                     {
1292                         break;
1293                     }
1294 
1295                     // end of buffer
1296                     if (pDst >= pDstEnd)
1297                     {
1298                         bufPos = (int)(pDst - pDstBegin);
1299                         FlushBuffer();
1300                         pDst = pDstBegin + 1;
1301                         continue;
1302                     }
1303 
1304                     // handle special characters
1305                     switch (ch)
1306                     {
1307                         case '-':
1308                             *pDst = (char)'-';
1309                             pDst++;
1310                             if (ch == stopChar)
1311                             {
1312                                 // Insert space between adjacent dashes or before comment's end dashes
1313                                 if (pSrc + 1 == pSrcEnd || *(pSrc + 1) == '-')
1314                                 {
1315                                     *pDst = (char)' ';
1316                                     pDst++;
1317                                 }
1318                             }
1319                             break;
1320                         case '?':
1321                             *pDst = (char)'?';
1322                             pDst++;
1323                             if (ch == stopChar)
1324                             {
1325                                 // Processing instruction: insert space between adjacent '?' and '>'
1326                                 if (pSrc + 1 < pSrcEnd && *(pSrc + 1) == '>')
1327                                 {
1328                                     *pDst = (char)' ';
1329                                     pDst++;
1330                                 }
1331                             }
1332                             break;
1333                         case ']':
1334                             *pDst = (char)']';
1335                             pDst++;
1336                             break;
1337                         case (char)0xD:
1338                             if (newLineHandling == NewLineHandling.Replace)
1339                             {
1340                                 // Normalize "\r\n", or "\r" to NewLineChars
1341                                 if (pSrc[1] == '\n')
1342                                 {
1343                                     pSrc++;
1344                                 }
1345 
1346                                 pDst = WriteNewLine(pDst);
1347                             }
1348                             else
1349                             {
1350                                 *pDst = (char)ch;
1351                                 pDst++;
1352                             }
1353                             break;
1354                         case (char)0xA:
1355                             if (newLineHandling == NewLineHandling.Replace)
1356                             {
1357                                 pDst = WriteNewLine(pDst);
1358                             }
1359                             else
1360                             {
1361                                 *pDst = (char)ch;
1362                                 pDst++;
1363                             }
1364                             break;
1365                         case '<':
1366                         case '&':
1367                         case (char)0x9:
1368                             *pDst = (char)ch;
1369                             pDst++;
1370                             break;
1371                         default:
1372                             if (XmlCharType.IsSurrogate(ch)) { pDst = EncodeSurrogate(pSrc, pSrcEnd, pDst); pSrc += 2; } else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1373                             continue;
1374                     }
1375                     pSrc++;
1376                 }
1377                 bufPos = (int)(pDst - pDstBegin);
1378             }
1379         }
1380 
WriteCDataSection(string text)1381         protected unsafe void WriteCDataSection(string text)
1382         {
1383             if (text.Length == 0)
1384             {
1385                 if (bufPos >= bufLen)
1386                 {
1387                     FlushBuffer();
1388                 }
1389                 return;
1390             }
1391 
1392             // write text
1393 
1394             fixed (char* pSrcBegin = text)
1395 
1396             fixed (char* pDstBegin = bufChars)
1397             {
1398                 char* pSrc = pSrcBegin;
1399 
1400                 char* pSrcEnd = pSrcBegin + text.Length;
1401 
1402                 char* pDst = pDstBegin + bufPos;
1403 
1404                 int ch = 0;
1405                 for (;;)
1406                 {
1407                     char* pDstEnd = pDst + (pSrcEnd - pSrc);
1408                     if (pDstEnd > pDstBegin + bufLen)
1409                     {
1410                         pDstEnd = pDstBegin + bufLen;
1411                     }
1412 
1413                     while (pDst < pDstEnd && (xmlCharType.IsAttributeValueChar((char)(ch = *pSrc)) && ch != ']'))
1414                     {
1415                         *pDst = (char)ch;
1416                         pDst++;
1417                         pSrc++;
1418                     }
1419 
1420                     Debug.Assert(pSrc <= pSrcEnd);
1421 
1422                     // end of value
1423                     if (pSrc >= pSrcEnd)
1424                     {
1425                         break;
1426                     }
1427 
1428                     // end of buffer
1429                     if (pDst >= pDstEnd)
1430                     {
1431                         bufPos = (int)(pDst - pDstBegin);
1432                         FlushBuffer();
1433                         pDst = pDstBegin + 1;
1434                         continue;
1435                     }
1436 
1437                     // handle special characters
1438                     switch (ch)
1439                     {
1440                         case '>':
1441                             if (hadDoubleBracket && pDst[-1] == (char)']')
1442                             {   // pDst[-1] will always correct - there is a padding character at _BUFFER[0]
1443                                 // The characters "]]>" were found within the CData text
1444                                 pDst = RawEndCData(pDst);
1445                                 pDst = RawStartCData(pDst);
1446                             }
1447                             *pDst = (char)'>';
1448                             pDst++;
1449                             break;
1450                         case ']':
1451                             if (pDst[-1] == (char)']')
1452                             {   // pDst[-1] will always correct - there is a padding character at _BUFFER[0]
1453                                 hadDoubleBracket = true;
1454                             }
1455                             else
1456                             {
1457                                 hadDoubleBracket = false;
1458                             }
1459                             *pDst = (char)']';
1460                             pDst++;
1461                             break;
1462                         case (char)0xD:
1463                             if (newLineHandling == NewLineHandling.Replace)
1464                             {
1465                                 // Normalize "\r\n", or "\r" to NewLineChars
1466                                 if (pSrc[1] == '\n')
1467                                 {
1468                                     pSrc++;
1469                                 }
1470 
1471                                 pDst = WriteNewLine(pDst);
1472                             }
1473                             else
1474                             {
1475                                 *pDst = (char)ch;
1476                                 pDst++;
1477                             }
1478                             break;
1479                         case (char)0xA:
1480                             if (newLineHandling == NewLineHandling.Replace)
1481                             {
1482                                 pDst = WriteNewLine(pDst);
1483                             }
1484                             else
1485                             {
1486                                 *pDst = (char)ch;
1487                                 pDst++;
1488                             }
1489                             break;
1490                         case '&':
1491                         case '<':
1492                         case '"':
1493                         case '\'':
1494                         case (char)0x9:
1495                             *pDst = (char)ch;
1496                             pDst++;
1497                             break;
1498                         default:
1499                             if (XmlCharType.IsSurrogate(ch)) { pDst = EncodeSurrogate(pSrc, pSrcEnd, pDst); pSrc += 2; } else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1500                             continue;
1501                     }
1502                     pSrc++;
1503                 }
1504                 bufPos = (int)(pDst - pDstBegin);
1505             }
1506         }
1507 
EncodeSurrogate(char* pSrc, char* pSrcEnd, char* pDst)1508         private static unsafe char* EncodeSurrogate(char* pSrc, char* pSrcEnd, char* pDst)
1509         {
1510             Debug.Assert(XmlCharType.IsSurrogate(*pSrc));
1511 
1512             int ch = *pSrc;
1513             if (ch <= XmlCharType.SurHighEnd)
1514             {
1515                 if (pSrc + 1 < pSrcEnd)
1516                 {
1517                     int lowChar = pSrc[1];
1518                     if (lowChar >= XmlCharType.SurLowStart &&
1519                         (LocalAppContextSwitches.DontThrowOnInvalidSurrogatePairs || lowChar <= XmlCharType.SurLowEnd))
1520                     {
1521                         pDst[0] = (char)ch;
1522                         pDst[1] = (char)lowChar;
1523                         pDst += 2;
1524 
1525                         return pDst;
1526                     }
1527                     throw XmlConvert.CreateInvalidSurrogatePairException((char)lowChar, (char)ch);
1528                 }
1529                 throw new ArgumentException(SR.Xml_InvalidSurrogateMissingLowChar);
1530             }
1531             throw XmlConvert.CreateInvalidHighSurrogateCharException((char)ch);
1532         }
1533 
InvalidXmlChar(int ch, char* pDst, bool entitize)1534         private unsafe char* InvalidXmlChar(int ch, char* pDst, bool entitize)
1535         {
1536             Debug.Assert(!xmlCharType.IsWhiteSpace((char)ch));
1537             Debug.Assert(!xmlCharType.IsAttributeValueChar((char)ch));
1538 
1539             if (checkCharacters)
1540             {
1541                 // This method will never be called on surrogates, so it is ok to pass in '\0' to the CreateInvalidCharException
1542                 throw XmlConvert.CreateInvalidCharException((char)ch, '\0');
1543             }
1544             else
1545             {
1546                 if (entitize)
1547                 {
1548                     return CharEntity(pDst, (char)ch);
1549                 }
1550                 else
1551                 {
1552                     *pDst = (char)ch;
1553                     pDst++;
1554 
1555                     return pDst;
1556                 }
1557             }
1558         }
1559 
EncodeChar(ref char* pSrc, char* pSrcEnd, ref char* pDst)1560         internal unsafe void EncodeChar(ref char* pSrc, char* pSrcEnd, ref char* pDst)
1561         {
1562             int ch = *pSrc;
1563             if (XmlCharType.IsSurrogate(ch)) { pDst = EncodeSurrogate(pSrc, pSrcEnd, pDst); pSrc += 2; } else if (ch <= 0x7F || ch >= 0xFFFE) { pDst = InvalidXmlChar(ch, pDst, false); pSrc++; } else { *pDst = (char)ch; pDst++; pSrc++; };
1564         }
1565 
ChangeTextContentMark(bool value)1566         protected void ChangeTextContentMark(bool value)
1567         {
1568             Debug.Assert(inTextContent != value);
1569             Debug.Assert(inTextContent || ((_lastMarkPos & 1) == 0));
1570             inTextContent = value;
1571             if (_lastMarkPos + 1 == _textContentMarks.Length)
1572             {
1573                 GrowTextContentMarks();
1574             }
1575             _textContentMarks[++_lastMarkPos] = this.bufPos;
1576         }
1577 
GrowTextContentMarks()1578         private void GrowTextContentMarks()
1579         {
1580             Debug.Assert(_lastMarkPos + 1 == _textContentMarks.Length);
1581             int[] newTextContentMarks = new int[_textContentMarks.Length * 2];
1582             Array.Copy(_textContentMarks, newTextContentMarks, _textContentMarks.Length);
1583             _textContentMarks = newTextContentMarks;
1584         }
1585 
1586         // Write NewLineChars to the specified buffer position and return an updated position.
1587 
WriteNewLine(char* pDst)1588         protected unsafe char* WriteNewLine(char* pDst)
1589         {
1590             fixed (char* pDstBegin = bufChars)
1591             {
1592                 bufPos = (int)(pDst - pDstBegin);
1593                 // Let RawText do the real work
1594                 RawText(newLineChars);
1595                 return pDstBegin + bufPos;
1596             }
1597         }
1598 
1599         // Following methods do not check whether pDst is beyond the bufSize because the buffer was allocated with a OVERFLOW to accommodate
1600         // for the writes of small constant-length string as below.
1601 
1602         // Entitize '<' as "&lt;".  Return an updated pointer.
1603 
LtEntity(char* pDst)1604         protected static unsafe char* LtEntity(char* pDst)
1605         {
1606             pDst[0] = (char)'&';
1607             pDst[1] = (char)'l';
1608             pDst[2] = (char)'t';
1609             pDst[3] = (char)';';
1610             return pDst + 4;
1611         }
1612 
1613         // Entitize '>' as "&gt;".  Return an updated pointer.
1614 
GtEntity(char* pDst)1615         protected static unsafe char* GtEntity(char* pDst)
1616         {
1617             pDst[0] = (char)'&';
1618             pDst[1] = (char)'g';
1619             pDst[2] = (char)'t';
1620             pDst[3] = (char)';';
1621             return pDst + 4;
1622         }
1623 
1624         // Entitize '&' as "&amp;".  Return an updated pointer.
1625 
AmpEntity(char* pDst)1626         protected static unsafe char* AmpEntity(char* pDst)
1627         {
1628             pDst[0] = (char)'&';
1629             pDst[1] = (char)'a';
1630             pDst[2] = (char)'m';
1631             pDst[3] = (char)'p';
1632             pDst[4] = (char)';';
1633             return pDst + 5;
1634         }
1635 
1636         // Entitize '"' as "&quot;".  Return an updated pointer.
1637 
QuoteEntity(char* pDst)1638         protected static unsafe char* QuoteEntity(char* pDst)
1639         {
1640             pDst[0] = (char)'&';
1641             pDst[1] = (char)'q';
1642             pDst[2] = (char)'u';
1643             pDst[3] = (char)'o';
1644             pDst[4] = (char)'t';
1645             pDst[5] = (char)';';
1646             return pDst + 6;
1647         }
1648 
1649         // Entitize '\t' as "&#x9;".  Return an updated pointer.
1650 
TabEntity(char* pDst)1651         protected static unsafe char* TabEntity(char* pDst)
1652         {
1653             pDst[0] = (char)'&';
1654             pDst[1] = (char)'#';
1655             pDst[2] = (char)'x';
1656             pDst[3] = (char)'9';
1657             pDst[4] = (char)';';
1658             return pDst + 5;
1659         }
1660 
1661         // Entitize 0xa as "&#xA;".  Return an updated pointer.
1662 
LineFeedEntity(char* pDst)1663         protected static unsafe char* LineFeedEntity(char* pDst)
1664         {
1665             pDst[0] = (char)'&';
1666             pDst[1] = (char)'#';
1667             pDst[2] = (char)'x';
1668             pDst[3] = (char)'A';
1669             pDst[4] = (char)';';
1670             return pDst + 5;
1671         }
1672 
1673         // Entitize 0xd as "&#xD;".  Return an updated pointer.
1674 
CarriageReturnEntity(char* pDst)1675         protected static unsafe char* CarriageReturnEntity(char* pDst)
1676         {
1677             pDst[0] = (char)'&';
1678             pDst[1] = (char)'#';
1679             pDst[2] = (char)'x';
1680             pDst[3] = (char)'D';
1681             pDst[4] = (char)';';
1682             return pDst + 5;
1683         }
1684 
CharEntity(char* pDst, char ch)1685         private static unsafe char* CharEntity(char* pDst, char ch)
1686         {
1687             string s = ((int)ch).ToString("X", NumberFormatInfo.InvariantInfo);
1688             pDst[0] = (char)'&';
1689             pDst[1] = (char)'#';
1690             pDst[2] = (char)'x';
1691             pDst += 3;
1692 
1693             fixed (char* pSrc = s)
1694             {
1695                 char* pS = pSrc;
1696                 while ((*pDst++ = (char)*pS++) != 0) ;
1697             }
1698 
1699             pDst[-1] = (char)';';
1700             return pDst;
1701         }
1702 
1703         // Write "<![CDATA[" to the specified buffer.  Return an updated pointer.
1704 
RawStartCData(char* pDst)1705         protected static unsafe char* RawStartCData(char* pDst)
1706         {
1707             pDst[0] = (char)'<';
1708             pDst[1] = (char)'!';
1709             pDst[2] = (char)'[';
1710             pDst[3] = (char)'C';
1711             pDst[4] = (char)'D';
1712             pDst[5] = (char)'A';
1713             pDst[6] = (char)'T';
1714             pDst[7] = (char)'A';
1715             pDst[8] = (char)'[';
1716             return pDst + 9;
1717         }
1718 
1719         // Write "]]>" to the specified buffer.  Return an updated pointer.
1720 
RawEndCData(char* pDst)1721         protected static unsafe char* RawEndCData(char* pDst)
1722         {
1723             pDst[0] = (char)']';
1724             pDst[1] = (char)']';
1725             pDst[2] = (char)'>';
1726             return pDst + 3;
1727         }
1728 
ValidateContentChars(string chars, string propertyName, bool allowOnlyWhitespace)1729         protected unsafe void ValidateContentChars(string chars, string propertyName, bool allowOnlyWhitespace)
1730         {
1731             if (allowOnlyWhitespace)
1732             {
1733                 if (!xmlCharType.IsOnlyWhitespace(chars))
1734                 {
1735                     throw new ArgumentException(SR.Format(SR.Xml_IndentCharsNotWhitespace, propertyName));
1736                 }
1737             }
1738             else
1739             {
1740                 string error = null;
1741                 for (int i = 0; i < chars.Length; i++)
1742                 {
1743                     if (!xmlCharType.IsTextChar(chars[i]))
1744                     {
1745                         switch (chars[i])
1746                         {
1747                             case '\n':
1748                             case '\r':
1749                             case '\t':
1750                                 continue;
1751                             case '<':
1752                             case '&':
1753                             case ']':
1754                                 error = SR.Format(SR.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs(chars, i));
1755                                 goto Error;
1756                             default:
1757                                 if (XmlCharType.IsHighSurrogate(chars[i]))
1758                                 {
1759                                     if (i + 1 < chars.Length)
1760                                     {
1761                                         if (XmlCharType.IsLowSurrogate(chars[i + 1]))
1762                                         {
1763                                             i++;
1764                                             continue;
1765                                         }
1766                                     }
1767                                     error = SR.Xml_InvalidSurrogateMissingLowChar;
1768                                     goto Error;
1769                                 }
1770                                 else if (XmlCharType.IsLowSurrogate(chars[i]))
1771                                 {
1772                                     error = SR.Format(SR.Xml_InvalidSurrogateHighChar, ((uint)chars[i]).ToString("X", CultureInfo.InvariantCulture));
1773                                     goto Error;
1774                                 }
1775                                 continue;
1776                         }
1777                     }
1778                 }
1779                 return;
1780 
1781             Error:
1782                 throw new ArgumentException(SR.Format(SR.Xml_InvalidCharsInIndent, new string[] { propertyName, error }));
1783             }
1784         }
1785     }
1786 
1787     // Same as base text writer class except that elements, attributes, comments, and pi's are indented.
1788     internal partial class XmlEncodedRawTextWriterIndent : XmlEncodedRawTextWriter
1789     {
1790         //
1791         // Fields
1792         //
1793         protected int indentLevel;
1794         protected bool newLineOnAttributes;
1795         protected string indentChars;
1796 
1797         protected bool mixedContent;
1798         private BitStack _mixedContentStack;
1799 
1800         protected ConformanceLevel conformanceLevel = ConformanceLevel.Auto;
1801 
1802         //
1803         // Constructors
1804         //
1805 
XmlEncodedRawTextWriterIndent(TextWriter writer, XmlWriterSettings settings)1806         public XmlEncodedRawTextWriterIndent(TextWriter writer, XmlWriterSettings settings) : base(writer, settings)
1807         {
1808             Init(settings);
1809         }
1810 
XmlEncodedRawTextWriterIndent(Stream stream, XmlWriterSettings settings)1811         public XmlEncodedRawTextWriterIndent(Stream stream, XmlWriterSettings settings) : base(stream, settings)
1812         {
1813             Init(settings);
1814         }
1815 
1816         //
1817         // XmlWriter methods
1818         //
1819         public override XmlWriterSettings Settings
1820         {
1821             get
1822             {
1823                 XmlWriterSettings settings = base.Settings;
1824 
1825                 settings.ReadOnly = false;
1826                 settings.Indent = true;
1827                 settings.IndentChars = indentChars;
1828                 settings.NewLineOnAttributes = newLineOnAttributes;
1829                 settings.ReadOnly = true;
1830 
1831                 return settings;
1832             }
1833         }
1834 
WriteDocType(string name, string pubid, string sysid, string subset)1835         public override void WriteDocType(string name, string pubid, string sysid, string subset)
1836         {
1837             // Add indentation
1838             if (!mixedContent && base.textPos != base.bufPos)
1839             {
1840                 WriteIndent();
1841             }
1842             base.WriteDocType(name, pubid, sysid, subset);
1843         }
1844 
WriteStartElement(string prefix, string localName, string ns)1845         public override void WriteStartElement(string prefix, string localName, string ns)
1846         {
1847             Debug.Assert(localName != null && localName.Length != 0 && prefix != null && ns != null);
1848 
1849             // Add indentation
1850             if (!mixedContent && base.textPos != base.bufPos)
1851             {
1852                 WriteIndent();
1853             }
1854             indentLevel++;
1855             _mixedContentStack.PushBit(mixedContent);
1856 
1857             base.WriteStartElement(prefix, localName, ns);
1858         }
1859 
StartElementContent()1860         internal override void StartElementContent()
1861         {
1862             // If this is the root element and we're writing a document
1863             //   do not inherit the mixedContent flag into the root element.
1864             //   This is to allow for whitespace nodes on root level
1865             //   without disabling indentation for the whole document.
1866             if (indentLevel == 1 && conformanceLevel == ConformanceLevel.Document)
1867             {
1868                 mixedContent = false;
1869             }
1870             else
1871             {
1872                 mixedContent = _mixedContentStack.PeekBit();
1873             }
1874             base.StartElementContent();
1875         }
1876 
OnRootElement(ConformanceLevel currentConformanceLevel)1877         internal override void OnRootElement(ConformanceLevel currentConformanceLevel)
1878         {
1879             // Just remember the current conformance level
1880             conformanceLevel = currentConformanceLevel;
1881         }
1882 
WriteEndElement(string prefix, string localName, string ns)1883         internal override void WriteEndElement(string prefix, string localName, string ns)
1884         {
1885             // Add indentation
1886             indentLevel--;
1887             if (!mixedContent && base.contentPos != base.bufPos)
1888             {
1889                 // There was content, so try to indent
1890                 if (base.textPos != base.bufPos)
1891                 {
1892                     WriteIndent();
1893                 }
1894             }
1895             mixedContent = _mixedContentStack.PopBit();
1896 
1897             base.WriteEndElement(prefix, localName, ns);
1898         }
1899 
WriteFullEndElement(string prefix, string localName, string ns)1900         internal override void WriteFullEndElement(string prefix, string localName, string ns)
1901         {
1902             // Add indentation
1903             indentLevel--;
1904             if (!mixedContent && base.contentPos != base.bufPos)
1905             {
1906                 // There was content, so try to indent
1907                 if (base.textPos != base.bufPos)
1908                 {
1909                     WriteIndent();
1910                 }
1911             }
1912             mixedContent = _mixedContentStack.PopBit();
1913 
1914             base.WriteFullEndElement(prefix, localName, ns);
1915         }
1916 
1917         // Same as base class, plus possible indentation.
WriteStartAttribute(string prefix, string localName, string ns)1918         public override void WriteStartAttribute(string prefix, string localName, string ns)
1919         {
1920             // Add indentation
1921             if (newLineOnAttributes)
1922             {
1923                 WriteIndent();
1924             }
1925 
1926             base.WriteStartAttribute(prefix, localName, ns);
1927         }
1928 
WriteCData(string text)1929         public override void WriteCData(string text)
1930         {
1931             mixedContent = true;
1932             base.WriteCData(text);
1933         }
1934 
WriteComment(string text)1935         public override void WriteComment(string text)
1936         {
1937             if (!mixedContent && base.textPos != base.bufPos)
1938             {
1939                 WriteIndent();
1940             }
1941 
1942             base.WriteComment(text);
1943         }
1944 
WriteProcessingInstruction(string target, string text)1945         public override void WriteProcessingInstruction(string target, string text)
1946         {
1947             if (!mixedContent && base.textPos != base.bufPos)
1948             {
1949                 WriteIndent();
1950             }
1951 
1952             base.WriteProcessingInstruction(target, text);
1953         }
1954 
WriteEntityRef(string name)1955         public override void WriteEntityRef(string name)
1956         {
1957             mixedContent = true;
1958             base.WriteEntityRef(name);
1959         }
1960 
WriteCharEntity(char ch)1961         public override void WriteCharEntity(char ch)
1962         {
1963             mixedContent = true;
1964             base.WriteCharEntity(ch);
1965         }
1966 
WriteSurrogateCharEntity(char lowChar, char highChar)1967         public override void WriteSurrogateCharEntity(char lowChar, char highChar)
1968         {
1969             mixedContent = true;
1970             base.WriteSurrogateCharEntity(lowChar, highChar);
1971         }
1972 
WriteWhitespace(string ws)1973         public override void WriteWhitespace(string ws)
1974         {
1975             mixedContent = true;
1976             base.WriteWhitespace(ws);
1977         }
1978 
WriteString(string text)1979         public override void WriteString(string text)
1980         {
1981             mixedContent = true;
1982             base.WriteString(text);
1983         }
1984 
WriteChars(char[] buffer, int index, int count)1985         public override void WriteChars(char[] buffer, int index, int count)
1986         {
1987             mixedContent = true;
1988             base.WriteChars(buffer, index, count);
1989         }
1990 
WriteRaw(char[] buffer, int index, int count)1991         public override void WriteRaw(char[] buffer, int index, int count)
1992         {
1993             mixedContent = true;
1994             base.WriteRaw(buffer, index, count);
1995         }
1996 
WriteRaw(string data)1997         public override void WriteRaw(string data)
1998         {
1999             mixedContent = true;
2000             base.WriteRaw(data);
2001         }
2002 
WriteBase64(byte[] buffer, int index, int count)2003         public override void WriteBase64(byte[] buffer, int index, int count)
2004         {
2005             mixedContent = true;
2006             base.WriteBase64(buffer, index, count);
2007         }
2008 
2009         //
2010         // Private methods
2011         //
Init(XmlWriterSettings settings)2012         private void Init(XmlWriterSettings settings)
2013         {
2014             indentLevel = 0;
2015             indentChars = settings.IndentChars;
2016             newLineOnAttributes = settings.NewLineOnAttributes;
2017             _mixedContentStack = new BitStack();
2018 
2019             // check indent characters that they are valid XML characters
2020             if (base.checkCharacters)
2021             {
2022                 if (newLineOnAttributes)
2023                 {
2024                     base.ValidateContentChars(indentChars, "IndentChars", true);
2025                     base.ValidateContentChars(newLineChars, "NewLineChars", true);
2026                 }
2027                 else
2028                 {
2029                     base.ValidateContentChars(indentChars, "IndentChars", false);
2030                     if (base.newLineHandling != NewLineHandling.Replace)
2031                     {
2032                         base.ValidateContentChars(newLineChars, "NewLineChars", false);
2033                     }
2034                 }
2035             }
2036         }
2037 
2038         // Add indentation to output.  Write newline and then repeat IndentChars for each indent level.
WriteIndent()2039         private void WriteIndent()
2040         {
2041             RawText(base.newLineChars);
2042             for (int i = indentLevel; i > 0; i--)
2043             {
2044                 RawText(indentChars);
2045             }
2046         }
2047     }
2048 }
2049 
2050