diff --git a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs index ba5ab7ed1b3d..dc8bddd5e1dc 100644 --- a/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs +++ b/src/Compilers/Core/Portable/Syntax/SyntaxNode.cs @@ -325,9 +325,9 @@ public virtual void WriteTo(TextWriter writer) /// is not supported. public SourceText GetText(Encoding? encoding = null, SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithm.Sha1) { - var builder = new StringBuilder(this.Green.FullWidth); - this.WriteTo(new StringWriter(builder)); - return new StringBuilderText(builder, encoding, checksumAlgorithm); + var writer = SourceTextWriter.Create(encoding, checksumAlgorithm, this.Green.FullWidth); + this.WriteTo(writer); + return writer.ToSourceText(); } /// diff --git a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs index 8a64e2ff7157..814853036893 100644 --- a/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs +++ b/src/Workspaces/CSharp/Portable/Workspace/LanguageServices/CSharpSyntaxTreeFactoryService.ParsedSyntaxTree.cs @@ -48,8 +48,7 @@ public override SourceText GetText(CancellationToken cancellationToken) { if (_lazyText == null) { - var sourceText = SourceTextExtensions.CreateSourceText(GetRoot(cancellationToken), Encoding, _checksumAlgorithm, cancellationToken); - Interlocked.CompareExchange(ref _lazyText, sourceText, null); + Interlocked.CompareExchange(ref _lazyText, GetRoot(cancellationToken).GetText(Encoding, _checksumAlgorithm), null); } return _lazyText; diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs index 5b8433ec1ccc..1eef6e5b28e9 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/SourceTextExtensions.cs @@ -4,9 +4,7 @@ using System; using System.Collections.Immutable; -using System.Diagnostics; using System.IO; -using System.Linq; using System.Text; using System.Threading; using Microsoft.CodeAnalysis.Host; @@ -238,87 +236,6 @@ public static SourceText ReadFrom(ITextFactoryService textService, ObjectReader return textService.CreateText(textReader, encoding, checksumAlgorithm, cancellationToken); } - public static SourceText CreateSourceText( - SyntaxNode node, Encoding? encoding, SourceHashAlgorithm checksumAlgorithm, CancellationToken cancellationToken) - { - // If this node is small enough to not go into the LOH, we can just fast path directly to creating a SourceText from it. - var totalLength = node.FullWidth(); - if (totalLength <= SourceTextLengthThreshold) - return SourceText.From(node.ToFullString(), encoding, checksumAlgorithm); - - // Allocate the space to write the node into. Explicitly chunked so that nothing goes into the LOH. - var chunks = CreateChunks(totalLength); - - // Write the node into that temp space. - using var chunkWriter = new CharArrayChunkTextWriter(totalLength, chunks, encoding!, cancellationToken); - node.WriteTo(chunkWriter); - Contract.ThrowIfTrue(totalLength != chunkWriter.Position); - - // Call into the text service to make us a SourceText from the chunks. Disposal of this reader will free all - // the intermediary chunks we allocated. - - Contract.ThrowIfTrue(chunks.Any(static (c, s) => c.Length != s, CharArrayLength)); - - using var chunkReader = new CharArrayChunkTextReader(chunks, totalLength); - var result = SourceText.From(chunkReader, totalLength, encoding, checksumAlgorithm); - Contract.ThrowIfTrue(totalLength != chunkReader.Position); - - return result; - - static ImmutableArray CreateChunks(int totalLength) - { - var numberOfChunks = 1 + (totalLength / CharArrayLength); - var buffer = new FixedSizeArrayBuilder(numberOfChunks); - for (var i = 0; i < numberOfChunks; i++) - buffer.Add(s_charArrayPool.Allocate()); - - return buffer.MoveToImmutable(); - } - } - - private static int GetIndexFromPosition(int position) => position / CharArrayLength; - private static int GetColumnFromPosition(int position) => position % CharArrayLength; - - private sealed class CharArrayChunkTextWriter( - int totalLength, ImmutableArray chunks, Encoding encoding, CancellationToken cancellationToken) : TextWriter - { - private readonly int _totalLength = totalLength; - private readonly ImmutableArray _chunks = chunks; - private readonly CancellationToken _cancellationToken = cancellationToken; - - /// - /// Public so that caller can assert that writing out the text actually wrote out the full text of the node. - /// - public int Position { get; private set; } - - public override Encoding Encoding { get; } = encoding; - - public override void Write(string? value) - { - Contract.ThrowIfNull(value); - - var valueSpan = value.AsSpan(); - while (valueSpan.Length > 0) - { - _cancellationToken.ThrowIfCancellationRequested(); - - var chunk = _chunks[GetIndexFromPosition(Position)]; - Contract.ThrowIfTrue(chunk.Length != CharArrayLength); - - var chunkIndex = GetColumnFromPosition(Position); - Contract.ThrowIfTrue(chunkIndex >= CharArrayLength); - - var count = Math.Min(valueSpan.Length, CharArrayLength - chunkIndex); - valueSpan[..count].CopyTo(chunk.AsSpan().Slice(chunkIndex, count)); - - Position += count; - valueSpan = valueSpan[count..]; - } - - Contract.ThrowIfTrue(Position > _totalLength); - } - } - private sealed class CharArrayChunkTextReader(ImmutableArray chunks, int length) : TextReaderWithLength(length) { private readonly ImmutableArray _chunks = chunks; @@ -329,6 +246,9 @@ private sealed class CharArrayChunkTextReader(ImmutableArray chunks, int /// public int Position { get; private set; } + private static int GetIndexFromPosition(int position) => position / CharArrayLength; + private static int GetColumnFromPosition(int position) => position % CharArrayLength; + public static TextReader CreateFromObjectReader(ObjectReader reader) { var length = reader.ReadInt32(); diff --git a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb index 751738b8b856..99b66db0d1bc 100644 --- a/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb +++ b/src/Workspaces/VisualBasic/Portable/Workspace/LanguageServices/VisualBasicSyntaxTreeFactoryService.ParsedSyntaxTree.vb @@ -41,8 +41,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Public Overrides Function GetText(Optional cancellationToken As CancellationToken = Nothing) As SourceText If _lazyText Is Nothing Then - Dim text = SourceTextExtensions.CreateSourceText(GetRoot(cancellationToken), Encoding, _checksumAlgorithm, cancellationToken) - Interlocked.CompareExchange(_lazyText, text, Nothing) + Interlocked.CompareExchange(_lazyText, GetRoot(cancellationToken).GetText(Encoding, _checksumAlgorithm), Nothing) End If Return _lazyText