2 //------------------------------------------------------------------------------
3 // <copyright file="XmlRawTextWriterGenerator.cxx" company="Microsoft">
4 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // <owner current="true" primary="true">[....]</owner>
7 //------------------------------------------------------------------------------
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:
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
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.
19 // Note: This file was generated without #define SILVERLIGHT
25 using System.Diagnostics;
26 using System.Globalization;
28 namespace System.Xml {
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 XmlUtf8RawTextWriter : XmlRawWriter {
40 private readonly bool useAsync;
44 protected byte[] bufBytes;
47 protected Stream stream;
49 // encoding of the stream or text writer
50 protected Encoding encoding;
53 protected XmlCharType xmlCharType = XmlCharType.Instance;
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;
65 protected bool writeToNull;
66 protected bool hadDoubleBracket;
67 protected bool inAttributeValue;
70 protected NewLineHandling newLineHandling;
71 protected bool closeOutput;
72 protected bool omitXmlDeclaration;
73 protected string newLineChars;
74 protected bool checkCharacters;
76 protected XmlStandalone standalone;
78 protected XmlOutputMethod outputMethod;
81 protected bool autoXmlDeclaration;
82 protected bool mergeCDataSections;
87 private const int BUFSIZE = 2048 * 3; // Should be greater than default FileStream size (4096), otherwise the FileStream will try to cache the data
88 private const int ASYNCBUFSIZE = 64 * 1024; // Set async buffer size to 64KB
89 private const int OVERFLOW = 32; // Allow overflow in order to reduce checks when writing out constant size markup
90 private const int INIT_MARKS_COUNT = 64;
95 // Construct and initialize an instance of this class.
96 protected XmlUtf8RawTextWriter( XmlWriterSettings settings ) {
99 useAsync = settings.Async;
103 newLineHandling = settings.NewLineHandling;
104 omitXmlDeclaration = settings.OmitXmlDeclaration;
105 newLineChars = settings.NewLineChars;
106 checkCharacters = settings.CheckCharacters;
107 closeOutput = settings.CloseOutput;
110 standalone = settings.Standalone;
111 outputMethod = settings.OutputMethod;
112 mergeCDataSections = settings.MergeCDataSections;
115 if ( checkCharacters && newLineHandling == NewLineHandling.Replace ) {
116 ValidateContentChars( newLineChars, "NewLineChars", false );
120 // Construct an instance of this class that serializes to a Stream interface.
121 public XmlUtf8RawTextWriter( Stream stream, XmlWriterSettings settings ) : this( settings ) {
122 Debug.Assert( stream != null && settings != null );
124 this.stream = stream;
125 this.encoding = settings.Encoding;
127 // the buffer is allocated will OVERFLOW in order to reduce checks when writing out constant size markup
128 if (settings.Async) {
129 bufLen = ASYNCBUFSIZE;
132 bufBytes = new byte[bufLen + OVERFLOW];
134 // Output UTF-8 byte order mark if Encoding object wants it
135 if ( !stream.CanSeek || stream.Position == 0 ) {
136 byte[] bom = encoding.GetPreamble();
137 if ( bom.Length != 0 ) {
138 Buffer.BlockCopy( bom, 0, bufBytes, 1, bom.Length );
139 bufPos += bom.Length;
140 textPos += bom.Length;
145 // Write the xml declaration
146 if ( settings.AutoXmlDeclaration ) {
147 WriteXmlDeclaration( standalone );
148 autoXmlDeclaration = true;
154 // XmlWriter implementation
156 // Returns settings the writer currently applies.
157 public override XmlWriterSettings Settings {
159 XmlWriterSettings settings = new XmlWriterSettings();
161 settings.Encoding = this.encoding;
162 settings.OmitXmlDeclaration = this.omitXmlDeclaration;
163 settings.NewLineHandling = this.newLineHandling;
164 settings.NewLineChars = this.newLineChars;
165 settings.CloseOutput = this.closeOutput;
166 settings.ConformanceLevel = ConformanceLevel.Auto;
167 settings.CheckCharacters = checkCharacters;
170 settings.AutoXmlDeclaration = autoXmlDeclaration;
171 settings.Standalone = standalone;
172 settings.OutputMethod = outputMethod;
175 settings.ReadOnly = true;
181 // Write the xml declaration. This must be the first call.
182 internal override void WriteXmlDeclaration( XmlStandalone standalone ) {
183 // Output xml declaration only if user allows it and it was not already output
184 if ( !omitXmlDeclaration && !autoXmlDeclaration ) {
186 RawText( "<?xml version=\"" );
192 if ( encoding != null ) {
193 RawText( "\" encoding=\"" );
194 RawText( encoding.WebName );
198 if ( standalone != XmlStandalone.Omit ) {
199 RawText( "\" standalone=\"" );
200 RawText( standalone == XmlStandalone.Yes ? "yes" : "no" );
207 internal override void WriteXmlDeclaration( string xmldecl ) {
208 // Output xml declaration only if user allows it and it was not already output
209 if ( !omitXmlDeclaration && !autoXmlDeclaration ) {
210 WriteProcessingInstruction( "xml", xmldecl );
215 // Serialize the document type declaration.
216 public override void WriteDocType( string name, string pubid, string sysid, string subset ) {
217 Debug.Assert( name != null && name.Length > 0 );
219 RawText( "<!DOCTYPE ");
221 if ( pubid != null ) {
222 RawText( " PUBLIC \"" );
225 if ( sysid != null ) {
228 bufBytes[bufPos++] = (byte) '"';
230 else if ( sysid != null ) {
231 RawText( " SYSTEM \"" );
233 bufBytes[bufPos++] = (byte) '"';
236 bufBytes[bufPos++] = (byte) ' ';
239 if ( subset != null ) {
240 bufBytes[bufPos++] = (byte) '[';
242 bufBytes[bufPos++] = (byte) ']';
245 bufBytes[this.bufPos++] = (byte) '>';
248 // Serialize the beginning of an element start tag: "<prefix:localName"
249 public override void WriteStartElement( string prefix, string localName, string ns) {
250 Debug.Assert( localName != null && localName.Length > 0 );
251 Debug.Assert( prefix != null );
253 bufBytes[bufPos++] = (byte) '<';
254 if ( prefix != null && prefix.Length != 0 ) {
256 bufBytes[this.bufPos++] = (byte) ':';
259 RawText( localName );
264 // Serialize the end of an element start tag in preparation for content serialization: ">"
265 internal override void StartElementContent() {
266 bufBytes[bufPos++] = (byte) '>';
268 // StartElementContent is always called; therefore, in order to allow shortcut syntax, we save the
269 // position of the '>' character. If WriteEndElement is called and no other characters have been
270 // output, then the '>' character can be be overwritten with the shortcut syntax " />".
274 // Serialize an element end tag: "</prefix:localName>", if content was output. Otherwise, serialize
275 // the shortcut syntax: " />".
276 internal override void WriteEndElement( string prefix, string localName, string ns ) {
277 Debug.Assert( localName != null && localName.Length > 0 );
278 Debug.Assert( prefix != null );
280 if ( contentPos != bufPos ) {
281 // Content has been output, so can't use shortcut syntax
282 bufBytes[bufPos++] = (byte) '<';
283 bufBytes[bufPos++] = (byte) '/';
285 if ( prefix != null && prefix.Length != 0) {
287 bufBytes[bufPos++] = (byte) ':';
289 RawText( localName );
290 bufBytes[bufPos++] = (byte) '>';
293 // Use shortcut syntax; overwrite the already output '>' character
295 bufBytes[bufPos++] = (byte) ' ';
296 bufBytes[bufPos++] = (byte) '/';
297 bufBytes[bufPos++] = (byte) '>';
301 // Serialize a full element end tag: "</prefix:localName>"
302 internal override void WriteFullEndElement( string prefix, string localName, string ns ) {
303 Debug.Assert( localName != null && localName.Length > 0 );
304 Debug.Assert( prefix != null );
306 bufBytes[bufPos++] = (byte) '<';
307 bufBytes[bufPos++] = (byte) '/';
309 if ( prefix != null && prefix.Length != 0) {
311 bufBytes[bufPos++] = (byte) ':';
313 RawText( localName );
314 bufBytes[bufPos++] = (byte) '>';
317 // Serialize an attribute tag using double quotes around the attribute value: 'prefix:localName="'
318 public override void WriteStartAttribute( string prefix, string localName, string ns ) {
319 Debug.Assert( localName != null && localName.Length > 0 );
320 Debug.Assert( prefix != null );
322 if ( attrEndPos == bufPos ) {
323 bufBytes[bufPos++] = (byte) ' ';
326 if ( prefix != null && prefix.Length > 0 ) {
328 bufBytes[bufPos++] = (byte) ':';
330 RawText( localName );
331 bufBytes[bufPos++] = (byte) '=';
332 bufBytes[bufPos++] = (byte) '"';
334 inAttributeValue = true;
337 // Serialize the end of an attribute value using double quotes: '"'
338 public override void WriteEndAttribute() {
340 bufBytes[bufPos++] = (byte) '"';
341 inAttributeValue = false;
346 internal override void WriteNamespaceDeclaration( string prefix, string namespaceName ) {
347 Debug.Assert( prefix != null && namespaceName != null );
349 this.WriteStartNamespaceDeclaration( prefix );
350 this.WriteString( namespaceName );
351 this.WriteEndNamespaceDeclaration();
354 internal override bool SupportsNamespaceDeclarationInChunks {
360 internal override void WriteStartNamespaceDeclaration(string prefix) {
361 Debug.Assert( prefix != null );
363 // VSTFDEVDIV bug #583965: Inconsistency between Silverlight 2 and Dev10 in the way a single xmlns attribute is serialized
364 // Resolved as: Won't fix (breaking change)
366 if ( prefix.Length == 0 ) {
367 RawText( " xmlns=\"" );
370 RawText( " xmlns:" );
372 bufBytes[bufPos++] = (byte)'=';
373 bufBytes[bufPos++] = (byte)'"';
376 inAttributeValue = true;
380 internal override void WriteEndNamespaceDeclaration() {
382 inAttributeValue = false;
384 bufBytes[bufPos++] = (byte)'"';
389 // Serialize a CData section. If the "]]>" pattern is found within
390 // the text, replace it with "]]><![CDATA[>".
391 public override void WriteCData( string text ) {
392 Debug.Assert( text != null );
394 if ( mergeCDataSections && bufPos == cdataPos ) {
395 // Merge adjacent cdata sections - overwrite the "]]>" characters
396 Debug.Assert( bufPos >= 4 );
400 // Start a new cdata section
401 bufBytes[bufPos++] = (byte) '<';
402 bufBytes[bufPos++] = (byte) '!';
403 bufBytes[bufPos++] = (byte) '[';
404 bufBytes[bufPos++] = (byte) 'C';
405 bufBytes[bufPos++] = (byte) 'D';
406 bufBytes[bufPos++] = (byte) 'A';
407 bufBytes[bufPos++] = (byte) 'T';
408 bufBytes[bufPos++] = (byte) 'A';
409 bufBytes[bufPos++] = (byte) '[';
412 WriteCDataSection( text );
414 bufBytes[bufPos++] = (byte) ']';
415 bufBytes[bufPos++] = (byte) ']';
416 bufBytes[bufPos++] = (byte) '>';
422 // Serialize a comment.
423 public override void WriteComment( string text ) {
424 Debug.Assert( text != null );
426 bufBytes[bufPos++] = (byte) '<';
427 bufBytes[bufPos++] = (byte) '!';
428 bufBytes[bufPos++] = (byte) '-';
429 bufBytes[bufPos++] = (byte) '-';
431 WriteCommentOrPi( text, '-' );
433 bufBytes[bufPos++] = (byte) '-';
434 bufBytes[bufPos++] = (byte) '-';
435 bufBytes[bufPos++] = (byte) '>';
438 // Serialize a processing instruction.
439 public override void WriteProcessingInstruction( string name, string text ) {
440 Debug.Assert( name != null && name.Length > 0 );
441 Debug.Assert( text != null );
443 bufBytes[bufPos++] = (byte) '<';
444 bufBytes[bufPos++] = (byte) '?';
447 if ( text.Length > 0 ) {
448 bufBytes[bufPos++] = (byte) ' ';
449 WriteCommentOrPi( text, '?' );
452 bufBytes[bufPos++] = (byte) '?';
453 bufBytes[bufPos++] = (byte) '>';
456 // Serialize an entity reference.
457 public override void WriteEntityRef( string name ) {
458 Debug.Assert( name != null && name.Length > 0 );
460 bufBytes[bufPos++] = (byte) '&';
462 bufBytes[bufPos++] = (byte) ';';
464 if ( bufPos > bufLen ) {
471 // Serialize a character entity reference.
472 public override void WriteCharEntity( char ch ) {
473 string strVal = ((int)ch).ToString( "X", NumberFormatInfo.InvariantInfo );
475 if ( checkCharacters && !xmlCharType.IsCharData( ch ) ) {
476 // we just have a single char, not a surrogate, therefore we have to pass in '\0' for the second char
477 throw XmlConvert.CreateInvalidCharException( ch, '\0' );
480 bufBytes[bufPos++] = (byte)'&';
481 bufBytes[bufPos++] = (byte)'#';
482 bufBytes[bufPos++] = (byte)'x';
484 bufBytes[bufPos++] = (byte)';';
486 if ( bufPos > bufLen ) {
493 // Serialize a whitespace node.
495 public override unsafe void WriteWhitespace( string ws ) {
496 Debug.Assert( ws != null );
498 fixed ( char * pSrc = ws ) {
499 char * pSrcEnd = pSrc + ws.Length;
500 if ( inAttributeValue) {
501 WriteAttributeTextBlock( pSrc, pSrcEnd );
504 WriteElementTextBlock( pSrc, pSrcEnd );
510 // Serialize either attribute or element text using XML rules.
512 public override unsafe void WriteString( string text ) {
513 Debug.Assert( text != null );
515 fixed ( char * pSrc = text ) {
516 char * pSrcEnd = pSrc + text.Length;
517 if ( inAttributeValue) {
518 WriteAttributeTextBlock( pSrc, pSrcEnd );
521 WriteElementTextBlock( pSrc, pSrcEnd );
527 // Serialize surrogate character entity.
528 public override void WriteSurrogateCharEntity( char lowChar, char highChar ) {
530 int surrogateChar = XmlCharType.CombineSurrogateChar( lowChar, highChar );
532 bufBytes[bufPos++] = (byte)'&';
533 bufBytes[bufPos++] = (byte)'#';
534 bufBytes[bufPos++] = (byte)'x';
535 RawText( surrogateChar.ToString( "X", NumberFormatInfo.InvariantInfo ) );
536 bufBytes[bufPos++] = (byte)';';
540 // Serialize either attribute or element text using XML rules.
541 // Arguments are validated in the XmlWellformedWriter layer.
543 public override unsafe void WriteChars( char[] buffer, int index, int count ) {
544 Debug.Assert( buffer != null );
545 Debug.Assert( index >= 0 );
546 Debug.Assert( count >= 0 && index + count <= buffer.Length );
548 fixed ( char * pSrcBegin = &buffer[index] ) {
549 if ( inAttributeValue ) {
550 WriteAttributeTextBlock( pSrcBegin, pSrcBegin + count );
553 WriteElementTextBlock( pSrcBegin, pSrcBegin + count );
559 // Serialize raw data.
560 // Arguments are validated in the XmlWellformedWriter layer
562 public override unsafe void WriteRaw( char[] buffer, int index, int count ) {
563 Debug.Assert( buffer != null );
564 Debug.Assert( index >= 0 );
565 Debug.Assert( count >= 0 && index + count <= buffer.Length );
567 fixed ( char * pSrcBegin = &buffer[index] ) {
568 WriteRawWithCharChecking( pSrcBegin, pSrcBegin + count );
574 // Serialize raw data.
576 public override unsafe void WriteRaw( string data ) {
577 Debug.Assert( data != null );
579 fixed ( char * pSrcBegin = data ) {
580 WriteRawWithCharChecking( pSrcBegin, pSrcBegin + data.Length );
586 // Flush all bytes in the buffer to output and close the output stream or writer.
587 public override void Close() {
593 // Future calls to Close or Flush shouldn't write to Stream or Writer
596 if ( stream != null ) {
615 // Flush all characters in the buffer to output and call Flush() on the output object.
616 public override void Flush() {
620 if ( stream != null ) {
627 // Implementation methods
629 // Flush all characters in the buffer to output. Do not flush the output object.
630 protected virtual void FlushBuffer() {
632 // Output all characters (except for previous characters stored at beginning of buffer)
633 if ( !writeToNull ) {
635 Debug.Assert( stream != null);
636 stream.Write( bufBytes, 1, bufPos - 1 );
641 // Future calls to flush (i.e. when Close() is called) don't attempt to write to stream
646 // Move last buffer character to the beginning of the buffer (so that previous character can always be determined)
647 bufBytes[0] = bufBytes[bufPos - 1];
649 if ( IsSurrogateByte( bufBytes[0] ) ) {
650 // Last character was the first byte in a surrogate encoding, so move last three
651 // bytes of encoding to the beginning of the buffer.
652 bufBytes[1] = bufBytes[bufPos];
653 bufBytes[2] = bufBytes[bufPos + 1];
654 bufBytes[3] = bufBytes[bufPos + 2];
657 // Reset buffer position
658 textPos = (textPos == bufPos) ? 1 : 0;
659 attrEndPos = (attrEndPos == bufPos) ? 1 : 0;
660 contentPos = 0; // Needs to be zero, since overwriting '>' character is no longer possible
661 cdataPos = 0; // Needs to be zero, since overwriting ']]>' characters is no longer possible
662 bufPos = 1; // Buffer position starts at 1, because we need to be able to safely step back -1 in case we need to
663 // close an empty element or in CDATA section detection of double ]; _BUFFER[0] will always be 0
667 private void FlushEncoder() {
668 // intentionally empty
672 // Serialize text that is part of an attribute value. The '&', '<', '>', and '"' characters
675 protected unsafe void WriteAttributeTextBlock( char *pSrc, char *pSrcEnd ) {
677 fixed ( byte * pDstBegin = bufBytes ) {
678 byte * pDst = pDstBegin + this.bufPos;
682 byte * pDstEnd = pDst + ( pSrcEnd - pSrc );
683 if ( pDstEnd > pDstBegin + bufLen ) {
684 pDstEnd = pDstBegin + bufLen;
687 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fAttrValue ) != 0 ) && ch <= 0x7F ) ) {
693 Debug.Assert( pSrc <= pSrcEnd );
696 if ( pSrc >= pSrcEnd ) {
701 if ( pDst >= pDstEnd ) {
703 bufPos = (int)(pDst - pDstBegin);
705 pDst = pDstBegin + 1;
710 // some character needs to be escaped
713 pDst = AmpEntity( pDst );
716 pDst = LtEntity( pDst );
719 pDst = GtEntity( pDst );
722 pDst = QuoteEntity( pDst );
729 if ( newLineHandling == NewLineHandling.None ) {
734 // escape tab in attributes
735 pDst = TabEntity( pDst );
739 if ( newLineHandling == NewLineHandling.None ) {
744 // escape new lines in attributes
745 pDst = CarriageReturnEntity( pDst );
749 if ( newLineHandling == NewLineHandling.None ) {
754 // escape new lines in attributes
755 pDst = LineFeedEntity( pDst );
759 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 = EncodeMultibyteUTF8( ch, pDst ); pSrc++; };
764 bufPos = (int)(pDst - pDstBegin);
769 // Serialize text that is part of element content. The '&', '<', and '>' characters
772 protected unsafe void WriteElementTextBlock( char *pSrc, char *pSrcEnd ) {
774 fixed ( byte * pDstBegin = bufBytes ) {
775 byte * pDst = pDstBegin + this.bufPos;
779 byte * pDstEnd = pDst + ( pSrcEnd - pSrc );
780 if ( pDstEnd > pDstBegin + bufLen ) {
781 pDstEnd = pDstBegin + bufLen;
784 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fAttrValue ) != 0 ) && ch <= 0x7F ) ) {
790 Debug.Assert( pSrc <= pSrcEnd );
793 if ( pSrc >= pSrcEnd ) {
798 if ( pDst >= pDstEnd ) {
800 bufPos = (int)(pDst - pDstBegin);
802 pDst = pDstBegin + 1;
807 // some character needs to be escaped
810 pDst = AmpEntity( pDst );
813 pDst = LtEntity( pDst );
816 pDst = GtEntity( pDst );
825 if ( newLineHandling == NewLineHandling.Replace ) {
827 pDst = WriteNewLine( pDst );
836 switch ( newLineHandling ) {
837 case NewLineHandling.Replace:
838 // Replace "\r\n", or "\r" with NewLineChars
839 if ( pSrc[1] == '\n' ) {
843 pDst = WriteNewLine( pDst );
846 case NewLineHandling.Entitize:
848 pDst = CarriageReturnEntity( pDst );
850 case NewLineHandling.None:
857 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 = EncodeMultibyteUTF8( ch, pDst ); pSrc++; };
862 bufPos = (int)(pDst - pDstBegin);
869 protected unsafe void RawText( string s ) {
870 Debug.Assert( s != null );
872 fixed ( char * pSrcBegin = s ) {
873 RawText( pSrcBegin, pSrcBegin + s.Length );
877 protected unsafe void RawText( char * pSrcBegin, char * pSrcEnd ) {
879 fixed ( byte * pDstBegin = bufBytes ) {
880 byte * pDst = pDstBegin + this.bufPos;
881 char * pSrc = pSrcBegin;
885 byte * pDstEnd = pDst + ( pSrcEnd - pSrc );
886 if ( pDstEnd > pDstBegin + this.bufLen ) {
887 pDstEnd = pDstBegin + this.bufLen;
890 while ( pDst < pDstEnd && ( ( ch = *pSrc ) <= 0x7F ) ) {
896 Debug.Assert( pSrc <= pSrcEnd );
899 if ( pSrc >= pSrcEnd ) {
904 if ( pDst >= pDstEnd ) {
906 bufPos = (int)(pDst - pDstBegin);
908 pDst = pDstBegin + 1;
913 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 = EncodeMultibyteUTF8( ch, pDst ); pSrc++; };
916 bufPos = (int)(pDst - pDstBegin);
921 protected unsafe void WriteRawWithCharChecking( char * pSrcBegin, char * pSrcEnd ) {
923 fixed ( byte * pDstBegin = bufBytes ) {
924 char * pSrc = pSrcBegin;
925 byte * pDst = pDstBegin + bufPos;
929 byte * pDstEnd = pDst + ( pSrcEnd - pSrc );
930 if ( pDstEnd > pDstBegin + bufLen ) {
931 pDstEnd = pDstBegin + bufLen;
934 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fText ) != 0 ) && ch <= 0x7F ) ) {
941 Debug.Assert( pSrc <= pSrcEnd );
944 if ( pSrc >= pSrcEnd ) {
949 if ( pDst >= pDstEnd ) {
951 bufPos = (int)(pDst - pDstBegin);
953 pDst = pDstBegin + 1;
958 // handle special characters
968 if ( newLineHandling == NewLineHandling.Replace ) {
969 // Normalize "\r\n", or "\r" to NewLineChars
970 if ( pSrc[1] == '\n' ) {
974 pDst = WriteNewLine( pDst );
983 if ( newLineHandling == NewLineHandling.Replace ) {
985 pDst = WriteNewLine( pDst );
994 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 = EncodeMultibyteUTF8( ch, pDst ); pSrc++; };
999 bufPos = (int)(pDst - pDstBegin);
1004 protected unsafe void WriteCommentOrPi( string text, int stopChar ) {
1006 if ( text.Length == 0 ) {
1007 if ( bufPos >= bufLen ) {
1013 fixed ( char * pSrcBegin = text )
1015 fixed ( byte * pDstBegin = bufBytes ) {
1016 char * pSrc = pSrcBegin;
1018 char * pSrcEnd = pSrcBegin + text.Length;
1020 byte * pDst = pDstBegin + bufPos;
1024 byte * pDstEnd = pDst + ( pSrcEnd - pSrc );
1025 if ( pDstEnd > pDstBegin + bufLen ) {
1026 pDstEnd = pDstBegin + bufLen;
1029 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fText ) != 0 ) && ch != stopChar && ch <= 0x7F ) ) {
1036 Debug.Assert( pSrc <= pSrcEnd );
1039 if ( pSrc >= pSrcEnd ) {
1044 if ( pDst >= pDstEnd ) {
1046 bufPos = (int)(pDst - pDstBegin);
1048 pDst = pDstBegin + 1;
1053 // handle special characters
1058 if ( ch == stopChar ) {
1059 // Insert space between adjacent dashes or before comment's end dashes
1060 if ( pSrc + 1 == pSrcEnd || *(pSrc + 1)== '-' ) {
1069 if ( ch == stopChar ) {
1070 // Processing instruction: insert space between adjacent '?' and '>'
1071 if ( pSrc + 1 < pSrcEnd && *(pSrc + 1)== '>' ) {
1082 if ( newLineHandling == NewLineHandling.Replace ) {
1083 // Normalize "\r\n", or "\r" to NewLineChars
1084 if ( pSrc[1] == '\n' ) {
1088 pDst = WriteNewLine( pDst );
1097 if ( newLineHandling == NewLineHandling.Replace ) {
1099 pDst = WriteNewLine( pDst );
1114 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 = EncodeMultibyteUTF8( ch, pDst ); pSrc++; };
1119 bufPos = (int)(pDst - pDstBegin);
1124 protected unsafe void WriteCDataSection( string text ) {
1125 if ( text.Length == 0 ) {
1126 if ( bufPos >= bufLen ) {
1134 fixed ( char * pSrcBegin = text )
1136 fixed ( byte * pDstBegin = bufBytes ) {
1137 char * pSrc = pSrcBegin;
1139 char * pSrcEnd = pSrcBegin + text.Length;
1141 byte * pDst = pDstBegin + bufPos;
1145 byte * pDstEnd = pDst + ( pSrcEnd - pSrc );
1146 if ( pDstEnd > pDstBegin + bufLen ) {
1147 pDstEnd = pDstBegin + bufLen;
1150 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fAttrValue ) != 0 ) && ch != ']' && ch <= 0x7F ) ) {
1157 Debug.Assert( pSrc <= pSrcEnd );
1160 if ( pSrc >= pSrcEnd ) {
1165 if ( pDst >= pDstEnd ) {
1167 bufPos = (int)(pDst - pDstBegin);
1169 pDst = pDstBegin + 1;
1174 // handle special characters
1177 if ( hadDoubleBracket && pDst[-1] == (byte) ']') { // pDst[-1] will always correct - there is a padding character at _BUFFER[0]
1178 // The characters "]]>" were found within the CData text
1179 pDst = RawEndCData( pDst );
1180 pDst = RawStartCData( pDst );
1186 if ( pDst[-1] == (byte)']' ) { // pDst[-1] will always correct - there is a padding character at _BUFFER[0]
1187 hadDoubleBracket = true;
1190 hadDoubleBracket = false;
1196 if ( newLineHandling == NewLineHandling.Replace ) {
1197 // Normalize "\r\n", or "\r" to NewLineChars
1198 if ( pSrc[1] == '\n' ) {
1202 pDst = WriteNewLine( pDst );
1211 if ( newLineHandling == NewLineHandling.Replace ) {
1213 pDst = WriteNewLine( pDst );
1230 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 = EncodeMultibyteUTF8( ch, pDst ); pSrc++; };
1235 bufPos = (int)(pDst - pDstBegin);
1240 // Returns true if UTF8 encoded byte is first of four bytes that encode a surrogate pair.
1241 // To do this, detect the bit pattern 11110xxx.
1242 private static bool IsSurrogateByte( byte b ) {
1243 return (b & 0xF8) == 0xF0;
1246 private static unsafe byte* EncodeSurrogate( char* pSrc, char* pSrcEnd, byte* pDst ) {
1247 Debug.Assert( XmlCharType.IsSurrogate( *pSrc ) );
1249 if ( ch <= XmlCharType.SurHighEnd ) {
1250 if ( pSrc + 1 < pSrcEnd ) {
1251 int lowChar = pSrc[1];
1252 if ( lowChar >= XmlCharType.SurLowStart &&
1253 (LocalAppContextSwitches.DontThrowOnInvalidSurrogatePairs || lowChar <= XmlCharType.SurLowEnd)) {
1255 // Calculate Unicode scalar value for easier manipulations (see section 3.7 in Unicode spec)
1256 // The scalar value repositions surrogate values to start at 0x10000.
1258 ch = XmlCharType.CombineSurrogateChar( lowChar, ch );
1260 pDst[0] = (byte)( 0xF0 | ( ch >> 18 ) );
1261 pDst[1] = (byte)( 0x80 | ( ch >> 12 ) & 0x3F );
1262 pDst[2] = (byte)( 0x80 | ( ch >> 6 ) & 0x3F );
1263 pDst[3] = (byte)( 0x80 | ch & 0x3F);
1268 throw XmlConvert.CreateInvalidSurrogatePairException( (char)lowChar, (char)ch );
1270 throw new ArgumentException( Res.GetString( Res.Xml_InvalidSurrogateMissingLowChar ) );
1272 throw XmlConvert.CreateInvalidHighSurrogateCharException( (char)ch );
1275 private unsafe byte* InvalidXmlChar( int ch, byte* pDst, bool entitize ) {
1276 Debug.Assert( !xmlCharType.IsWhiteSpace( (char)ch ) );
1277 Debug.Assert( !xmlCharType.IsAttributeValueChar( (char)ch ) );
1279 if ( checkCharacters ) {
1280 // This method will never be called on surrogates, so it is ok to pass in '\0' to the CreateInvalidCharException
1281 throw XmlConvert.CreateInvalidCharException( (char)ch, '\0' );
1285 return CharEntity( pDst, (char)ch );
1296 pDst = EncodeMultibyteUTF8( ch, pDst );
1304 internal unsafe void EncodeChar(ref char* pSrc, char*pSrcEnd, ref byte* pDst) {
1306 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 = EncodeMultibyteUTF8( ch, pDst ); pSrc++; };
1309 internal static unsafe byte* EncodeMultibyteUTF8( int ch, byte* pDst ) {
1310 Debug.Assert( ch >= 0x80 && !XmlCharType.IsSurrogate( ch ) );
1312 /* UTF8-2: If ch is in 0x80-0x7ff range, then use 2 bytes to encode it */
1314 *pDst = (byte)( unchecked((sbyte)0xC0) | (ch >> 6) );
1316 /* UTF8-3: If ch is anything else, then default to using 3 bytes to encode it. */
1318 *pDst = (byte)( unchecked((sbyte)0xE0) | ( ch >> 12 ) );
1321 *pDst = (byte)( unchecked((sbyte)0x80) | ( ch >> 6 ) & 0x3F);
1324 *pDst = (byte)( 0x80 | ch & 0x3F );
1328 // Encode *pSrc as a sequence of UTF8 bytes. Write the bytes to pDst and return an updated pointer.
1330 internal static unsafe void CharToUTF8( ref char * pSrc, char * pSrcEnd, ref byte * pDst ) {
1337 else if ( XmlCharType.IsSurrogate( ch ) ) {
1338 pDst = EncodeSurrogate( pSrc, pSrcEnd, pDst );
1342 pDst = EncodeMultibyteUTF8( ch, pDst );
1347 // Write NewLineChars to the specified buffer position and return an updated position.
1349 protected unsafe byte * WriteNewLine( byte * pDst ) {
1350 fixed ( byte * pDstBegin = bufBytes ) {
1351 bufPos = (int) (pDst - pDstBegin);
1352 // Let RawText do the real work
1353 RawText( newLineChars );
1354 return pDstBegin + bufPos;
1358 // Following methods do not check whether pDst is beyond the bufSize because the buffer was allocated with a OVERFLOW to accomodate
1359 // for the writes of small constant-length string as below.
1361 // Entitize '<' as "<". Return an updated pointer.
1363 protected static unsafe byte * LtEntity( byte * pDst ) {
1364 pDst[0] = (byte)'&';
1365 pDst[1] = (byte)'l';
1366 pDst[2] = (byte)'t';
1367 pDst[3] = (byte)';';
1371 // Entitize '>' as ">". Return an updated pointer.
1373 protected static unsafe byte * GtEntity( byte * pDst ) {
1374 pDst[0] = (byte)'&';
1375 pDst[1] = (byte)'g';
1376 pDst[2] = (byte)'t';
1377 pDst[3] = (byte)';';
1381 // Entitize '&' as "&". Return an updated pointer.
1383 protected static unsafe byte * AmpEntity( byte * pDst ) {
1384 pDst[0] = (byte)'&';
1385 pDst[1] = (byte)'a';
1386 pDst[2] = (byte)'m';
1387 pDst[3] = (byte)'p';
1388 pDst[4] = (byte)';';
1392 // Entitize '"' as """. Return an updated pointer.
1394 protected static unsafe byte * QuoteEntity( byte * pDst ) {
1395 pDst[0] = (byte)'&';
1396 pDst[1] = (byte)'q';
1397 pDst[2] = (byte)'u';
1398 pDst[3] = (byte)'o';
1399 pDst[4] = (byte)'t';
1400 pDst[5] = (byte)';';
1404 // Entitize '\t' as "	". Return an updated pointer.
1406 protected static unsafe byte * TabEntity( byte * pDst ) {
1407 pDst[0] = (byte)'&';
1408 pDst[1] = (byte)'#';
1409 pDst[2] = (byte)'x';
1410 pDst[3] = (byte)'9';
1411 pDst[4] = (byte)';';
1415 // Entitize 0xa as "
". Return an updated pointer.
1417 protected static unsafe byte * LineFeedEntity( byte * pDst ) {
1418 pDst[0] = (byte)'&';
1419 pDst[1] = (byte)'#';
1420 pDst[2] = (byte)'x';
1421 pDst[3] = (byte)'A';
1422 pDst[4] = (byte)';';
1426 // Entitize 0xd as "
". Return an updated pointer.
1428 protected static unsafe byte * CarriageReturnEntity( byte * pDst ) {
1429 pDst[0] = (byte)'&';
1430 pDst[1] = (byte)'#';
1431 pDst[2] = (byte)'x';
1432 pDst[3] = (byte)'D';
1433 pDst[4] = (byte)';';
1437 private static unsafe byte * CharEntity( byte * pDst, char ch ) {
1438 string s = ((int)ch).ToString( "X",NumberFormatInfo.InvariantInfo );
1439 pDst[0] = (byte)'&';
1440 pDst[1] = (byte)'#';
1441 pDst[2] = (byte)'x';
1444 fixed ( char *pSrc = s ) {
1446 while ( ( *pDst++ = (byte)*pS++ ) != 0 );
1449 pDst[-1] = (byte)';';
1453 // Write "<![CDATA[" to the specified buffer. Return an updated pointer.
1455 protected static unsafe byte * RawStartCData( byte * pDst ) {
1456 pDst[0] = (byte)'<';
1457 pDst[1] = (byte)'!';
1458 pDst[2] = (byte)'[';
1459 pDst[3] = (byte)'C';
1460 pDst[4] = (byte)'D';
1461 pDst[5] = (byte)'A';
1462 pDst[6] = (byte)'T';
1463 pDst[7] = (byte)'A';
1464 pDst[8] = (byte)'[';
1468 // Write "]]>" to the specified buffer. Return an updated pointer.
1470 protected static unsafe byte * RawEndCData( byte * pDst ) {
1471 pDst[0] = (byte)']';
1472 pDst[1] = (byte)']';
1473 pDst[2] = (byte)'>';
1477 protected unsafe void ValidateContentChars( string chars, string propertyName, bool allowOnlyWhitespace ) {
1479 if ( allowOnlyWhitespace ) {
1480 if ( !xmlCharType.IsOnlyWhitespace( chars ) ) {
1481 throw new ArgumentException( Res.GetString( Res.Xml_IndentCharsNotWhitespace, propertyName ) );
1485 string error = null;
1486 for ( int i = 0; i < chars.Length; i++ ) {
1487 if ( !xmlCharType.IsTextChar( chars[i] ) ) {
1488 switch ( chars[i] ) {
1496 error = Res.GetString( Res.Xml_InvalidCharacter, XmlException.BuildCharExceptionArgs( chars, i ) );
1499 if ( XmlCharType.IsHighSurrogate(chars[i]) ) {
1500 if ( i + 1 < chars.Length ) {
1501 if ( XmlCharType.IsLowSurrogate(chars[i + 1]) ) {
1506 error = Res.GetString( Res.Xml_InvalidSurrogateMissingLowChar );
1509 else if ( XmlCharType.IsLowSurrogate(chars[i]) ) {
1510 error = Res.GetString( Res.Xml_InvalidSurrogateHighChar, ((uint)chars[i]).ToString( "X", CultureInfo.InvariantCulture ) );
1520 throw new ArgumentException( Res.GetString( Res.Xml_InvalidCharsInIndent, new string[] { propertyName, error } ) );
1526 // Same as base text writer class except that elements, attributes, comments, and pi's are indented.
1527 internal partial class XmlUtf8RawTextWriterIndent : XmlUtf8RawTextWriter {
1532 protected int indentLevel;
1533 protected bool newLineOnAttributes;
1534 protected string indentChars;
1536 protected bool mixedContent;
1537 private BitStack mixedContentStack;
1539 protected ConformanceLevel conformanceLevel = ConformanceLevel.Auto;
1545 public XmlUtf8RawTextWriterIndent( Stream stream, XmlWriterSettings settings ) : base( stream, settings ) {
1550 // XmlWriter methods
1552 public override XmlWriterSettings Settings {
1554 XmlWriterSettings settings = base.Settings;
1556 settings.ReadOnly = false;
1557 settings.Indent = true;
1558 settings.IndentChars = indentChars;
1559 settings.NewLineOnAttributes = newLineOnAttributes;
1560 settings.ReadOnly = true;
1566 public override void WriteDocType( string name, string pubid, string sysid, string subset ) {
1568 if ( !mixedContent && base.textPos != base.bufPos) {
1571 base.WriteDocType( name, pubid, sysid, subset );
1574 public override void WriteStartElement( string prefix, string localName, string ns ) {
1575 Debug.Assert( localName != null && localName.Length != 0 && prefix != null && ns != null );
1578 if ( !mixedContent && base.textPos != base.bufPos) {
1582 mixedContentStack.PushBit( mixedContent );
1584 base.WriteStartElement( prefix, localName, ns );
1587 internal override void StartElementContent() {
1588 // If this is the root element and we're writing a document
1589 // do not inherit the mixedContent flag into the root element.
1590 // This is to allow for whitespace nodes on root level
1591 // without disabling indentation for the whole document.
1592 if (indentLevel == 1 && conformanceLevel == ConformanceLevel.Document) {
1593 mixedContent = false;
1596 mixedContent = mixedContentStack.PeekBit();
1598 base.StartElementContent();
1601 internal override void OnRootElement(ConformanceLevel currentConformanceLevel) {
1602 // Just remember the current conformance level
1603 conformanceLevel = currentConformanceLevel;
1606 internal override void WriteEndElement(string prefix, string localName, string ns) {
1609 if ( !mixedContent && base.contentPos != base.bufPos ) {
1610 // There was content, so try to indent
1611 if ( base.textPos != base.bufPos ) {
1615 mixedContent = mixedContentStack.PopBit();
1617 base.WriteEndElement( prefix, localName, ns );
1620 internal override void WriteFullEndElement(string prefix, string localName, string ns) {
1623 if ( !mixedContent && base.contentPos != base.bufPos ) {
1624 // There was content, so try to indent
1625 if ( base.textPos != base.bufPos ) {
1629 mixedContent = mixedContentStack.PopBit();
1631 base.WriteFullEndElement( prefix, localName, ns );
1634 // Same as base class, plus possible indentation.
1635 public override void WriteStartAttribute( string prefix, string localName, string ns ) {
1637 if ( newLineOnAttributes ) {
1641 base.WriteStartAttribute( prefix, localName, ns );
1644 public override void WriteCData( string text ) {
1645 mixedContent = true;
1646 base.WriteCData( text );
1649 public override void WriteComment( string text ) {
1650 if ( !mixedContent && base.textPos != base.bufPos ) {
1654 base.WriteComment( text );
1657 public override void WriteProcessingInstruction( string target, string text ) {
1658 if ( !mixedContent && base.textPos != base.bufPos ) {
1662 base.WriteProcessingInstruction( target, text );
1665 public override void WriteEntityRef( string name ) {
1666 mixedContent = true;
1667 base.WriteEntityRef( name );
1670 public override void WriteCharEntity( char ch ) {
1671 mixedContent = true;
1672 base.WriteCharEntity( ch );
1675 public override void WriteSurrogateCharEntity( char lowChar, char highChar ) {
1676 mixedContent = true;
1677 base.WriteSurrogateCharEntity( lowChar, highChar );
1680 public override void WriteWhitespace( string ws ) {
1681 mixedContent = true;
1682 base.WriteWhitespace( ws );
1685 public override void WriteString( string text ) {
1686 mixedContent = true;
1687 base.WriteString( text );
1690 public override void WriteChars( char[] buffer, int index, int count ) {
1691 mixedContent = true;
1692 base.WriteChars( buffer, index, count );
1695 public override void WriteRaw( char[] buffer, int index, int count ) {
1696 mixedContent = true;
1697 base.WriteRaw( buffer, index, count );
1700 public override void WriteRaw( string data ) {
1701 mixedContent = true;
1702 base.WriteRaw( data );
1705 public override void WriteBase64( byte[] buffer, int index, int count ) {
1706 mixedContent = true;
1707 base.WriteBase64( buffer, index, count );
1713 private void Init( XmlWriterSettings settings ) {
1715 indentChars = settings.IndentChars;
1716 newLineOnAttributes = settings.NewLineOnAttributes;
1717 mixedContentStack = new BitStack();
1719 // check indent characters that they are valid XML characters
1720 if ( base.checkCharacters ) {
1721 if ( newLineOnAttributes ) {
1722 base.ValidateContentChars( indentChars, "IndentChars", true );
1723 base.ValidateContentChars( newLineChars, "NewLineChars", true );
1726 base.ValidateContentChars( indentChars, "IndentChars", false );
1727 if ( base.newLineHandling != NewLineHandling.Replace ) {
1728 base.ValidateContentChars( newLineChars, "NewLineChars", false );
1734 // Add indentation to output. Write newline and then repeat IndentChars for each indent level.
1735 private void WriteIndent() {
1736 RawText( base.newLineChars );
1737 for ( int i = indentLevel; i > 0; i-- ) {
1738 RawText( indentChars );