/// <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);
            }
        }