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