Created
December 2, 2020 06:58
-
-
Save YairHalberstadt/1df8fbeb846d6de0f4b701af314df26a to your computer and use it in GitHub Desktop.
Stringbuilder optimization
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
// Licensed to the .NET Foundation under one or more agreements. | |
// The .NET Foundation licenses this file to you under the MIT license. | |
// See the LICENSE file in the project root for more information. | |
using System; | |
using System.Diagnostics; | |
using System.Text; | |
using Roslyn.Utilities; | |
namespace Microsoft.CodeAnalysis.Text | |
{ | |
/// <summary> | |
/// Implementation of <see cref="SourceText"/> based on a <see cref="StringBuilder"/> input | |
/// </summary> | |
internal sealed partial class StringBuilderText : SourceText | |
{ | |
/// <summary> | |
/// Underlying string on which this SourceText instance is based | |
/// </summary> | |
private readonly StringBuilder _builder; | |
private readonly Encoding? _encodingOpt; | |
#if NETCOREAPP | |
private int _currentChunkStart; | |
private StringBuilder.ChunkEnumerator _chunkEnumerator; | |
private readonly object _chunkLock = new object(); | |
#endif | |
public StringBuilderText(StringBuilder builder, Encoding? encodingOpt, SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithm.Sha1) | |
: base(checksumAlgorithm: checksumAlgorithm) | |
{ | |
RoslynDebug.Assert(builder != null); | |
_builder = builder; | |
_encodingOpt = encodingOpt; | |
#if NETCOREAPP | |
_chunkEnumerator = builder.GetChunks(); | |
_chunkEnumerator.MoveNext(); | |
_currentChunkStart = 0; | |
#endif | |
} | |
public override Encoding? Encoding | |
{ | |
get { return _encodingOpt; } | |
} | |
/// <summary> | |
/// Underlying string which is the source of this SourceText instance | |
/// </summary> | |
internal StringBuilder Builder | |
{ | |
get { return _builder; } | |
} | |
/// <summary> | |
/// The length of the text represented by <see cref="StringBuilderText"/>. | |
/// </summary> | |
public override int Length | |
{ | |
get { return _builder.Length; } | |
} | |
/// <summary> | |
/// Returns a character at given position. | |
/// </summary> | |
/// <param name="position">The position to get the character from.</param> | |
/// <returns>The character.</returns> | |
/// <exception cref="ArgumentOutOfRangeException">When position is negative or | |
/// greater than <see cref="Length"/>.</exception> | |
public override char this[int position] | |
{ | |
get | |
{ | |
if (position < 0 || position >= _builder.Length) | |
{ | |
throw new ArgumentOutOfRangeException(nameof(position)); | |
} | |
#if NETCOREAPP | |
// StringBuilder index is O(n), | |
// so optimize the common case of iterating through the text from begining to end using ChunkEnumerator. | |
// This optimization still works if theres a bit of backtracking, so long as we never skip over a character completely. | |
var currentChunkIdx = position - _currentChunkStart; | |
var currentChunk = _chunkEnumerator.Current.Span; | |
if (currentChunkIdx <= currentChunk.Length && currentChunkIdx >= 0) | |
{ | |
lock (_chunkLock) | |
{ | |
currentChunkIdx = position - _currentChunkStart; | |
currentChunk = _chunkEnumerator.Current.Span; | |
if (currentChunkIdx < currentChunk.Length && currentChunkIdx >= 0) | |
{ | |
return currentChunk[currentChunkIdx]; | |
} | |
else if (currentChunkIdx == currentChunk.Length) | |
{ | |
_currentChunkStart += currentChunk.Length; | |
do | |
{ | |
_chunkEnumerator.MoveNext(); | |
currentChunk = _chunkEnumerator.Current.Span; | |
} | |
while (currentChunk.Length == 0); | |
return currentChunk[0]; | |
} | |
} | |
} | |
#endif | |
return _builder[position]; | |
} | |
} | |
/// <summary> | |
/// Provides a string representation of the StringBuilderText located within given span. | |
/// </summary> | |
/// <exception cref="ArgumentOutOfRangeException">When given span is outside of the text range.</exception> | |
public override string ToString(TextSpan span) | |
{ | |
if (span.End > _builder.Length) | |
{ | |
throw new ArgumentOutOfRangeException(nameof(span)); | |
} | |
return _builder.ToString(span.Start, span.Length); | |
} | |
public override void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) | |
{ | |
_builder.CopyTo(sourceIndex, destination, destinationIndex, count); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment