Created
June 20, 2017 02:08
-
-
Save svick/0c11c752ee59890c5b9f94bf3bfeb4a4 to your computer and use it in GitHub Desktop.
A simple implementtion of StringBuilder.EnumerateChunks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| class Program | |
| { | |
| static void Main() | |
| { | |
| var sb = new StringBuilder | |
| { | |
| m_ChunkChars = "cd".ToCharArray(), | |
| m_ChunkLength = 2, | |
| m_ChunkPrevious = new StringBuilder { m_ChunkChars = "ab".ToCharArray(), m_ChunkLength = 2 } | |
| }; | |
| foreach (var chunk in sb.EnumerateChunks()) | |
| { | |
| Console.WriteLine(new string(chunk.ToArray())); | |
| } | |
| sb.ForEachChunk( | |
| c => | |
| { | |
| Console.WriteLine(new string(c.ToArray())); | |
| return true; | |
| }); | |
| } | |
| } | |
| public sealed class StringBuilder | |
| { | |
| // A StringBuilder is internally represented as a linked list of blocks each of which holds | |
| // a chunk of the string. It turns out string as a whole can also be represented as just a chunk, | |
| // so that is what we do. | |
| /// <summary> | |
| /// The character buffer for this chunk. | |
| /// </summary> | |
| internal char[] m_ChunkChars; | |
| /// <summary> | |
| /// The chunk that logically precedes this chunk. | |
| /// </summary> | |
| internal StringBuilder m_ChunkPrevious; | |
| /// <summary> | |
| /// The number of characters in this chunk. | |
| /// This is the number of elements in <see cref="m_ChunkChars"/> that are in use, from the start of the buffer. | |
| /// </summary> | |
| internal int m_ChunkLength; | |
| /// <summary> | |
| /// The logical offset of this chunk's characters in the string it is a part of. | |
| /// This is the sum of the number of characters in preceding blocks. | |
| /// </summary> | |
| //internal int m_ChunkOffset; | |
| public delegate TResult SpanFunc<T1, TResult>(ReadOnlySpan<T1> input); | |
| public void ForEachChunk(SpanFunc<char, bool> callback) | |
| { | |
| //VerifyClassInvariant(); | |
| ForEachChunk(callback, this); | |
| } | |
| private bool ForEachChunk(SpanFunc<char, bool> callback, StringBuilder chunk) | |
| { | |
| // The chunks are last written first, so we need to output the others first. | |
| var next = chunk.m_ChunkPrevious; | |
| if (next != null && !ForEachChunk(callback, next)) | |
| return false; | |
| // The fields here might be changing and inaccurate if threads are racing, but the validation that Span does on | |
| // construction insures that the values, even if inaccurate, are 'in bounds'. | |
| return callback(new Span<char>(chunk.m_ChunkChars, 0, chunk.m_ChunkLength)); | |
| } | |
| public ChunkEnumerable EnumerateChunks() => new ChunkEnumerable(this); | |
| public struct ChunkEnumerable | |
| { | |
| private readonly StringBuilder builder; | |
| public ChunkEnumerable(StringBuilder builder) => this.builder = builder; | |
| public ChunkEnumerator GetEnumerator() => new ChunkEnumerator(builder); | |
| } | |
| public struct ChunkEnumerator | |
| { | |
| private readonly Stack<StringBuilder> builders; | |
| public ChunkEnumerator(StringBuilder builder) | |
| { | |
| builders = new Stack<StringBuilder>(); | |
| while (builder != null) | |
| { | |
| builders.Push(builder); | |
| builder = builder.m_ChunkPrevious; | |
| } | |
| builders.Push(null); | |
| } | |
| public bool MoveNext() | |
| { | |
| if (builders.Count == 0) | |
| return false; | |
| builders.Pop(); | |
| return builders.Count > 0; | |
| } | |
| public ReadOnlySpan<char> Current | |
| { | |
| get | |
| { | |
| var sb = builders.Peek(); | |
| return new ReadOnlySpan<char>(sb.m_ChunkChars, 0, sb.m_ChunkLength); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment