From 9b1f2b29a3357cf1f98348fa65c8a2af91c1a934 Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Wed, 11 Apr 2018 13:46:28 -0700 Subject: [PATCH 01/12] Adding EnumerateChunks which allow efficient scanning of a StringBuilder --- .../shared/System/Text/StringBuilder.cs | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index 8c1e045216c0..9f0403bfd607 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -557,6 +557,113 @@ public char this[int index] } } + /// + /// EnumerateChunks returns ChunkEnumerable that follows the IEnumerable pattern and + /// thus can be used in a C# 'foreach' statement. Thus + /// foreach (ReadOnlyMemory span in sb.EnumerateChunks()) + /// { /* opererate on bytes using 'span' */ } + /// + /// + public ChunkEnumerable EnumerateChunks() => new ChunkEnumerable(this); + + /// + /// ChunkEnumerable supports the IEnumerable pattern so foreach works (see EnumerateChunks) + /// It needs to be public (so the compiler can use it when building a foreach statement) + /// but users typically don't use it explicitly (which is why it is nested). + /// + public struct ChunkEnumerable + { + internal ChunkEnumerable(StringBuilder stringBuilder) { _stringBuilder = stringBuilder; } + public ChunkEnumerator GetEnumerator() => new ChunkEnumerator(this._stringBuilder); + private StringBuilder _stringBuilder; + } + + /// + /// ChunkEnumerator supports the IEnumerable pattern so foreach works (see EnumerateChunks) + /// It needs to be public (so the compiler can use it when building a foreach statement) + /// but users typically don't use it explicitly (which is why it is nested). + /// + public struct ChunkEnumerator + { + public bool MoveNext() + { + if (_currentChunk == _firstChunk) + return false; + + if (_manyChunks != null) + return _manyChunks.MoveNext(ref _currentChunk); + + StringBuilder next = _firstChunk; + while (next.m_ChunkPrevious != _currentChunk) + next = next.m_ChunkPrevious; + _currentChunk = next; + return true; + } + public ReadOnlySpan Current => new ReadOnlySpan(_currentChunk.m_ChunkChars, 0, _currentChunk.m_ChunkLength); + + #region private + internal ChunkEnumerator(StringBuilder stringBuilder) + { + _firstChunk = stringBuilder; + _currentChunk = null; + _manyChunks = null; + + // There is a performance-vs-allocation tradeoff. Because the chunks + // are a linked list with each chunk pointing to its PREDECESSOR, walking + // the list FORWARD is not efficient. If there are few chunks (< 8) we + // simply scan from the start each time, and tolerate the N*N behavior. + // However above this size, we allocate an array to hold pointers to all + // the chunks and we can be efficient for large N. + int chunkCount = ChunkCount(stringBuilder); + if (8 < chunkCount) + _manyChunks = new ManyChunkInfo(stringBuilder, chunkCount); + } + + private static int ChunkCount(StringBuilder stringBuilder) + { + int ret = 0; + while (stringBuilder != null) + { + ret++; + stringBuilder = stringBuilder.m_ChunkPrevious; + } + return ret; + } + + /// + /// Used to hold all the chunks indexes when you have many chunks. + /// + private class ManyChunkInfo + { + public bool MoveNext(ref StringBuilder current) + { + int pos = ++_chunkPos; + if (_chunks.Length <= pos) + return false; + current = _chunks[pos]; + return true; + } + + public ManyChunkInfo(StringBuilder stringBuilder, int chunkCount) + { + _chunks = new StringBuilder[chunkCount]; + while (0 < --chunkCount) + { + _chunks[chunkCount] = stringBuilder; + stringBuilder = stringBuilder.m_ChunkPrevious; + } + _chunkPos = -1; + } + StringBuilder[] _chunks; // These are in normal order (first chunk first) + int _chunkPos; + } + + StringBuilder _firstChunk; // The first Stringbuilder chunk (which is the end of the logical string) + StringBuilder _currentChunk; // The chunk that this enumerator is currently returning (Current). + ManyChunkInfo _manyChunks; // Only used for long string builders with many chunks (see constructor) + #endregion + } + /// /// Appends a character 0 or more times to the end of this builder. /// From 8ae354469039d44d6e7a3381dea8a559cdd3dba3 Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Tue, 15 May 2018 14:32:59 -0700 Subject: [PATCH 02/12] Change Span to Memory --- .../shared/System/Text/StringBuilder.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index 9f0403bfd607..ac50bcb3fd47 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -560,8 +560,8 @@ public char this[int index] /// /// EnumerateChunks returns ChunkEnumerable that follows the IEnumerable pattern and /// thus can be used in a C# 'foreach' statement. Thus - /// foreach (ReadOnlyMemory span in sb.EnumerateChunks()) - /// { /* opererate on bytes using 'span' */ } + /// foreach (ReadOnlyMemory chunk in sb.EnumerateChunks()) + /// { /* opererate on bytes using 'chunk' */ } /// /// public ChunkEnumerable EnumerateChunks() => new ChunkEnumerable(this); @@ -599,7 +599,7 @@ public bool MoveNext() _currentChunk = next; return true; } - public ReadOnlySpan Current => new ReadOnlySpan(_currentChunk.m_ChunkChars, 0, _currentChunk.m_ChunkLength); + public ReadOnlyMemory Current => new ReadOnlyMemory(_currentChunk.m_ChunkChars, 0, _currentChunk.m_ChunkLength); #region private internal ChunkEnumerator(StringBuilder stringBuilder) From e646dd23b33935cd206539111828714babbeb1d8 Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Wed, 16 May 2018 18:55:41 -0700 Subject: [PATCH 03/12] Add Asserts. Fix bug found in testing. --- .../shared/System/Text/StringBuilder.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index ac50bcb3fd47..0cac07a22fb9 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -573,9 +573,12 @@ public char this[int index] /// public struct ChunkEnumerable { - internal ChunkEnumerable(StringBuilder stringBuilder) { _stringBuilder = stringBuilder; } + internal ChunkEnumerable(StringBuilder stringBuilder) { + Debug.Assert(stringBuilder != null); // Because it is only called with at 'this' pointer. + _stringBuilder = stringBuilder; + } public ChunkEnumerator GetEnumerator() => new ChunkEnumerator(this._stringBuilder); - private StringBuilder _stringBuilder; + private StringBuilder _stringBuilder; // We insure this is never null. } /// @@ -604,8 +607,9 @@ public bool MoveNext() #region private internal ChunkEnumerator(StringBuilder stringBuilder) { + Debug.Assert(stringBuilder != null); _firstChunk = stringBuilder; - _currentChunk = null; + _currentChunk = null; // MoveNext will find the last chunk if we do this. _manyChunks = null; // There is a performance-vs-allocation tradeoff. Because the chunks @@ -647,7 +651,7 @@ public bool MoveNext(ref StringBuilder current) public ManyChunkInfo(StringBuilder stringBuilder, int chunkCount) { _chunks = new StringBuilder[chunkCount]; - while (0 < --chunkCount) + while (0 <= --chunkCount) { _chunks[chunkCount] = stringBuilder; stringBuilder = stringBuilder.m_ChunkPrevious; @@ -661,7 +665,7 @@ public ManyChunkInfo(StringBuilder stringBuilder, int chunkCount) StringBuilder _firstChunk; // The first Stringbuilder chunk (which is the end of the logical string) StringBuilder _currentChunk; // The chunk that this enumerator is currently returning (Current). ManyChunkInfo _manyChunks; // Only used for long string builders with many chunks (see constructor) - #endregion +#endregion } /// @@ -1186,7 +1190,7 @@ public StringBuilder Append(ReadOnlySpan value) return this; } - #region AppendJoin +#region AppendJoin public unsafe StringBuilder AppendJoin(string separator, params object[] values) { @@ -1294,7 +1298,7 @@ private unsafe StringBuilder AppendJoinCore(char* separator, int separatorLen return this; } - #endregion +#endregion public StringBuilder Insert(int index, String value) { From 31590489ad0cca06094a82268ed542619962db4b Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Thu, 17 May 2018 10:41:29 -0700 Subject: [PATCH 04/12] Fix typo --- src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index 0cac07a22fb9..25f2a5aee001 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -561,7 +561,7 @@ public char this[int index] /// EnumerateChunks returns ChunkEnumerable that follows the IEnumerable pattern and /// thus can be used in a C# 'foreach' statement. Thus /// foreach (ReadOnlyMemory chunk in sb.EnumerateChunks()) - /// { /* opererate on bytes using 'chunk' */ } + /// { /* operate on bytes using 'chunk' */ } /// /// public ChunkEnumerable EnumerateChunks() => new ChunkEnumerable(this); From 009a54961064a7df648a80071f3f267e626f16fc Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Thu, 17 May 2018 10:46:57 -0700 Subject: [PATCH 05/12] Better comments --- .../shared/System/Text/StringBuilder.cs | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index 25f2a5aee001..be61e267dba9 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -559,9 +559,11 @@ public char this[int index] /// /// EnumerateChunks returns ChunkEnumerable that follows the IEnumerable pattern and - /// thus can be used in a C# 'foreach' statement. Thus + /// thus can be used in a C# 'foreach' statemens to retreive the data in the StringBuilder + /// as chunks (ReadOnlyMemory) of characters. An example use is: + /// /// foreach (ReadOnlyMemory chunk in sb.EnumerateChunks()) - /// { /* operate on bytes using 'chunk' */ } + /// { /* operate on chars using 'chunk' */ } /// /// public ChunkEnumerable EnumerateChunks() => new ChunkEnumerable(this); @@ -569,25 +571,35 @@ public char this[int index] /// /// ChunkEnumerable supports the IEnumerable pattern so foreach works (see EnumerateChunks) /// It needs to be public (so the compiler can use it when building a foreach statement) - /// but users typically don't use it explicitly (which is why it is nested). + /// but users typically don't use it explicitly (which is why it is a nested type). /// public struct ChunkEnumerable { - internal ChunkEnumerable(StringBuilder stringBuilder) { + /// + /// Implements the IEnumerable pattern. + /// + public ChunkEnumerator GetEnumerator() => new ChunkEnumerator(this._stringBuilder); + + #region private + internal ChunkEnumerable(StringBuilder stringBuilder) + { Debug.Assert(stringBuilder != null); // Because it is only called with at 'this' pointer. _stringBuilder = stringBuilder; } - public ChunkEnumerator GetEnumerator() => new ChunkEnumerator(this._stringBuilder); private StringBuilder _stringBuilder; // We insure this is never null. + #endregion } /// - /// ChunkEnumerator supports the IEnumerable pattern so foreach works (see EnumerateChunks) + /// ChunkEnumerator supports the IEnumerator pattern so foreach works (see EnumerateChunks) /// It needs to be public (so the compiler can use it when building a foreach statement) - /// but users typically don't use it explicitly (which is why it is nested). + /// but users typically don't use it explicitly (which is why it is a nested type). /// public struct ChunkEnumerator { + /// + /// Implements the IEnumerator pattern. + /// public bool MoveNext() { if (_currentChunk == _firstChunk) @@ -602,6 +614,9 @@ public bool MoveNext() _currentChunk = next; return true; } + /// + /// Implements the IEnumerator pattern. + /// public ReadOnlyMemory Current => new ReadOnlyMemory(_currentChunk.m_ChunkChars, 0, _currentChunk.m_ChunkLength); #region private From 014c623435e97e80adcd8ae7f6b8021e6087ddad Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Fri, 18 May 2018 08:18:48 -0700 Subject: [PATCH 06/12] Improve comments, review feedback. --- .../shared/System/Text/StringBuilder.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index be61e267dba9..285593ef2821 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -563,8 +563,19 @@ public char this[int index] /// as chunks (ReadOnlyMemory) of characters. An example use is: /// /// foreach (ReadOnlyMemory chunk in sb.EnumerateChunks()) - /// { /* operate on chars using 'chunk' */ } + /// foreach(char c in chunk.Span) + /// { /* operation on c } /// + /// Note that creating a ReadOnlySpan from a ReadOnlyMemory is expensive compared to the + /// fetching of the character, so create a local variable for the SPAN if you need to use + /// a for statement for example + /// + /// foreach (ReadOnlyMemory chunk in sb.EnumerateChunks()) + /// { + /// var span = chunk.Span; + /// for(int i = 0; i < span.Length; i++) + /// { /* operation on span[i] */ } + /// } /// public ChunkEnumerable EnumerateChunks() => new ChunkEnumerable(this); @@ -583,7 +594,7 @@ public struct ChunkEnumerable #region private internal ChunkEnumerable(StringBuilder stringBuilder) { - Debug.Assert(stringBuilder != null); // Because it is only called with at 'this' pointer. + Debug.Assert(stringBuilder != null); // Because it is only called with a 'this' pointer. _stringBuilder = stringBuilder; } private StringBuilder _stringBuilder; // We insure this is never null. @@ -593,7 +604,7 @@ internal ChunkEnumerable(StringBuilder stringBuilder) /// /// ChunkEnumerator supports the IEnumerator pattern so foreach works (see EnumerateChunks) /// It needs to be public (so the compiler can use it when building a foreach statement) - /// but users typically don't use it explicitly (which is why it is a nested type). + /// but users typically don't use it explicitly (which is why it is a nested type). /// public struct ChunkEnumerator { @@ -614,6 +625,7 @@ public bool MoveNext() _currentChunk = next; return true; } + /// /// Implements the IEnumerator pattern. /// @@ -668,6 +680,7 @@ public ManyChunkInfo(StringBuilder stringBuilder, int chunkCount) _chunks = new StringBuilder[chunkCount]; while (0 <= --chunkCount) { + Debug.Assert(stringBuilder != null); _chunks[chunkCount] = stringBuilder; stringBuilder = stringBuilder.m_ChunkPrevious; } From 6eeb7b6b315d0561dd46707f42c95caa8e34295f Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Fri, 18 May 2018 13:38:06 -0700 Subject: [PATCH 07/12] add readonly attributes --- .../shared/System/Text/StringBuilder.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index 285593ef2821..e76028387765 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -597,7 +597,7 @@ internal ChunkEnumerable(StringBuilder stringBuilder) Debug.Assert(stringBuilder != null); // Because it is only called with a 'this' pointer. _stringBuilder = stringBuilder; } - private StringBuilder _stringBuilder; // We insure this is never null. + readonly private StringBuilder _stringBuilder; // We ensure this is never null. #endregion } @@ -686,13 +686,14 @@ public ManyChunkInfo(StringBuilder stringBuilder, int chunkCount) } _chunkPos = -1; } - StringBuilder[] _chunks; // These are in normal order (first chunk first) + + readonly StringBuilder[] _chunks; // These are in normal order (first chunk first) int _chunkPos; } - StringBuilder _firstChunk; // The first Stringbuilder chunk (which is the end of the logical string) - StringBuilder _currentChunk; // The chunk that this enumerator is currently returning (Current). - ManyChunkInfo _manyChunks; // Only used for long string builders with many chunks (see constructor) + readonly StringBuilder _firstChunk; // The first Stringbuilder chunk (which is the end of the logical string) + StringBuilder _currentChunk; // The chunk that this enumerator is currently returning (Current). + readonly ManyChunkInfo _manyChunks; // Only used for long string builders with many chunks (see constructor) #endregion } From 6198c7443b08a719edb17e2467625b35301b6b7c Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Fri, 18 May 2018 13:40:11 -0700 Subject: [PATCH 08/12] Add another readonly attribute --- src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index e76028387765..964a56afa668 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -584,7 +584,7 @@ public char this[int index] /// It needs to be public (so the compiler can use it when building a foreach statement) /// but users typically don't use it explicitly (which is why it is a nested type). /// - public struct ChunkEnumerable + readonly public struct ChunkEnumerable { /// /// Implements the IEnumerable pattern. From 1028e3801f8eb72680d4d35b68dc734be5f84785 Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Tue, 22 May 2018 10:55:16 -0700 Subject: [PATCH 09/12] Collapse ChunkEnumerable and ChunkEnumerator. --- .../shared/System/Text/StringBuilder.cs | 39 +++++++------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index 964a56afa668..d5549575079c 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -558,8 +558,8 @@ public char this[int index] } /// - /// EnumerateChunks returns ChunkEnumerable that follows the IEnumerable pattern and - /// thus can be used in a C# 'foreach' statemens to retreive the data in the StringBuilder + /// EnumerateChunks returns ChunkEnumerator that follows the IEnumerable pattern and + /// thus can be used in a C# 'foreach' statements to retreive the data in the StringBuilder /// as chunks (ReadOnlyMemory) of characters. An example use is: /// /// foreach (ReadOnlyMemory chunk in sb.EnumerateChunks()) @@ -577,37 +577,24 @@ public char this[int index] /// { /* operation on span[i] */ } /// } /// - public ChunkEnumerable EnumerateChunks() => new ChunkEnumerable(this); + public ChunkEnumerator EnumerateChunks() => new ChunkEnumerator(this); + /// - /// ChunkEnumerable supports the IEnumerable pattern so foreach works (see EnumerateChunks) - /// It needs to be public (so the compiler can use it when building a foreach statement) - /// but users typically don't use it explicitly (which is why it is a nested type). + /// ChunkEnumerator supports both the IEnumerable and IEnumerator pattern so foreach + /// works (see EnumerateChunks). It needs to be public (so the compiler can use it + /// when building a foreach statement) but users typically don't use it explicitly. + /// (which is why it is a nested type). /// - readonly public struct ChunkEnumerable + public struct ChunkEnumerator { + [ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Never)] // Only here to make foreach work + /// - /// Implements the IEnumerable pattern. + /// Implement IEnumerable.GetEnumerator() to return 'this' as the IEnumerator /// - public ChunkEnumerator GetEnumerator() => new ChunkEnumerator(this._stringBuilder); - - #region private - internal ChunkEnumerable(StringBuilder stringBuilder) - { - Debug.Assert(stringBuilder != null); // Because it is only called with a 'this' pointer. - _stringBuilder = stringBuilder; - } - readonly private StringBuilder _stringBuilder; // We ensure this is never null. - #endregion - } + public ChunkEnumerator GetEnumerator() { return this; } - /// - /// ChunkEnumerator supports the IEnumerator pattern so foreach works (see EnumerateChunks) - /// It needs to be public (so the compiler can use it when building a foreach statement) - /// but users typically don't use it explicitly (which is why it is a nested type). - /// - public struct ChunkEnumerator - { /// /// Implements the IEnumerator pattern. /// From 57055f17d50e275e9c904b3f8975065e3d7af526 Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Tue, 22 May 2018 11:16:05 -0700 Subject: [PATCH 10/12] Fix attribute placement --- src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index d5549575079c..68cd380c9a55 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -588,11 +588,10 @@ public char this[int index] /// public struct ChunkEnumerator { - [ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Never)] // Only here to make foreach work - /// /// Implement IEnumerable.GetEnumerator() to return 'this' as the IEnumerator /// + [ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Never)] // Only here to make foreach work public ChunkEnumerator GetEnumerator() { return this; } /// From 79cbf2e17f6cdd1095671c6ab9fa8b9c48bba0f5 Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Tue, 22 May 2018 11:18:36 -0700 Subject: [PATCH 11/12] Fix whitespace --- .../shared/System/Text/StringBuilder.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index 68cd380c9a55..d5cfe9cb53e3 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -588,6 +588,7 @@ public char this[int index] /// public struct ChunkEnumerator { + /// /// Implement IEnumerable.GetEnumerator() to return 'this' as the IEnumerator /// @@ -1205,7 +1206,7 @@ public StringBuilder Append(ReadOnlySpan value) return this; } -#region AppendJoin + #region AppendJoin public unsafe StringBuilder AppendJoin(string separator, params object[] values) { @@ -1313,7 +1314,7 @@ private unsafe StringBuilder AppendJoinCore(char* separator, int separatorLen return this; } -#endregion + #endregion public StringBuilder Insert(int index, String value) { From 0ba7f8d3a974382a214820330408f20389317306 Mon Sep 17 00:00:00 2001 From: Vance Morrison Date: Tue, 22 May 2018 11:25:01 -0700 Subject: [PATCH 12/12] Fixe whitespace --- src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs index d5cfe9cb53e3..fd8da2be3df1 100644 --- a/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs +++ b/src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs @@ -579,7 +579,6 @@ public char this[int index] /// public ChunkEnumerator EnumerateChunks() => new ChunkEnumerator(this); - /// /// ChunkEnumerator supports both the IEnumerable and IEnumerator pattern so foreach /// works (see EnumerateChunks). It needs to be public (so the compiler can use it @@ -588,7 +587,6 @@ public char this[int index] /// public struct ChunkEnumerator { - /// /// Implement IEnumerable.GetEnumerator() to return 'this' as the IEnumerator ///