6 using System.Diagnostics;
7 using System.Globalization;
9 using System.Threading.Tasks;
11 namespace System.Xml {
13 // Concrete implementation of XmlWriter abstract class that serializes events as encoded XML
14 // text. The general-purpose XmlEncodedTextWriter uses the Encoder class to output to any
15 // encoding. The XmlUtf8TextWriter class combined the encoding operation with serialization
16 // in order to achieve better performance.
17 internal partial class XmlEncodedRawTextWriter : XmlRawWriter {
19 protected void CheckAsyncCall() {
21 throw new InvalidOperationException(Res.GetString(Res.Xml_WriterAsyncNotSetException));
25 // Write the xml declaration. This must be the first call.
26 internal override async Task WriteXmlDeclarationAsync( XmlStandalone standalone ) {
28 // Output xml declaration only if user allows it and it was not already output
29 if ( !omitXmlDeclaration && !autoXmlDeclaration ) {
31 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
33 await RawTextAsync( "<?xml version=\"" ).ConfigureAwait(false);
36 await RawTextAsync( "1.0" ).ConfigureAwait(false);
39 if ( encoding != null ) {
40 await RawTextAsync( "\" encoding=\"" ).ConfigureAwait(false);
41 await RawTextAsync( encoding.WebName ).ConfigureAwait(false);
45 if ( standalone != XmlStandalone.Omit ) {
46 await RawTextAsync( "\" standalone=\"" ).ConfigureAwait(false);
47 await RawTextAsync( standalone == XmlStandalone.Yes ? "yes" : "no" ).ConfigureAwait(false);
50 await RawTextAsync( "\"?>" ).ConfigureAwait(false);
54 internal override Task WriteXmlDeclarationAsync( string xmldecl ) {
56 // Output xml declaration only if user allows it and it was not already output
57 if ( !omitXmlDeclaration && !autoXmlDeclaration ) {
58 return WriteProcessingInstructionAsync( "xml", xmldecl );
61 return AsyncHelper.DoneTask;
65 // Serialize the document type declaration.
66 public override async Task WriteDocTypeAsync( string name, string pubid, string sysid, string subset ) {
68 Debug.Assert( name != null && name.Length > 0 );
70 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
72 await RawTextAsync( "<!DOCTYPE ").ConfigureAwait(false);
73 await RawTextAsync(name).ConfigureAwait(false);
74 if ( pubid != null ) {
75 await RawTextAsync( " PUBLIC \"" ).ConfigureAwait(false);
76 await RawTextAsync( pubid ).ConfigureAwait(false);
77 await RawTextAsync( "\" \"").ConfigureAwait(false);
78 if ( sysid != null ) {
79 await RawTextAsync( sysid ).ConfigureAwait(false);
81 bufChars[bufPos++] = (char) '"';
83 else if ( sysid != null ) {
84 await RawTextAsync( " SYSTEM \"" ).ConfigureAwait(false);
85 await RawTextAsync( sysid ).ConfigureAwait(false);
86 bufChars[bufPos++] = (char) '"';
89 bufChars[bufPos++] = (char) ' ';
92 if ( subset != null ) {
93 bufChars[bufPos++] = (char) '[';
94 await RawTextAsync( subset ).ConfigureAwait(false);
95 bufChars[bufPos++] = (char) ']';
98 bufChars[this.bufPos++] = (char) '>';
101 // Serialize the beginning of an element start tag: "<prefix:localName"
102 public override Task WriteStartElementAsync( string prefix, string localName, string ns) {
104 Debug.Assert( localName != null && localName.Length > 0 );
105 Debug.Assert( prefix != null );
107 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
109 bufChars[bufPos++] = (char) '<';
110 if (prefix != null && prefix.Length != 0) {
111 task = RawTextAsync(string.Concat(prefix, ":", localName));
114 task = RawTextAsync(localName);
116 return task.CallVoidFuncWhenFinish(WriteStartElementAsync_SetAttEndPos);
119 private void WriteStartElementAsync_SetAttEndPos() {
123 // Serialize an element end tag: "</prefix:localName>", if content was output. Otherwise, serialize
124 // the shortcut syntax: " />".
125 internal override Task WriteEndElementAsync(string prefix, string localName, string ns) {
127 Debug.Assert(localName != null && localName.Length > 0);
128 Debug.Assert(prefix != null);
130 if (trackTextContent && inTextContent != false) { ChangeTextContentMark(false); }
132 if (contentPos != bufPos) {
133 // Content has been output, so can't use shortcut syntax
134 bufChars[bufPos++] = (char)'<';
135 bufChars[bufPos++] = (char)'/';
137 if (prefix != null && prefix.Length != 0) {
138 return RawTextAsync(string.Concat(prefix, ":", localName, ">"));
142 return RawTextAsync(string.Concat(localName, ">"));
146 // Use shortcut syntax; overwrite the already output '>' character
148 bufChars[bufPos++] = (char)' ';
149 bufChars[bufPos++] = (char)'/';
150 bufChars[bufPos++] = (char)'>';
152 return AsyncHelper.DoneTask;
155 // Serialize a full element end tag: "</prefix:localName>"
156 internal override Task WriteFullEndElementAsync(string prefix, string localName, string ns) {
158 Debug.Assert( localName != null && localName.Length > 0 );
159 Debug.Assert( prefix != null );
161 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
163 bufChars[bufPos++] = (char) '<';
164 bufChars[bufPos++] = (char) '/';
166 if (prefix != null && prefix.Length != 0) {
167 return RawTextAsync(string.Concat(prefix, ":", localName, ">"));
170 return RawTextAsync(string.Concat(localName, ">"));
174 // Serialize an attribute tag using double quotes around the attribute value: 'prefix:localName="'
175 protected internal override Task WriteStartAttributeAsync( string prefix, string localName, string ns ) {
177 Debug.Assert( localName != null && localName.Length > 0 );
178 Debug.Assert( prefix != null );
180 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
182 if ( attrEndPos == bufPos ) {
183 bufChars[bufPos++] = (char) ' ';
186 if (prefix != null && prefix.Length > 0) {
187 task = RawTextAsync(string.Concat(prefix, ":", localName));
190 task = RawTextAsync(localName);
192 return task.CallVoidFuncWhenFinish(WriteStartAttribute_SetInAttribute);
195 private void WriteStartAttribute_SetInAttribute() {
196 bufChars[bufPos++] = (char)'=';
197 bufChars[bufPos++] = (char)'"';
198 inAttributeValue = true;
201 // Serialize the end of an attribute value using double quotes: '"'
202 protected internal override Task WriteEndAttributeAsync() {
204 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
205 bufChars[bufPos++] = (char) '"';
206 inAttributeValue = false;
209 return AsyncHelper.DoneTask;
212 internal override async Task WriteNamespaceDeclarationAsync( string prefix, string namespaceName ) {
214 Debug.Assert( prefix != null && namespaceName != null );
216 await this.WriteStartNamespaceDeclarationAsync( prefix ).ConfigureAwait(false);
217 await this.WriteStringAsync( namespaceName ).ConfigureAwait(false);
218 await this.WriteEndNamespaceDeclarationAsync().ConfigureAwait(false);
221 internal override async Task WriteStartNamespaceDeclarationAsync(string prefix) {
223 Debug.Assert( prefix != null );
225 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
227 // VSTFDEVDIV bug #583965: Inconsistency between Silverlight 2 and Dev10 in the way a single xmlns attribute is serialized
228 // Resolved as: Won't fix (breaking change)
231 if ( attrEndPos == bufPos ) {
232 bufChars[bufPos++] = (char)' ';
236 if ( prefix.Length == 0 ) {
237 await RawTextAsync( " xmlns=\"" ).ConfigureAwait(false);
240 await RawTextAsync( " xmlns:" ).ConfigureAwait(false);
241 await RawTextAsync( prefix ).ConfigureAwait(false);
242 bufChars[bufPos++] = (char)'=';
243 bufChars[bufPos++] = (char)'"';
246 inAttributeValue = true;
247 if ( trackTextContent && inTextContent != true ) { ChangeTextContentMark( true ); }
250 internal override Task WriteEndNamespaceDeclarationAsync() {
252 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
253 inAttributeValue = false;
255 bufChars[bufPos++] = (char)'"';
258 return AsyncHelper.DoneTask;
262 // Serialize a CData section. If the "]]>" pattern is found within
263 // the text, replace it with "]]><![CDATA[>".
264 public override async Task WriteCDataAsync( string text ) {
266 Debug.Assert( text != null );
268 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
270 if ( mergeCDataSections && bufPos == cdataPos ) {
271 // Merge adjacent cdata sections - overwrite the "]]>" characters
272 Debug.Assert( bufPos >= 4 );
276 // Start a new cdata section
277 bufChars[bufPos++] = (char) '<';
278 bufChars[bufPos++] = (char) '!';
279 bufChars[bufPos++] = (char) '[';
280 bufChars[bufPos++] = (char) 'C';
281 bufChars[bufPos++] = (char) 'D';
282 bufChars[bufPos++] = (char) 'A';
283 bufChars[bufPos++] = (char) 'T';
284 bufChars[bufPos++] = (char) 'A';
285 bufChars[bufPos++] = (char) '[';
288 await WriteCDataSectionAsync( text ).ConfigureAwait(false);
290 bufChars[bufPos++] = (char) ']';
291 bufChars[bufPos++] = (char) ']';
292 bufChars[bufPos++] = (char) '>';
298 // Serialize a comment.
299 public override async Task WriteCommentAsync( string text ) {
301 Debug.Assert( text != null );
303 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
305 bufChars[bufPos++] = (char) '<';
306 bufChars[bufPos++] = (char) '!';
307 bufChars[bufPos++] = (char) '-';
308 bufChars[bufPos++] = (char) '-';
310 await WriteCommentOrPiAsync( text, '-' ).ConfigureAwait(false);
312 bufChars[bufPos++] = (char) '-';
313 bufChars[bufPos++] = (char) '-';
314 bufChars[bufPos++] = (char) '>';
317 // Serialize a processing instruction.
318 public override async Task WriteProcessingInstructionAsync( string name, string text ) {
320 Debug.Assert( name != null && name.Length > 0 );
321 Debug.Assert( text != null );
323 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
325 bufChars[bufPos++] = (char) '<';
326 bufChars[bufPos++] = (char) '?';
327 await RawTextAsync( name ).ConfigureAwait(false);
329 if ( text.Length > 0 ) {
330 bufChars[bufPos++] = (char) ' ';
331 await WriteCommentOrPiAsync( text, '?' ).ConfigureAwait(false);
334 bufChars[bufPos++] = (char) '?';
335 bufChars[bufPos++] = (char) '>';
338 // Serialize an entity reference.
339 public override async Task WriteEntityRefAsync( string name ) {
341 Debug.Assert( name != null && name.Length > 0 );
343 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
345 bufChars[bufPos++] = (char) '&';
346 await RawTextAsync( name ).ConfigureAwait(false);
347 bufChars[bufPos++] = (char) ';';
349 if ( bufPos > bufLen ) {
350 await FlushBufferAsync().ConfigureAwait(false);
356 // Serialize a character entity reference.
357 public override async Task WriteCharEntityAsync( char ch ) {
359 string strVal = ((int)ch).ToString( "X", NumberFormatInfo.InvariantInfo );
361 if ( checkCharacters && !xmlCharType.IsCharData( ch ) ) {
362 // we just have a single char, not a surrogate, therefore we have to pass in '\0' for the second char
363 throw XmlConvert.CreateInvalidCharException( ch, '\0' );
366 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
368 bufChars[bufPos++] = (char)'&';
369 bufChars[bufPos++] = (char)'#';
370 bufChars[bufPos++] = (char)'x';
371 await RawTextAsync( strVal ).ConfigureAwait(false);
372 bufChars[bufPos++] = (char)';';
374 if ( bufPos > bufLen ) {
375 await FlushBufferAsync().ConfigureAwait(false);
381 // Serialize a whitespace node.
383 public override Task WriteWhitespaceAsync( string ws ) {
385 Debug.Assert( ws != null );
386 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
388 if ( inAttributeValue) {
389 return WriteAttributeTextBlockAsync(ws);
392 return WriteElementTextBlockAsync(ws);
397 // Serialize either attribute or element text using XML rules.
399 public override Task WriteStringAsync( string text ) {
401 Debug.Assert( text != null );
402 if ( trackTextContent && inTextContent != true ) { ChangeTextContentMark( true ); }
404 if ( inAttributeValue) {
405 return WriteAttributeTextBlockAsync( text );
408 return WriteElementTextBlockAsync( text );
413 // Serialize surrogate character entity.
414 public override async Task WriteSurrogateCharEntityAsync( char lowChar, char highChar ) {
416 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
417 int surrogateChar = XmlCharType.CombineSurrogateChar( lowChar, highChar );
419 bufChars[bufPos++] = (char)'&';
420 bufChars[bufPos++] = (char)'#';
421 bufChars[bufPos++] = (char)'x';
422 await RawTextAsync( surrogateChar.ToString( "X", NumberFormatInfo.InvariantInfo ) ).ConfigureAwait(false);
423 bufChars[bufPos++] = (char)';';
427 // Serialize either attribute or element text using XML rules.
428 // Arguments are validated in the XmlWellformedWriter layer.
430 public override Task WriteCharsAsync( char[] buffer, int index, int count ) {
432 Debug.Assert( buffer != null );
433 Debug.Assert( index >= 0 );
434 Debug.Assert( count >= 0 && index + count <= buffer.Length );
436 if ( trackTextContent && inTextContent != true ) { ChangeTextContentMark( true ); }
438 if ( inAttributeValue) {
439 return WriteAttributeTextBlockAsync( buffer, index, count );
442 return WriteElementTextBlockAsync( buffer, index, count );
447 // Serialize raw data.
448 // Arguments are validated in the XmlWellformedWriter layer
450 public override async Task WriteRawAsync( char[] buffer, int index, int count ) {
452 Debug.Assert( buffer != null );
453 Debug.Assert( index >= 0 );
454 Debug.Assert( count >= 0 && index + count <= buffer.Length );
456 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
458 await WriteRawWithCharCheckingAsync( buffer, index, count).ConfigureAwait(false);
463 // Serialize raw data.
465 public override async Task WriteRawAsync( string data ) {
467 Debug.Assert( data != null );
469 if ( trackTextContent && inTextContent != false ) { ChangeTextContentMark( false ); }
471 await WriteRawWithCharCheckingAsync(data).ConfigureAwait(false);
476 // Flush all characters in the buffer to output and call Flush() on the output object.
477 public override async Task FlushAsync() {
479 await FlushBufferAsync().ConfigureAwait(false);
480 await FlushEncoderAsync().ConfigureAwait(false);
482 if ( stream != null ) {
483 await stream.FlushAsync().ConfigureAwait(false);
485 else if ( writer != null ) {
486 await writer.FlushAsync().ConfigureAwait(false);
492 // Implementation methods
494 // Flush all characters in the buffer to output. Do not flush the output object.
495 protected virtual async Task FlushBufferAsync() {
497 // Output all characters (except for previous characters stored at beginning of buffer)
498 if ( !writeToNull ) {
500 Debug.Assert( stream != null || writer != null );
502 if ( stream != null ) {
503 if ( trackTextContent ) {
504 charEntityFallback.Reset( textContentMarks, lastMarkPos );
505 // reset text content tracking
507 if ((lastMarkPos & 1) != 0) {
508 // If the previous buffer ended inside a text content we need to preserve that info
509 // which means the next index to which we write has to be even
510 textContentMarks[1] = 1;
516 Debug.Assert( textContentMarks[0] == 1 );
518 await EncodeCharsAsync( 1, bufPos, true ).ConfigureAwait(false);
521 // Write text to TextWriter
522 await writer.WriteAsync( bufChars, 1, bufPos - 1 ).ConfigureAwait(false);
528 // Future calls to flush (i.e. when Close() is called) don't attempt to write to stream
533 // Move last buffer character to the beginning of the buffer (so that previous character can always be determined)
534 bufChars[0] = bufChars[bufPos - 1];
536 // Reset buffer position
537 textPos = (textPos == bufPos) ? 1 : 0;
538 attrEndPos = (attrEndPos == bufPos) ? 1 : 0;
539 contentPos = 0; // Needs to be zero, since overwriting '>' character is no longer possible
540 cdataPos = 0; // Needs to be zero, since overwriting ']]>' characters is no longer possible
541 bufPos = 1; // Buffer position starts at 1, because we need to be able to safely step back -1 in case we need to
542 // close an empty element or in CDATA section detection of double ]; _BUFFER[0] will always be 0
546 private async Task EncodeCharsAsync( int startOffset, int endOffset, bool writeAllToStream ) {
547 // Write encoded text to stream
551 while ( startOffset < endOffset ) {
552 if ( charEntityFallback != null ) {
553 charEntityFallback.StartOffset = startOffset;
555 encoder.Convert( bufChars, startOffset, endOffset - startOffset, bufBytes, bufBytesUsed, bufBytes.Length - bufBytesUsed, false, out chEnc, out bEnc, out completed );
556 startOffset += chEnc;
557 bufBytesUsed += bEnc;
558 if ( bufBytesUsed >= ( bufBytes.Length - 16 ) ) {
559 await stream.WriteAsync( bufBytes, 0, bufBytesUsed ).ConfigureAwait(false);
563 if ( writeAllToStream && bufBytesUsed > 0 ) {
564 await stream.WriteAsync( bufBytes, 0, bufBytesUsed ).ConfigureAwait(false);
569 private Task FlushEncoderAsync() {
570 Debug.Assert( bufPos == 1 );
571 if ( stream != null ) {
575 // decode no chars, just flush
576 encoder.Convert( bufChars, 1, 0, bufBytes, 0, bufBytes.Length, true, out chEnc, out bEnc, out completed );
578 return stream.WriteAsync( bufBytes, 0, bEnc );
582 return AsyncHelper.DoneTask;
586 // Serialize text that is part of an attribute value. The '&', '<', '>', and '"' characters
588 [SecuritySafeCritical]
589 protected unsafe int WriteAttributeTextBlockNoFlush( char *pSrc, char *pSrcEnd) {
592 fixed ( char * pDstBegin = bufChars ) {
593 char * pDst = pDstBegin + this.bufPos;
597 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
598 if ( pDstEnd > pDstBegin + bufLen ) {
599 pDstEnd = pDstBegin + bufLen;
602 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fAttrValue ) != 0 ) ) ) {
608 Debug.Assert( pSrc <= pSrcEnd );
611 if ( pSrc >= pSrcEnd ) {
616 if ( pDst >= pDstEnd ) {
618 bufPos = (int)(pDst - pDstBegin);
619 return (int)(pSrc - pRaw);
623 // some character needs to be escaped
626 pDst = AmpEntity( pDst );
629 pDst = LtEntity( pDst );
632 pDst = GtEntity( pDst );
635 pDst = QuoteEntity( pDst );
642 if ( newLineHandling == NewLineHandling.None ) {
647 // escape tab in attributes
648 pDst = TabEntity( pDst );
652 if ( newLineHandling == NewLineHandling.None ) {
657 // escape new lines in attributes
658 pDst = CarriageReturnEntity( pDst );
662 if ( newLineHandling == NewLineHandling.None ) {
667 // escape new lines in attributes
668 pDst = LineFeedEntity( pDst );
672 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++; };
677 bufPos = (int)(pDst - pDstBegin);
684 [SecuritySafeCritical]
685 protected unsafe int WriteAttributeTextBlockNoFlush( char[] chars, int index, int count ) {
691 fixed(char* pSrc = &chars[index]) {
692 char* pSrcBeg = pSrc;
693 char* pSrcEnd = pSrcBeg + count;
694 return WriteAttributeTextBlockNoFlush(pSrcBeg, pSrcEnd);
698 [SecuritySafeCritical]
699 protected unsafe int WriteAttributeTextBlockNoFlush( string text, int index, int count ) {
705 fixed(char* pSrc = text) {
706 char* pSrcBeg = pSrc + index;
707 char* pSrcEnd = pSrcBeg + count;
708 return WriteAttributeTextBlockNoFlush(pSrcBeg, pSrcEnd);
712 protected async Task WriteAttributeTextBlockAsync(char[] chars, int index, int count ) {
714 int curIndex = index;
715 int leftCount = count;
717 writeLen = WriteAttributeTextBlockNoFlush(chars, curIndex, leftCount);
718 curIndex += writeLen;
719 leftCount -= writeLen;
721 await FlushBufferAsync().ConfigureAwait(false);
723 } while(writeLen >= 0);
726 protected Task WriteAttributeTextBlockAsync(string text) {
729 int leftCount = text.Length;
731 writeLen = WriteAttributeTextBlockNoFlush(text, curIndex, leftCount);
732 curIndex += writeLen;
733 leftCount -= writeLen;
735 return _WriteAttributeTextBlockAsync(text, curIndex, leftCount);
738 return AsyncHelper.DoneTask;
742 private async Task _WriteAttributeTextBlockAsync(string text, int curIndex, int leftCount) {
744 await FlushBufferAsync().ConfigureAwait(false);
746 writeLen = WriteAttributeTextBlockNoFlush(text, curIndex, leftCount);
747 curIndex += writeLen;
748 leftCount -= writeLen;
750 await FlushBufferAsync().ConfigureAwait(false);
752 } while(writeLen >= 0);
755 // Serialize text that is part of element content. The '&', '<', and '>' characters
757 [SecuritySafeCritical]
758 protected unsafe int WriteElementTextBlockNoFlush(char *pSrc, char *pSrcEnd, out bool needWriteNewLine) {
759 needWriteNewLine = false;
762 fixed ( char * pDstBegin = bufChars ) {
763 char * pDst = pDstBegin + this.bufPos;
767 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
768 if ( pDstEnd > pDstBegin + bufLen ) {
769 pDstEnd = pDstBegin + bufLen;
772 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fAttrValue ) != 0 ) ) ) {
778 Debug.Assert( pSrc <= pSrcEnd );
781 if ( pSrc >= pSrcEnd ) {
786 if ( pDst >= pDstEnd ) {
788 bufPos = (int)(pDst - pDstBegin);
789 return (int)(pSrc - pRaw);
793 // some character needs to be escaped
796 pDst = AmpEntity( pDst );
799 pDst = LtEntity( pDst );
802 pDst = GtEntity( pDst );
811 if ( newLineHandling == NewLineHandling.Replace ) {
813 bufPos = (int)(pDst - pDstBegin);
814 needWriteNewLine = true;
815 return (int)(pSrc - pRaw);
824 switch ( newLineHandling ) {
825 case NewLineHandling.Replace:
826 // Replace "\r\n", or "\r" with NewLineChars
827 if ( pSrc[1] == '\n' ) {
831 bufPos = (int)(pDst - pDstBegin);
832 needWriteNewLine = true;
833 return (int)(pSrc - pRaw);
835 case NewLineHandling.Entitize:
837 pDst = CarriageReturnEntity( pDst );
839 case NewLineHandling.None:
846 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++; };
851 bufPos = (int)(pDst - pDstBegin);
860 [SecuritySafeCritical]
861 protected unsafe int WriteElementTextBlockNoFlush( char[] chars, int index, int count, out bool needWriteNewLine ) {
862 needWriteNewLine = false;
868 fixed (char* pSrc = &chars[index]) {
869 char* pSrcBeg = pSrc;
870 char* pSrcEnd = pSrcBeg + count;
871 return WriteElementTextBlockNoFlush(pSrcBeg, pSrcEnd, out needWriteNewLine);
875 [SecuritySafeCritical]
876 protected unsafe int WriteElementTextBlockNoFlush( string text, int index, int count, out bool needWriteNewLine ) {
877 needWriteNewLine = false;
883 fixed (char* pSrc = text) {
884 char* pSrcBeg = pSrc + index;
885 char* pSrcEnd = pSrcBeg + count;
886 return WriteElementTextBlockNoFlush(pSrcBeg, pSrcEnd, out needWriteNewLine);
890 protected async Task WriteElementTextBlockAsync(char[] chars, int index, int count ) {
892 int curIndex = index;
893 int leftCount = count;
894 bool needWriteNewLine = false;
896 writeLen = WriteElementTextBlockNoFlush(chars, curIndex, leftCount, out needWriteNewLine);
897 curIndex += writeLen;
898 leftCount -= writeLen;
899 if (needWriteNewLine)
902 await RawTextAsync(newLineChars).ConfigureAwait(false);
906 else if (writeLen >= 0)
908 await FlushBufferAsync().ConfigureAwait(false);
910 } while(writeLen >= 0 || needWriteNewLine);
913 protected Task WriteElementTextBlockAsync(string text) {
916 int leftCount = text.Length;
917 bool needWriteNewLine = false;
919 writeLen = WriteElementTextBlockNoFlush(text, curIndex, leftCount, out needWriteNewLine);
920 curIndex += writeLen;
921 leftCount -= writeLen;
922 if (needWriteNewLine) {
923 return _WriteElementTextBlockAsync(true, text, curIndex, leftCount);
925 else if (writeLen >= 0) {
926 return _WriteElementTextBlockAsync(false, text, curIndex, leftCount);
929 return AsyncHelper.DoneTask;
932 private async Task _WriteElementTextBlockAsync(bool newLine, string text, int curIndex, int leftCount) {
934 bool needWriteNewLine = false;
937 await RawTextAsync(newLineChars).ConfigureAwait(false);
942 await FlushBufferAsync().ConfigureAwait(false);
946 writeLen = WriteElementTextBlockNoFlush(text, curIndex, leftCount, out needWriteNewLine);
947 curIndex += writeLen;
948 leftCount -= writeLen;
949 if (needWriteNewLine) {
951 await RawTextAsync(newLineChars).ConfigureAwait(false);
955 else if (writeLen >= 0) {
956 await FlushBufferAsync().ConfigureAwait(false);
958 } while (writeLen >= 0 || needWriteNewLine);
961 [SecuritySafeCritical]
962 protected unsafe int RawTextNoFlush( char * pSrcBegin, char * pSrcEnd ) {
963 char* pRaw = pSrcBegin;
965 fixed ( char * pDstBegin = bufChars ) {
966 char * pDst = pDstBegin + this.bufPos;
967 char * pSrc = pSrcBegin;
971 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
972 if ( pDstEnd > pDstBegin + this.bufLen ) {
973 pDstEnd = pDstBegin + this.bufLen;
976 while ( pDst < pDstEnd && ( ( ch = *pSrc ) < XmlCharType.SurHighStart ) ) {
982 Debug.Assert( pSrc <= pSrcEnd );
985 if ( pSrc >= pSrcEnd ) {
990 if ( pDst >= pDstEnd ) {
992 bufPos = (int)(pDst - pDstBegin);
993 return (int)(pSrc - pRaw);
997 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++; };
1000 bufPos = (int)(pDst - pDstBegin);
1007 [SecuritySafeCritical]
1008 protected unsafe int RawTextNoFlush( string text, int index, int count ) {
1013 fixed(char* pSrc = text) {
1014 char* pSrcBegin = pSrc + index;
1015 char* pSrcEnd = pSrcBegin + count;
1016 return RawTextNoFlush(pSrcBegin, pSrcEnd );
1020 protected Task RawTextAsync(string text) {
1023 int leftCount = text.Length;
1025 writeLen = RawTextNoFlush(text, curIndex, leftCount);
1026 curIndex += writeLen;
1027 leftCount -= writeLen;
1028 if (writeLen >= 0) {
1029 return _RawTextAsync(text, curIndex, leftCount);
1032 return AsyncHelper.DoneTask;
1035 private async Task _RawTextAsync(string text, int curIndex, int leftCount) {
1036 await FlushBufferAsync().ConfigureAwait(false);
1039 writeLen = RawTextNoFlush(text, curIndex, leftCount);
1040 curIndex += writeLen;
1041 leftCount -= writeLen;
1042 if (writeLen >= 0) {
1043 await FlushBufferAsync().ConfigureAwait(false);
1045 } while (writeLen >= 0);
1048 [SecuritySafeCritical]
1049 protected unsafe int WriteRawWithCharCheckingNoFlush( char * pSrcBegin, char * pSrcEnd, out bool needWriteNewLine) {
1050 needWriteNewLine = false;
1051 char* pRaw = pSrcBegin;
1053 fixed ( char * pDstBegin = bufChars ) {
1054 char * pSrc = pSrcBegin;
1055 char * pDst = pDstBegin + bufPos;
1059 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
1060 if ( pDstEnd > pDstBegin + bufLen ) {
1061 pDstEnd = pDstBegin + bufLen;
1064 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fText ) != 0 ) ) ) {
1071 Debug.Assert( pSrc <= pSrcEnd );
1074 if ( pSrc >= pSrcEnd ) {
1079 if ( pDst >= pDstEnd ) {
1081 bufPos = (int)(pDst - pDstBegin);
1082 return (int)(pSrc - pRaw);
1086 // handle special characters
1096 if ( newLineHandling == NewLineHandling.Replace ) {
1097 // Normalize "\r\n", or "\r" to NewLineChars
1098 if ( pSrc[1] == '\n' ) {
1102 bufPos = (int)(pDst - pDstBegin);
1103 needWriteNewLine = true;
1104 return (int)(pSrc - pRaw);
1113 if ( newLineHandling == NewLineHandling.Replace ) {
1115 bufPos = (int)(pDst - pDstBegin);
1116 needWriteNewLine = true;
1117 return (int)(pSrc - pRaw);
1126 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++; };
1131 bufPos = (int)(pDst - pDstBegin);
1138 [SecuritySafeCritical]
1139 protected unsafe int WriteRawWithCharCheckingNoFlush( char[] chars, int index, int count, out bool needWriteNewLine ) {
1140 needWriteNewLine = false;
1145 fixed(char* pSrc = &chars[index]) {
1146 char* pSrcBeg = pSrc;
1147 char* pSrcEnd = pSrcBeg + count;
1148 return WriteRawWithCharCheckingNoFlush(pSrcBeg, pSrcEnd, out needWriteNewLine);
1152 [SecuritySafeCritical]
1153 protected unsafe int WriteRawWithCharCheckingNoFlush(string text, int index, int count , out bool needWriteNewLine) {
1154 needWriteNewLine = false;
1159 fixed(char* pSrc = text) {
1160 char* pSrcBeg = pSrc + index;
1161 char* pSrcEnd = pSrcBeg + count;
1162 return WriteRawWithCharCheckingNoFlush(pSrcBeg, pSrcEnd, out needWriteNewLine);
1166 protected async Task WriteRawWithCharCheckingAsync(char[] chars, int index, int count ) {
1168 int curIndex = index;
1169 int leftCount = count;
1170 bool needWriteNewLine = false;
1172 writeLen = WriteRawWithCharCheckingNoFlush(chars, curIndex, leftCount, out needWriteNewLine);
1173 curIndex += writeLen;
1174 leftCount -= writeLen;
1175 if (needWriteNewLine)
1177 await RawTextAsync(newLineChars).ConfigureAwait(false);
1181 else if (writeLen >= 0)
1183 await FlushBufferAsync().ConfigureAwait(false);
1185 } while(writeLen >= 0 || needWriteNewLine);
1188 protected async Task WriteRawWithCharCheckingAsync(string text ) {
1191 int leftCount = text.Length;
1192 bool needWriteNewLine = false;
1194 writeLen = WriteRawWithCharCheckingNoFlush(text, curIndex, leftCount, out needWriteNewLine);
1195 curIndex += writeLen;
1196 leftCount -= writeLen;
1197 if (needWriteNewLine)
1199 await RawTextAsync(newLineChars).ConfigureAwait(false);
1203 else if (writeLen >= 0)
1205 await FlushBufferAsync().ConfigureAwait(false);
1207 } while(writeLen >= 0 || needWriteNewLine);
1210 [SecuritySafeCritical]
1211 protected unsafe int WriteCommentOrPiNoFlush( string text, int index, int count, int stopChar, out bool needWriteNewLine ) {
1213 needWriteNewLine = false;
1218 fixed ( char * pSrcText = text ) {
1219 char * pSrcBegin = pSrcText + index;
1221 fixed ( char * pDstBegin = bufChars ) {
1222 char * pSrc = pSrcBegin;
1226 char * pSrcEnd = pSrcBegin + count;
1228 char * pDst = pDstBegin + bufPos;
1232 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
1233 if ( pDstEnd > pDstBegin + bufLen ) {
1234 pDstEnd = pDstBegin + bufLen;
1237 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fText ) != 0 ) && ch != stopChar ) ) {
1244 Debug.Assert( pSrc <= pSrcEnd );
1247 if ( pSrc >= pSrcEnd ) {
1252 if ( pDst >= pDstEnd ) {
1254 bufPos = (int)(pDst - pDstBegin);
1255 return (int)(pSrc - pRaw);
1259 // handle special characters
1264 if ( ch == stopChar ) {
1265 // Insert space between adjacent dashes or before comment's end dashes
1266 if ( pSrc + 1 == pSrcEnd || *(pSrc + 1)== '-' ) {
1275 if ( ch == stopChar ) {
1276 // Processing instruction: insert space between adjacent '?' and '>'
1277 if ( pSrc + 1 < pSrcEnd && *(pSrc + 1)== '>' ) {
1288 if ( newLineHandling == NewLineHandling.Replace ) {
1289 // Normalize "\r\n", or "\r" to NewLineChars
1290 if ( pSrc[1] == '\n' ) {
1294 bufPos = (int)(pDst - pDstBegin);
1295 needWriteNewLine = true;
1296 return (int)(pSrc - pRaw);
1305 if ( newLineHandling == NewLineHandling.Replace ) {
1307 bufPos = (int)(pDst - pDstBegin);
1308 needWriteNewLine = true;
1309 return (int)(pSrc - pRaw);
1324 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++; };
1329 bufPos = (int)(pDst - pDstBegin);
1337 protected async Task WriteCommentOrPiAsync(string text, int stopChar) {
1339 if ( text.Length == 0 ) {
1340 if ( bufPos >= bufLen ) {
1341 await FlushBufferAsync().ConfigureAwait(false);
1348 int leftCount = text.Length;
1349 bool needWriteNewLine = false;
1351 writeLen = WriteCommentOrPiNoFlush(text, curIndex, leftCount, stopChar, out needWriteNewLine);
1352 curIndex += writeLen;
1353 leftCount -= writeLen;
1354 if (needWriteNewLine)
1356 await RawTextAsync(newLineChars).ConfigureAwait(false);
1360 else if (writeLen >= 0)
1362 await FlushBufferAsync().ConfigureAwait(false);
1364 } while(writeLen >= 0 || needWriteNewLine);
1367 [SecuritySafeCritical]
1368 protected unsafe int WriteCDataSectionNoFlush( string text, int index, int count , out bool needWriteNewLine) {
1369 needWriteNewLine = false;
1377 fixed ( char * pSrcText = text ) {
1379 char* pSrcBegin = pSrcText + index;
1381 fixed ( char * pDstBegin = bufChars ) {
1382 char * pSrc = pSrcBegin;
1384 char * pSrcEnd = pSrcBegin + count;
1388 char * pDst = pDstBegin + bufPos;
1392 char * pDstEnd = pDst + ( pSrcEnd - pSrc );
1393 if ( pDstEnd > pDstBegin + bufLen ) {
1394 pDstEnd = pDstBegin + bufLen;
1397 while ( pDst < pDstEnd && ( ( ( xmlCharType.charProperties[( ch = *pSrc )] & XmlCharType.fAttrValue ) != 0 ) && ch != ']' ) ) {
1404 Debug.Assert( pSrc <= pSrcEnd );
1407 if ( pSrc >= pSrcEnd ) {
1412 if ( pDst >= pDstEnd ) {
1414 bufPos = (int)(pDst - pDstBegin);
1415 return (int)(pSrc - pRaw);
1419 // handle special characters
1422 if ( hadDoubleBracket && pDst[-1] == (char) ']') { // pDst[-1] will always correct - there is a padding character at _BUFFER[0]
1423 // The characters "]]>" were found within the CData text
1424 pDst = RawEndCData( pDst );
1425 pDst = RawStartCData( pDst );
1431 if ( pDst[-1] == (char)']' ) { // pDst[-1] will always correct - there is a padding character at _BUFFER[0]
1432 hadDoubleBracket = true;
1435 hadDoubleBracket = false;
1441 if ( newLineHandling == NewLineHandling.Replace ) {
1442 // Normalize "\r\n", or "\r" to NewLineChars
1443 if ( pSrc[1] == '\n' ) {
1447 bufPos = (int)(pDst - pDstBegin);
1448 needWriteNewLine = true;
1449 return (int)(pSrc - pRaw);
1458 if ( newLineHandling == NewLineHandling.Replace ) {
1460 bufPos = (int)(pDst - pDstBegin);
1461 needWriteNewLine = true;
1462 return (int)(pSrc - pRaw);
1479 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++; };
1484 bufPos = (int)(pDst - pDstBegin);
1492 protected async Task WriteCDataSectionAsync(string text) {
1494 if ( text.Length == 0 ) {
1495 if ( bufPos >= bufLen ) {
1496 await FlushBufferAsync().ConfigureAwait(false);
1503 int leftCount = text.Length;
1504 bool needWriteNewLine = false;
1506 writeLen = WriteCDataSectionNoFlush(text, curIndex, leftCount, out needWriteNewLine);
1507 curIndex += writeLen;
1508 leftCount -= writeLen;
1509 if (needWriteNewLine)
1511 await RawTextAsync(newLineChars).ConfigureAwait(false);
1515 else if (writeLen >= 0)
1517 await FlushBufferAsync().ConfigureAwait(false);
1519 } while(writeLen >= 0 || needWriteNewLine);
1524 // Same as base text writer class except that elements, attributes, comments, and pi's are indented.
1525 internal partial class XmlEncodedRawTextWriterIndent : XmlEncodedRawTextWriter {
1527 public override async Task WriteDocTypeAsync( string name, string pubid, string sysid, string subset ) {
1530 if ( !mixedContent && base.textPos != base.bufPos) {
1531 await WriteIndentAsync().ConfigureAwait(false);
1533 await base.WriteDocTypeAsync( name, pubid, sysid, subset ).ConfigureAwait(false);
1536 public override async Task WriteStartElementAsync( string prefix, string localName, string ns ) {
1538 Debug.Assert( localName != null && localName.Length != 0 && prefix != null && ns != null );
1541 if ( !mixedContent && base.textPos != base.bufPos) {
1542 await WriteIndentAsync().ConfigureAwait(false);
1545 mixedContentStack.PushBit( mixedContent );
1547 await base.WriteStartElementAsync( prefix, localName, ns ).ConfigureAwait(false);
1550 internal override async Task WriteEndElementAsync(string prefix, string localName, string ns) {
1554 if ( !mixedContent && base.contentPos != base.bufPos ) {
1555 // There was content, so try to indent
1556 if ( base.textPos != base.bufPos ) {
1557 await WriteIndentAsync().ConfigureAwait(false);
1560 mixedContent = mixedContentStack.PopBit();
1562 await base.WriteEndElementAsync( prefix, localName, ns ).ConfigureAwait(false);
1565 internal override async Task WriteFullEndElementAsync(string prefix, string localName, string ns) {
1569 if ( !mixedContent && base.contentPos != base.bufPos ) {
1570 // There was content, so try to indent
1571 if ( base.textPos != base.bufPos ) {
1572 await WriteIndentAsync().ConfigureAwait(false);
1575 mixedContent = mixedContentStack.PopBit();
1577 await base.WriteFullEndElementAsync( prefix, localName, ns ).ConfigureAwait(false);
1580 // Same as base class, plus possible indentation.
1581 protected internal override async Task WriteStartAttributeAsync( string prefix, string localName, string ns ) {
1584 if ( newLineOnAttributes ) {
1585 await WriteIndentAsync().ConfigureAwait(false);
1588 await base.WriteStartAttributeAsync( prefix, localName, ns ).ConfigureAwait(false);
1591 public override Task WriteCDataAsync( string text ) {
1593 mixedContent = true;
1594 return base.WriteCDataAsync( text );
1597 public override async Task WriteCommentAsync( string text ) {
1599 if ( !mixedContent && base.textPos != base.bufPos ) {
1600 await WriteIndentAsync().ConfigureAwait(false);
1603 await base.WriteCommentAsync( text ).ConfigureAwait(false);
1606 public override async Task WriteProcessingInstructionAsync( string target, string text ) {
1608 if ( !mixedContent && base.textPos != base.bufPos ) {
1609 await WriteIndentAsync().ConfigureAwait(false);
1612 await base.WriteProcessingInstructionAsync( target, text ).ConfigureAwait(false);
1615 public override Task WriteEntityRefAsync( string name ) {
1617 mixedContent = true;
1618 return base.WriteEntityRefAsync( name );
1621 public override Task WriteCharEntityAsync( char ch ) {
1623 mixedContent = true;
1624 return base.WriteCharEntityAsync( ch );
1627 public override Task WriteSurrogateCharEntityAsync( char lowChar, char highChar ) {
1629 mixedContent = true;
1630 return base.WriteSurrogateCharEntityAsync( lowChar, highChar );
1633 public override Task WriteWhitespaceAsync( string ws ) {
1635 mixedContent = true;
1636 return base.WriteWhitespaceAsync( ws );
1639 public override Task WriteStringAsync( string text ) {
1641 mixedContent = true;
1642 return base.WriteStringAsync( text );
1645 public override Task WriteCharsAsync( char[] buffer, int index, int count ) {
1647 mixedContent = true;
1648 return base.WriteCharsAsync( buffer, index, count );
1651 public override Task WriteRawAsync( char[] buffer, int index, int count ) {
1653 mixedContent = true;
1654 return base.WriteRawAsync( buffer, index, count );
1657 public override Task WriteRawAsync( string data ) {
1659 mixedContent = true;
1660 return base.WriteRawAsync( data );
1663 public override Task WriteBase64Async( byte[] buffer, int index, int count ) {
1665 mixedContent = true;
1666 return base.WriteBase64Async( buffer, index, count );
1669 // Add indentation to output. Write newline and then repeat IndentChars for each indent level.
1670 private async Task WriteIndentAsync() {
1672 await RawTextAsync( base.newLineChars ).ConfigureAwait(false);
1673 for ( int i = indentLevel; i > 0; i-- ) {
1674 await RawTextAsync( indentChars ).ConfigureAwait(false);