| File: Workspace\Host\TemporaryStorage\LegacyTemporaryStorageService.cs | Web Access |
| Project: src\roslyn\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces) |
// 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.Composition; using System.IO; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.Host; /// <summary> /// Legacy implementation of obsolete public API <see cref="ITemporaryStorageService"/>. /// </summary> [Obsolete] [ExportWorkspaceService(typeof(ITemporaryStorageService)), Shared] internal sealed class LegacyTemporaryStorageService : ITemporaryStorageService { [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] public LegacyTemporaryStorageService() { } public ITemporaryStreamStorage CreateTemporaryStreamStorage(CancellationToken cancellationToken = default) => new StreamStorage(); public ITemporaryTextStorage CreateTemporaryTextStorage(CancellationToken cancellationToken = default) => new TextStorage(); private sealed class StreamStorage : ITemporaryStreamStorage { private MemoryStream? _stream; public void Dispose() { _stream?.Dispose(); _stream = null; } public Stream ReadStream(CancellationToken cancellationToken = default) { var stream = _stream ?? throw new InvalidOperationException(); // Return a read-only view of the underlying buffer to prevent users from overwriting or directly // disposing the backing storage. return new MemoryStream(stream.GetBuffer(), 0, (int)stream.Length, writable: false); } public async Task<Stream> ReadStreamAsync(CancellationToken cancellationToken = default) { return ReadStream(cancellationToken); } public void WriteStream(Stream stream, CancellationToken cancellationToken = default) { var newStream = new MemoryStream(); stream.CopyTo(newStream); var existingValue = Interlocked.CompareExchange(ref _stream, newStream, null); if (existingValue is not null) { throw new InvalidOperationException(WorkspacesResources.Temporary_storage_cannot_be_written_more_than_once); } } public async Task WriteStreamAsync(Stream stream, CancellationToken cancellationToken = default) { var newStream = new MemoryStream(); #if NET await stream.CopyToAsync(newStream, cancellationToken).ConfigureAwait(false); # else await stream.CopyToAsync(newStream).ConfigureAwait(false); #endif var existingValue = Interlocked.CompareExchange(ref _stream, newStream, null); if (existingValue is not null) { throw new InvalidOperationException(WorkspacesResources.Temporary_storage_cannot_be_written_more_than_once); } } } private sealed class TextStorage : ITemporaryTextStorage { private SourceText? _sourceText; public void Dispose() => _sourceText = null; public SourceText ReadText(CancellationToken cancellationToken = default) => _sourceText ?? throw new InvalidOperationException(); public async Task<SourceText> ReadTextAsync(CancellationToken cancellationToken = default) => ReadText(cancellationToken); public void WriteText(SourceText text, CancellationToken cancellationToken = default) { // This is a trivial implementation, indeed. Note, however, that we retain a strong // reference to the source text, which defeats the intent of RecoverableTextAndVersion, but // is appropriate for this trivial implementation. var existingValue = Interlocked.CompareExchange(ref _sourceText, text, null); if (existingValue is not null) { throw new InvalidOperationException(WorkspacesResources.Temporary_storage_cannot_be_written_more_than_once); } } public async Task WriteTextAsync(SourceText text, CancellationToken cancellationToken = default) { WriteText(text, cancellationToken); } } }