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