/// <summary> /// Consolidate two string rebuilders, taking advantage of the fact that they have already extracted the line breaks. /// </summary> public static SimpleStringRebuilder Create(StringRebuilder left, StringRebuilder right) { Debug.Assert(left.Length > 0); Debug.Assert(right.Length > 0); int length = left.Length + right.Length; char[] result = new char[length]; left.CopyTo(0, result, 0, left.Length); right.CopyTo(0, result, left.Length, right.Length); string text = new string(result); int[] lineBreaks; if ((left.LineBreakCount == 0) && (right.LineBreakCount == 0)) { lineBreaks = _emptyLineBreaks; //_lineBreakSpan defaults to 0, 0 which is what we want } else { int offset = 0; if ((text[left.Length] == '\n') && (text[left.Length - 1] == '\r')) { //We have a \r\n spanning the seam ... add that as a special linebreak later. offset = 1; } lineBreaks = new int[left.LineBreakCount + right.LineBreakCount - offset]; int lastLineBreak = 0; int leftLines = left.LineBreakCount - offset; for (int i = 0; (i < leftLines); ++i) { LineSpan lineSpan = left.GetLineFromLineNumber(i); lineBreaks[lastLineBreak++] = lineSpan.End; } if (offset == 1) { lineBreaks[lastLineBreak++] = left.Length - 1; } for (int i = offset; (i < right.LineBreakCount); ++i) { LineSpan lineSpan = right.GetLineFromLineNumber(i); lineBreaks[lastLineBreak++] = lineSpan.End + left.Length; } } return(new SimpleStringRebuilder(SimpleTextStorage.Create(text, lineBreaks))); }
/// <summary> /// Construct a new SimpleStringRebuilder. /// </summary> private SimpleStringRebuilder(string source) : this(SimpleTextStorage.Create(source)) { }
protected override IEnumerable <ITextStorage> DoLoad() { LineBreak firstLineBreak = default(LineBreak); int cumulativeLineLength = 0; bool pendingNewline = false; char[] buffer = AcquireBuffer(BlockSize); try { while (true) { int read = this.reader.ReadBlock(buffer, 0, buffer.Length); if (read == 0) { this.loadCompleted = true; yield break; } else { List <int> lineBreaks = new List <int>(); int c = 0; while (c < read) { int breakLength = TextUtilities.LengthOfLineBreak(buffer, c, read); if (breakLength == 0) { ++c; cumulativeLineLength++; } else { lineBreaks.Add(c); // update information about consistent line endings and line lengths. // this is made complicated by the possibility that \r\n straddles a block boundary. // Todo: might as well handle issues of undecodable codes here instead of the // guessing that we currently do elsewhere. this.longestLineLength = Math.Max(this.longestLineLength, cumulativeLineLength); cumulativeLineLength = 0; if (pendingNewline) { // we've already done consistency checking for this newline, which was part of a // return-newline pair crossing a block boundary. Debug.Assert(c == 0 && buffer[0] == '\n'); pendingNewline = false; } else if (c == read - 1 && buffer[c] == '\r') { // we are on the last character of the block, but it might be the // first character in a two-character line break that crosses a block // boundary. We don't care about that for purposes of constructing the list // of line breaks, but we do care in the context of determining whether line // breaks are consistent. int peeky = this.reader.Peek(); if (peeky < 0) { // end of file. if (firstLineBreak.IsInitialized && !firstLineBreak.IsCr) { this.hasConsistentLineEndings = false; } } else { if (peeky == '\n') { pendingNewline = true; if (!firstLineBreak.IsInitialized) { firstLineBreak = LineBreak.CrLf; } else if (!firstLineBreak.IsCrLf) { this.hasConsistentLineEndings = false; } } else { if (!firstLineBreak.IsInitialized) { firstLineBreak = LineBreak.Cr; } else if (!firstLineBreak.IsCr) { this.hasConsistentLineEndings = false; } } } } // common cases follow else if (!firstLineBreak.IsInitialized) { firstLineBreak = new LineBreak(breakLength, buffer[c]); } else if (!firstLineBreak.IsConsistentWith(breakLength, buffer, c)) { this.hasConsistentLineEndings = false; } c += breakLength; } } this.longestLineLength = Math.Max(this.longestLineLength, cumulativeLineLength); yield return(SimpleTextStorage.Create(new string(buffer, 0, read), lineBreaks)); if (read < buffer.Length) { this.loadCompleted = true; yield break; } } } } finally { ReleaseBuffer(buffer); } }