From d8c9929a970efafebe8378aca39c78971778fc6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Fri, 15 Sep 2023 21:46:17 +0200 Subject: [PATCH] Remove stream-like functionality from ContentReader/Writer Use streams instead --- .../ToolUserCommandHandlerService.cs | 13 +-- .../ItemPreview/ElementPreviewViewModel.cs | 2 +- .../ContentAccess/ContentAccessStream.cs | 80 ----------------- .../ContentAccess/IContentProvider.cs | 2 - .../ContentAccess/IContentReader.cs | 8 +- .../ContentAccess/IContentWriter.cs | 7 +- .../ContentAccess/IStreamContainer.cs | 6 ++ .../StreamCopyCommandHandler.cs | 66 ++++---------- .../LocalContentReader.cs | 33 +------ .../LocalContentWriter.cs | 25 +----- .../IRemoteContentProvider.cs | 2 +- .../FileTime.Providers.Remote/ProxyStream.cs | 58 +++++++++++++ .../RemoteContentProvider.cs | 7 +- .../RemoteContentWriter.cs | 87 +++++++++++++++---- .../ContentAccess/IContentAccessManager.cs | 6 +- .../IRemoteConnection.cs | 14 ++- .../Connections/SignalR/ISignalRHub.cs | 18 ++-- .../Connections/SignalR/SignalRConnection.cs | 45 ++++++++-- .../ContentAccess/ContentAccessManager.cs | 20 +++-- .../FileTime.Server.Web/ConnectionHub.cs | 59 ++++++++++--- .../CompressOperation.cs | 17 ++-- .../Compress/CompressCommand.cs | 2 +- .../CompressedContentProvider.cs | 2 +- .../CompressedContentReader.cs | 15 +--- .../CompressedContentReaderFactory.cs | 2 +- .../CompressedSubContentProvider.cs | 2 +- .../Decompress/DecompressCommand.cs | 4 +- .../VirtualDiskContentProvider.cs | 2 +- .../VirtualDiskContentReader.cs | 15 +--- .../VirtualDiskContentReaderFactory.cs | 2 +- .../VirtualDiskSubContentProvider.cs | 2 +- 31 files changed, 313 insertions(+), 310 deletions(-) delete mode 100644 src/Core/FileTime.Core.Abstraction/ContentAccess/ContentAccessStream.cs create mode 100644 src/Core/FileTime.Core.Abstraction/ContentAccess/IStreamContainer.cs create mode 100644 src/Providers/FileTime.Providers.Remote/ProxyStream.cs diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs index bdabdd9..949a3c0 100644 --- a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/ToolUserCommandHandlerService.cs @@ -70,7 +70,8 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase new TypeUserCommandHandler(AddRemoteContentProvider), new TypeUserCommandHandler(OpenInDefaultFileExplorer), new TypeUserCommandHandler(CopyNativePath), - new TypeUserCommandHandler(CopyBase64), + //TODO: this should rather base hash instead of base64 + //new TypeUserCommandHandler(CopyBase64), new TypeUserCommandHandler(Edit), new TypeUserCommandHandler(Search), new TypeUserCommandHandler(ScanSize), @@ -92,10 +93,10 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase var path = containerNameInput.Value!; - Func>? connection = null; + Func>? connection = null; if (path.StartsWith("http")) { - connection = async () => await SignalRConnection.GetOrCreateForAsync(path, providerName.Value); + connection = () => SignalRConnection.GetOrCreateForAsync(path, providerName.Value); } if (connection is null) @@ -121,7 +122,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase await _userCommandHandlerService.HandleCommandAsync( new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, remoteContentProvider))); - + await remoteContentProvider.InitializeChildren(); } @@ -192,7 +193,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase await _currentSelectedTab.Tab.Ordering.SetValue(sortItemsCommand.Ordering); } - private async Task CopyBase64() + /*private async Task CopyBase64() { var item = _currentSelectedItem?.Value?.BaseItem; if (item?.Type != AbsolutePathType.Element || item is not IElement element) return; @@ -209,7 +210,7 @@ public class ToolUserCommandHandlerService : UserCommandHandlerServiceBase var base64Hash = Convert.ToBase64String(ms.ToArray()); await _systemClipboardService.CopyToClipboardAsync(base64Hash); - } + }*/ private async Task Search(SearchCommand searchCommand) { diff --git a/src/AppCommon/FileTime.App.Core/ViewModels/ItemPreview/ElementPreviewViewModel.cs b/src/AppCommon/FileTime.App.Core/ViewModels/ItemPreview/ElementPreviewViewModel.cs index 92289f5..de4fe44 100644 --- a/src/AppCommon/FileTime.App.Core/ViewModels/ItemPreview/ElementPreviewViewModel.cs +++ b/src/AppCommon/FileTime.App.Core/ViewModels/ItemPreview/ElementPreviewViewModel.cs @@ -49,7 +49,7 @@ public partial class ElementPreviewViewModel : IElementPreviewViewModel, IAsyncI { var readerFactory = _contentAccessorFactory.GetContentReaderFactory(element.Provider); var reader = await readerFactory.CreateContentReaderAsync(element); - await using var inputStream = reader.AsStream(); + await using var inputStream = reader.GetStream(); using var pdfDocument = PdfDocument.Open(inputStream); var contentBuilder = new StringBuilder(); diff --git a/src/Core/FileTime.Core.Abstraction/ContentAccess/ContentAccessStream.cs b/src/Core/FileTime.Core.Abstraction/ContentAccess/ContentAccessStream.cs deleted file mode 100644 index a9efb55..0000000 --- a/src/Core/FileTime.Core.Abstraction/ContentAccess/ContentAccessStream.cs +++ /dev/null @@ -1,80 +0,0 @@ -namespace FileTime.Core.ContentAccess; - -public class ContentAccessStream : Stream -{ - private readonly IContentReader? _contentReader; - private readonly IContentWriter? _contentWriter; - public override bool CanRead => _contentReader != null; - - public override bool CanSeek => _contentReader != null; - - public override bool CanWrite => _contentWriter != null; - - public override long Length => throw new NotImplementedException(); - - public override long Position - { - get => throw new NotImplementedException(); - set => throw new NotImplementedException(); - } - - public ContentAccessStream(IContentReader contentReader) - { - _contentReader = contentReader; - } - - public ContentAccessStream(IContentWriter contentWriter) - { - _contentWriter = contentWriter; - } - - public override void Flush() - { - if (_contentWriter == null) throw new NotSupportedException(); - Task.Run(async () => await _contentWriter.FlushAsync()).Wait(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - if (_contentReader == null) throw new IOException("This stream is not readable"); - var dataTask = Task.Run(async () => await _contentReader.ReadBytesAsync(count, offset)); - dataTask.Wait(); - var data = dataTask.Result; - - if (data.Length > count) throw new Exception("More bytes has been read than requested"); - Array.Copy(data, buffer, data.Length); - return data.Length; - } - - public override long Seek(long offset, SeekOrigin origin) - { - if (_contentReader == null) throw new NotSupportedException(); - - var newPosition = origin switch - { - SeekOrigin.Begin => offset, - SeekOrigin.Current => _contentReader.Position ?? 0 + offset, - _ => throw new NotSupportedException() - }; - _contentReader.SetPosition(newPosition); - return newPosition; - } - - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - if (_contentWriter == null) throw new NotSupportedException(); - var data = buffer; - if (buffer.Length != count) - { - data = new byte[count]; - Array.Copy(buffer, data, count); - } - - Task.Run(async () => await _contentWriter.WriteBytesAsync(data, offset)).Wait(); - } -} \ No newline at end of file diff --git a/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs b/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs index 96262da..ac056ab 100644 --- a/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs +++ b/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs @@ -7,8 +7,6 @@ namespace FileTime.Core.ContentAccess; public interface IContentProvider : IContainer, IOnContainerEnter { - bool SupportsContentStreams { get; } - Task GetItemByFullNameAsync( FullName fullName, PointInTime pointInTime, diff --git a/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentReader.cs b/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentReader.cs index c71a63e..b670d9b 100644 --- a/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentReader.cs +++ b/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentReader.cs @@ -1,11 +1,5 @@ namespace FileTime.Core.ContentAccess; -public interface IContentReader : IDisposable +public interface IContentReader : IStreamContainer { - int PreferredBufferSize { get; } - long? Position { get; } - - Task ReadBytesAsync(int bufferSize, int? offset = null); - void SetPosition(long position); - Stream AsStream(); } \ No newline at end of file diff --git a/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentWriter.cs b/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentWriter.cs index 70bf305..79d482f 100644 --- a/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentWriter.cs +++ b/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentWriter.cs @@ -1,10 +1,5 @@ namespace FileTime.Core.ContentAccess; -public interface IContentWriter : IDisposable +public interface IContentWriter : IStreamContainer { - int PreferredBufferSize { get; } - - Task WriteBytesAsync(byte[] data, int? index = null, CancellationToken cancellationToken = default); - Task FlushAsync(CancellationToken cancellationToken = default); - Stream AsStream(); } \ No newline at end of file diff --git a/src/Core/FileTime.Core.Abstraction/ContentAccess/IStreamContainer.cs b/src/Core/FileTime.Core.Abstraction/ContentAccess/IStreamContainer.cs new file mode 100644 index 0000000..28d0d89 --- /dev/null +++ b/src/Core/FileTime.Core.Abstraction/ContentAccess/IStreamContainer.cs @@ -0,0 +1,6 @@ +namespace FileTime.Core.ContentAccess; + +public interface IStreamContainer : IDisposable +{ + Stream GetStream(); +} \ No newline at end of file diff --git a/src/Core/FileTime.Core.CommandHandlers/StreamCopyCommandHandler.cs b/src/Core/FileTime.Core.CommandHandlers/StreamCopyCommandHandler.cs index 0a908c4..81a2f19 100644 --- a/src/Core/FileTime.Core.CommandHandlers/StreamCopyCommandHandler.cs +++ b/src/Core/FileTime.Core.CommandHandlers/StreamCopyCommandHandler.cs @@ -7,44 +7,14 @@ namespace FileTime.Core.CommandHandlers; public class StreamCopyCommandHandler : ICommandHandler { - private readonly IContentProviderRegistry _contentProviderRegistry; private readonly IContentAccessorFactory _contentAccessorFactory; - public StreamCopyCommandHandler( - IContentProviderRegistry contentProviderRegistry, - IContentAccessorFactory contentAccessorFactory) + public StreamCopyCommandHandler(IContentAccessorFactory contentAccessorFactory) { - _contentProviderRegistry = contentProviderRegistry; _contentAccessorFactory = contentAccessorFactory; } - public async Task CanHandleAsync(ICommand command) - { - if (command is not CopyCommand copyCommand) return false; - - var targetSupportsContentStream = - (await _contentProviderRegistry - .ContentProviders - .ToAsyncEnumerable() - .FirstOrDefaultAwaitAsync(async p => await p.CanHandlePathAsync(copyCommand.Target)) - )?.SupportsContentStreams ?? false; - - var allSourcesSupportsContentStream = - (await copyCommand - .Sources - .ToAsyncEnumerable() - .SelectAwait(s => - _contentProviderRegistry - .ContentProviders - .ToAsyncEnumerable() - .FirstOrDefaultAwaitAsync(async p => await p.CanHandlePathAsync(s)) - ) - .ToListAsync() - ) - .All(p => p?.SupportsContentStreams ?? false); - - return targetSupportsContentStream && allSourcesSupportsContentStream; - } + public Task CanHandleAsync(ICommand command) => Task.FromResult(command is CopyCommand); public async Task ExecuteAsync(ICommand command) { @@ -53,7 +23,7 @@ public class StreamCopyCommandHandler : ICommandHandler await copyCommand.ExecuteAsync(CopyElement); } - public async Task CopyElement(AbsolutePath sourcePath, AbsolutePath targetPath, CopyCommandContext copyCommandContext) + private async Task CopyElement(AbsolutePath sourcePath, AbsolutePath targetPath, CopyCommandContext copyCommandContext) { if (copyCommandContext.CancellationToken.IsCancellationRequested) return; @@ -72,25 +42,25 @@ public class StreamCopyCommandHandler : ICommandHandler using var reader = await _contentAccessorFactory.GetContentReaderFactory(source.Provider).CreateContentReaderAsync(source); using var writer = await _contentAccessorFactory.GetContentWriterFactory(target.Provider).CreateContentWriterAsync(target); - byte[] dataRead; + var readerStream = reader.GetStream(); + var writerStream = writer.GetStream(); + + var dataRead = new byte[1024 * 1024]; long currentProgress = 0; - do + while (true) { if (copyCommandContext.CancellationToken.IsCancellationRequested) return; - dataRead = await reader.ReadBytesAsync(writer.PreferredBufferSize); - if (dataRead.Length > 0) - { - await writer.WriteBytesAsync(dataRead, cancellationToken: copyCommandContext.CancellationToken); - await writer.FlushAsync(copyCommandContext.CancellationToken); - currentProgress += dataRead.LongLength; - if (copyCommandContext.CurrentProgress is not null) - { - copyCommandContext.CurrentProgress.SetProgressSafe(currentProgress); - } + var readLength = await readerStream.ReadAsync(dataRead); + var actualDataRead = dataRead[..readLength]; + if (actualDataRead.Length == 0) break; - await copyCommandContext.UpdateProgressAsync(); - } - } while (dataRead.Length > 0); + await writerStream.WriteAsync(actualDataRead, cancellationToken: copyCommandContext.CancellationToken); + await writerStream.FlushAsync(copyCommandContext.CancellationToken); + currentProgress += actualDataRead.LongLength; + copyCommandContext.CurrentProgress?.SetProgressSafe(currentProgress); + + await copyCommandContext.UpdateProgressAsync(); + } } } \ No newline at end of file diff --git a/src/Providers/FileTime.Providers.Local/LocalContentReader.cs b/src/Providers/FileTime.Providers.Local/LocalContentReader.cs index fa2dfe7..839d7ea 100644 --- a/src/Providers/FileTime.Providers.Local/LocalContentReader.cs +++ b/src/Providers/FileTime.Providers.Local/LocalContentReader.cs @@ -5,44 +5,14 @@ namespace FileTime.Providers.Local; public class LocalContentReader : IContentReader { private readonly FileStream _readerStream; - private readonly BinaryReader _binaryReader; private bool _disposed; - public int PreferredBufferSize => 1024 * 1024; - public long? Position { get; private set; } - public LocalContentReader(FileStream readerStream) { _readerStream = readerStream; - _binaryReader = new BinaryReader(_readerStream); } - public Task ReadBytesAsync(int bufferSize, int? offset = null) - { - var max = bufferSize > 0 && bufferSize < PreferredBufferSize ? bufferSize : PreferredBufferSize; - - if (offset != null) - { - if (Position == null) Position = 0; - var buffer = new byte[max]; - var bytesRead = _binaryReader.Read(buffer, offset.Value, max); - Position += bytesRead; - - if (buffer.Length != bytesRead) - { - Array.Resize(ref buffer, bytesRead); - } - return Task.FromResult(buffer); - } - else - { - return Task.FromResult(_binaryReader.ReadBytes(max)); - } - } - - public void SetPosition(long position) => Position = position; - - public Stream AsStream() => _binaryReader.BaseStream; + public Stream GetStream() => _readerStream; ~LocalContentReader() { @@ -62,7 +32,6 @@ public class LocalContentReader : IContentReader if (disposing) { _readerStream.Dispose(); - _binaryReader.Dispose(); } } _disposed = true; diff --git a/src/Providers/FileTime.Providers.Local/LocalContentWriter.cs b/src/Providers/FileTime.Providers.Local/LocalContentWriter.cs index b3196a0..466b0269 100644 --- a/src/Providers/FileTime.Providers.Local/LocalContentWriter.cs +++ b/src/Providers/FileTime.Providers.Local/LocalContentWriter.cs @@ -5,36 +5,14 @@ namespace FileTime.Providers.Local; public class LocalContentWriter : IContentWriter { private readonly FileStream _writerStream; - private readonly BinaryWriter _binaryWriter; private bool _disposed; - public int PreferredBufferSize => 1024 * 1024; public LocalContentWriter(FileStream writerStream) { _writerStream = writerStream; - _binaryWriter = new BinaryWriter(_writerStream); } - public Task WriteBytesAsync(byte[] data, int? index = null, CancellationToken cancellationToken = default) - { - if (index != null) - { - _binaryWriter.Write(data, index.Value, data.Length); - } - else - { - _binaryWriter.Write(data); - } - return Task.CompletedTask; - } - - public Task FlushAsync(CancellationToken cancellationToken = default) - { - _binaryWriter.Flush(); - return Task.CompletedTask; - } - - public Stream AsStream() => _binaryWriter.BaseStream; + public Stream GetStream() => _writerStream; ~LocalContentWriter() { @@ -54,7 +32,6 @@ public class LocalContentWriter : IContentWriter if (disposing) { _writerStream.Dispose(); - _binaryWriter.Dispose(); } } _disposed = true; diff --git a/src/Providers/FileTime.Providers.Remote.Abstractions/IRemoteContentProvider.cs b/src/Providers/FileTime.Providers.Remote.Abstractions/IRemoteContentProvider.cs index 2f3b9dc..b3e380c 100644 --- a/src/Providers/FileTime.Providers.Remote.Abstractions/IRemoteContentProvider.cs +++ b/src/Providers/FileTime.Providers.Remote.Abstractions/IRemoteContentProvider.cs @@ -5,7 +5,7 @@ namespace FileTime.Providers.Remote; public interface IRemoteContentProvider : IContentProvider { - Task GetRemoteConnectionAsync(); + ValueTask GetRemoteConnectionAsync(); string RemoteProviderName { get; } Task InitializeChildren(); } \ No newline at end of file diff --git a/src/Providers/FileTime.Providers.Remote/ProxyStream.cs b/src/Providers/FileTime.Providers.Remote/ProxyStream.cs new file mode 100644 index 0000000..e1d04e3 --- /dev/null +++ b/src/Providers/FileTime.Providers.Remote/ProxyStream.cs @@ -0,0 +1,58 @@ +namespace FileTime.Providers.Remote; + +internal sealed class ProxyStream : Stream +{ + private readonly RemoteContentWriter _remoteContentWriter; + + public ProxyStream(RemoteContentWriter remoteContentWriter) + { + _remoteContentWriter = remoteContentWriter; + } + + public override void Flush() => InitializeRemoteWriterAndRun(() => _remoteContentWriter.FlushAsync()); + + public override int Read(byte[] buffer, int offset, int count) + => InitializeRemoteWriterAndRun(() => _remoteContentWriter.ReadAsync(buffer, offset, count)); + + public override long Seek(long offset, SeekOrigin origin) + => InitializeRemoteWriterAndRun(() => _remoteContentWriter.SeekAsync(offset, origin)); + + public override void SetLength(long value) => InitializeRemoteWriterAndRun(() => _remoteContentWriter.SetLengthAsync(value)); + + public override void Write(byte[] buffer, int offset, int count) => InitializeRemoteWriterAndRun(() => _remoteContentWriter.WriteAsync(buffer, offset, count)); + + public override bool CanRead => InitializeRemoteWriterAndRun(() => _remoteContentWriter.CanReadAsync()); + + public override bool CanSeek => InitializeRemoteWriterAndRun(() => _remoteContentWriter.CanSeekAsync()); + + public override bool CanWrite => InitializeRemoteWriterAndRun(() => _remoteContentWriter.CanWriteAsync()); + + public override long Length => InitializeRemoteWriterAndRun(() => _remoteContentWriter.GetLengthAsync()); + + public override long Position + { + get => InitializeRemoteWriterAndRun(() => _remoteContentWriter.GetPositionAsync()); + set => InitializeRemoteWriterAndRun(() => _remoteContentWriter.SetPositionAsync(value)); + } + + private void InitializeRemoteWriterAndRun(Func func) + { + var task = Task.Run(async () => + { + await _remoteContentWriter.InitializeRemoteWriterAsync(); + await func(); + }); + task.Wait(); + } + + private T InitializeRemoteWriterAndRun(Func> func) + { + var task = Task.Run(async () => + { + await _remoteContentWriter.InitializeRemoteWriterAsync(); + return await func(); + }); + task.Wait(); + return task.Result; + } +} \ No newline at end of file diff --git a/src/Providers/FileTime.Providers.Remote/RemoteContentProvider.cs b/src/Providers/FileTime.Providers.Remote/RemoteContentProvider.cs index f8ca943..8ea01f0 100644 --- a/src/Providers/FileTime.Providers.Remote/RemoteContentProvider.cs +++ b/src/Providers/FileTime.Providers.Remote/RemoteContentProvider.cs @@ -11,7 +11,7 @@ namespace FileTime.Providers.Remote; public sealed class RemoteContentProvider : ContentProviderBase, IRemoteContentProvider { private readonly IServiceProvider _serviceProvider; - private readonly Func> _remoteConnectionProvider; + private readonly Func> _remoteConnectionProvider; private readonly SemaphoreSlim _initializeSemaphore = new(1, 1); private bool _initialized; @@ -20,7 +20,7 @@ public sealed class RemoteContentProvider : ContentProviderBase, IRemoteContentP public RemoteContentProvider( ITimelessContentProvider timelessContentProvider, IServiceProvider serviceProvider, - Func> remoteConnectionProvider, + Func> remoteConnectionProvider, string remoteName, string name) : base(name, timelessContentProvider) @@ -30,8 +30,7 @@ public sealed class RemoteContentProvider : ContentProviderBase, IRemoteContentP _remoteConnectionProvider = remoteConnectionProvider; } - public async Task GetRemoteConnectionAsync() - => await _remoteConnectionProvider(); + public ValueTask GetRemoteConnectionAsync() => _remoteConnectionProvider(); public async Task InitializeChildren() { diff --git a/src/Providers/FileTime.Providers.Remote/RemoteContentWriter.cs b/src/Providers/FileTime.Providers.Remote/RemoteContentWriter.cs index 256b811..9c7e232 100644 --- a/src/Providers/FileTime.Providers.Remote/RemoteContentWriter.cs +++ b/src/Providers/FileTime.Providers.Remote/RemoteContentWriter.cs @@ -30,25 +30,78 @@ public class RemoteContentWriter : IContentWriter, IInitable await (await _remoteContentProvider.GetRemoteConnectionAsync()).CloseWriterAsync(_transactionId)); } - public int PreferredBufferSize => 10 * 1024 * 1024; + public Stream GetStream() => new ProxyStream(this); - public async Task WriteBytesAsync(byte[] data, int? index = null, CancellationToken cancellationToken = default) - { - if (!_isRemoteWriterInitialized) await InitializeRemoteWriter(_nativePath); - await (await _remoteContentProvider.GetRemoteConnectionAsync()).WriteBytesAsync(_transactionId, data, index, cancellationToken); - } - - public async Task FlushAsync(CancellationToken cancellationToken = default) - { - if (!_isRemoteWriterInitialized) return; - await (await _remoteContentProvider.GetRemoteConnectionAsync()).FlushWriterAsync(_transactionId, cancellationToken); - } - - public Stream AsStream() => new ContentAccessStream(this); - - private async Task InitializeRemoteWriter(NativePath nativePath) + public async Task InitializeRemoteWriterAsync() { + if (_isRemoteWriterInitialized) return; _isRemoteWriterInitialized = true; - await (await _remoteContentProvider.GetRemoteConnectionAsync()).InitializeRemoteWriter(_remoteContentProviderId, _transactionId, nativePath); + await (await _remoteContentProvider.GetRemoteConnectionAsync()).InitializeRemoteWriter(_remoteContentProviderId, _transactionId, _nativePath); + } + + public async Task FlushAsync() + { + await InitializeRemoteWriterAsync(); + await (await _remoteContentProvider.GetRemoteConnectionAsync()).FlushAsync(_transactionId); + } + + public async Task ReadAsync(byte[] buffer, int offset, int count) + { + await InitializeRemoteWriterAsync(); + return await (await _remoteContentProvider.GetRemoteConnectionAsync()).ReadAsync(_transactionId, buffer, offset, count); + } + + public async Task SeekAsync(long offset, SeekOrigin origin) + { + await InitializeRemoteWriterAsync(); + return await (await _remoteContentProvider.GetRemoteConnectionAsync()).SeekAsync(_transactionId, offset, origin); + } + + public async Task SetLengthAsync(long value) + { + await InitializeRemoteWriterAsync(); + await (await _remoteContentProvider.GetRemoteConnectionAsync()).SetLengthAsync(_transactionId, value); + } + + public async Task WriteAsync(byte[] buffer, int offset, int count) + { + await InitializeRemoteWriterAsync(); + await (await _remoteContentProvider.GetRemoteConnectionAsync()).WriteAsync(_transactionId, buffer, offset, count); + } + + public async Task CanReadAsync() + { + await InitializeRemoteWriterAsync(); + return await (await _remoteContentProvider.GetRemoteConnectionAsync()).CanReadAsync(_transactionId); + } + + public async Task CanSeekAsync() + { + await InitializeRemoteWriterAsync(); + return await (await _remoteContentProvider.GetRemoteConnectionAsync()).CanSeekAsync(_transactionId); + } + + public async Task CanWriteAsync() + { + await InitializeRemoteWriterAsync(); + return await (await _remoteContentProvider.GetRemoteConnectionAsync()).CanWriteAsync(_transactionId); + } + + public async Task GetLengthAsync() + { + await InitializeRemoteWriterAsync(); + return await (await _remoteContentProvider.GetRemoteConnectionAsync()).GetLengthAsync(_transactionId); + } + + public async Task GetPositionAsync() + { + await InitializeRemoteWriterAsync(); + return await (await _remoteContentProvider.GetRemoteConnectionAsync()).GetPositionAsync(_transactionId); + } + + public async Task SetPositionAsync(long position) + { + await InitializeRemoteWriterAsync(); + await (await _remoteContentProvider.GetRemoteConnectionAsync()).SetPositionAsync(_transactionId, position); } } \ No newline at end of file diff --git a/src/Server/FileTime.Server.Common.Abstractions/ContentAccess/IContentAccessManager.cs b/src/Server/FileTime.Server.Common.Abstractions/ContentAccess/IContentAccessManager.cs index 6d8d93e..bedf963 100644 --- a/src/Server/FileTime.Server.Common.Abstractions/ContentAccess/IContentAccessManager.cs +++ b/src/Server/FileTime.Server.Common.Abstractions/ContentAccess/IContentAccessManager.cs @@ -4,7 +4,7 @@ namespace FileTime.Server.Common.ContentAccess; public interface IContentAccessManager { - void AddContentWriter(string transactionId, IContentWriter contentWriter); - IContentWriter GetContentWriter(string transactionId); - void RemoveContentWriter(string transactionId); + void AddContentStreamContainer(string transactionId, IStreamContainer streamContainer); + Stream GetContentStream(string transactionId); + void RemoveContentStreamContainer(string transactionId); } \ No newline at end of file diff --git a/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs b/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs index d73b154..b6e9716 100644 --- a/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs +++ b/src/Server/FileTime.Server.Common.Abstractions/IRemoteConnection.cs @@ -13,10 +13,20 @@ public interface IRemoteConnection Task DeleteItemAsync(string contentProviderId, FullName fullName); Task MoveItemAsync(string contentProviderId, FullName fullName, FullName newPath); Task InitializeRemoteWriter(string contentProviderId, string transactionId, NativePath nativePath); - Task WriteBytesAsync(string transactionId, byte[] data, int? index, CancellationToken cancellationToken = default); - Task FlushWriterAsync(string transactionId, CancellationToken cancellationToken = default); Task CloseWriterAsync(string transactionId); Task GetNativePathAsync(string contentProviderId, FullName fullName); + + Task FlushAsync(string transactionId); + Task ReadAsync(string transactionId, byte[] buffer, int offset, int count); + Task SeekAsync(string transactionId, long offset, SeekOrigin origin); + Task SetLengthAsync(string transactionId, long value); + Task WriteAsync(string transactionId, byte[] buffer, int offset, int count); + Task CanReadAsync(string transactionId); + Task CanSeekAsync(string transactionId); + Task CanWriteAsync(string transactionId); + Task GetLengthAsync(string transactionId); + Task GetPositionAsync(string transactionId); + Task SetPositionAsync(string transactionId, long position); Task GetItemByNativePathAsync( string contentProviderId, diff --git a/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs b/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs index 4e8fb0e..27229c5 100644 --- a/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs +++ b/src/Server/FileTime.Server.Common/Connections/SignalR/ISignalRHub.cs @@ -13,15 +13,23 @@ public interface ISignalRHub Task CreateElementAsync(string contentProviderId, string fullName); Task DeleteItemAsync(string contentProviderId, string fullName); Task MoveItemAsync(string contentProviderId, string fullName, string newPath); - - //TODO: CancellationToken https://github.com/nenoNaninu/TypedSignalR.Client/issues/120 - Task FlushWriterAsync(string transactionId); Task InitializeRemoteWriter(string contentProviderId, string transactionId, string nativePath); - //TODO: CancellationToken https://github.com/nenoNaninu/TypedSignalR.Client/issues/120 - Task WriteBytesAsync(string transactionId, string data, int index); Task CloseWriterAsync(string transactionId); Task GetNativePathAsync(string contentProviderId, string fullNamePath); + + Task FlushAsync(string transactionId); + Task ReadAsync(string transactionId, int dataLength); + Task SeekAsync(string transactionId, long offset, SeekOrigin origin); + Task SetLengthAsync(string transactionId, long value); + Task WriteAsync(string transactionId, string data); + Task CanReadAsync(string transactionId); + Task CanSeekAsync(string transactionId); + Task CanWriteAsync(string transactionId); + Task GetLengthAsync(string transactionId); + Task GetPositionAsync(string transactionId); + Task SetPositionAsync(string transactionId, long position); + Task GetItemByNativePathAsync( string contentProviderId, NativePath nativePath, diff --git a/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs b/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs index a0ba9a2..9c279dd 100644 --- a/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs +++ b/src/Server/FileTime.Server.Common/Connections/SignalR/SignalRConnection.cs @@ -32,7 +32,7 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable GetOrCreateForAsync(string baseUrl, string providerName) + public static async ValueTask GetOrCreateForAsync(string baseUrl, string providerName) { SignalRConnection? connection; lock (ConnectionsLock) @@ -70,12 +70,6 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable await _client.MoveItemAsync(contentProviderId, fullName.Path, newPath.Path); - public async Task WriteBytesAsync(string transactionId, byte[] data, int? index, CancellationToken cancellationToken = default) - => await _client.WriteBytesAsync(transactionId, Convert.ToBase64String(data), index ?? -1); - - public async Task FlushWriterAsync(string transactionId, CancellationToken cancellationToken = default) - => await _client.FlushWriterAsync(transactionId); - public async Task InitializeRemoteWriter(string contentProviderId, string transactionId, NativePath nativePath) => await _client.InitializeRemoteWriter(contentProviderId, transactionId, nativePath.Path); @@ -88,6 +82,40 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable _client.FlushAsync(transactionId); + + public async Task ReadAsync(string transactionId, byte[] buffer, int offset, int count) + { + var dataString = await _client.ReadAsync(transactionId, count); + var data = GetDataFromString(dataString); + + data.CopyTo(buffer.AsSpan(offset, data.Length)); + + return data.Length; + } + + public Task SeekAsync(string transactionId, long offset, SeekOrigin origin) => _client.SeekAsync(transactionId, offset, origin); + + public Task SetLengthAsync(string transactionId, long value) => _client.SetLengthAsync(transactionId, value); + + public Task WriteAsync(string transactionId, byte[] buffer, int offset, int count) + { + var data = GetStringFromData(buffer.AsSpan(offset, count)); + return _client.WriteAsync(transactionId, data); + } + + public Task CanReadAsync(string transactionId) => _client.CanReadAsync(transactionId); + + public Task CanSeekAsync(string transactionId) => _client.CanSeekAsync(transactionId); + + public Task CanWriteAsync(string transactionId) => _client.CanWriteAsync(transactionId); + + public Task GetLengthAsync(string transactionId) => _client.GetLengthAsync(transactionId); + + public Task GetPositionAsync(string transactionId) => _client.GetPositionAsync(transactionId); + + public Task SetPositionAsync(string transactionId, long position) => _client.SetPositionAsync(transactionId, position); + public async Task GetItemByNativePathAsync( string contentProviderId, NativePath nativePath, @@ -110,4 +138,7 @@ public class SignalRConnection : IRemoteConnection, IAsyncInitable GetChildren(string contentProviderId, string fullName) => await _client.GetChildren(contentProviderId, fullName); + + private static byte[] GetDataFromString(string data) => Convert.FromBase64String(data); + private static string GetStringFromData(ReadOnlySpan data) => Convert.ToBase64String(data); } \ No newline at end of file diff --git a/src/Server/FileTime.Server.Common/ContentAccess/ContentAccessManager.cs b/src/Server/FileTime.Server.Common/ContentAccess/ContentAccessManager.cs index d3a6f6d..6cc0ac3 100644 --- a/src/Server/FileTime.Server.Common/ContentAccess/ContentAccessManager.cs +++ b/src/Server/FileTime.Server.Common/ContentAccess/ContentAccessManager.cs @@ -4,10 +4,18 @@ namespace FileTime.Server.Common.ContentAccess; public class ContentAccessManager : IContentAccessManager { - private readonly Dictionary _contentWriters = new(); - public void AddContentWriter(string transactionId, IContentWriter contentWriter) - => _contentWriters.Add(transactionId, contentWriter); - - public IContentWriter GetContentWriter(string transactionId) => _contentWriters[transactionId]; - public void RemoveContentWriter(string transactionId) => _contentWriters.Remove(transactionId); + private readonly Dictionary _contentStreamContainers = new(); + + public void AddContentStreamContainer(string transactionId, IStreamContainer streamContainer) + => _contentStreamContainers.Add(transactionId, streamContainer); + + public Stream GetContentStream(string transactionId) => _contentStreamContainers[transactionId].GetStream(); + + public void RemoveContentStreamContainer(string transactionId) + { + if (!_contentStreamContainers.TryGetValue(transactionId, out var streamContainer)) return; + + streamContainer.Dispose(); + _contentStreamContainers.Remove(transactionId); + } } \ No newline at end of file diff --git a/src/Server/FileTime.Server.Web/ConnectionHub.cs b/src/Server/FileTime.Server.Web/ConnectionHub.cs index 252a14f..2a104e5 100644 --- a/src/Server/FileTime.Server.Web/ConnectionHub.cs +++ b/src/Server/FileTime.Server.Web/ConnectionHub.cs @@ -103,20 +103,13 @@ public class ConnectionHub : Hub, ISignalRHub throw new FileNotFoundException("Item is not an element", nativePath); var contentWriter = await _contentAccessorFactory.GetContentWriterFactory(contentProvider).CreateContentWriterAsync(element); - _contentAccessManager.AddContentWriter(transactionId, contentWriter); + _contentAccessManager.AddContentStreamContainer(transactionId, contentWriter); } - public async Task WriteBytesAsync(string transactionId, string data, int index) - => await _contentAccessManager.GetContentWriter(transactionId).WriteBytesAsync(Convert.FromBase64String(data), index == -1 ? null : index); - - public async Task FlushWriterAsync(string transactionId) - => await _contentAccessManager.GetContentWriter(transactionId).FlushAsync(); - - public Task CloseWriterAsync(string transactionId) { - _contentAccessManager.GetContentWriter(transactionId).Dispose(); - _contentAccessManager.RemoveContentWriter(transactionId); + _contentAccessManager.GetContentStream(transactionId).Dispose(); + _contentAccessManager.RemoveContentStreamContainer(transactionId); return Task.CompletedTask; } @@ -167,8 +160,54 @@ public class ConnectionHub : Hub, ISignalRHub throw new NotSupportedException(); } + public async Task FlushAsync(string transactionId) + => await _contentAccessManager.GetContentStream(transactionId).FlushAsync(); + + public async Task ReadAsync(string transactionId, int dataLength) + { + // this might be stack allocated when dataLength is small + var data = new byte[dataLength]; + var dataRead = await _contentAccessManager.GetContentStream(transactionId).ReadAsync(data); + + return GetStringFromData(data.AsSpan()[..dataRead]); + } + + public Task SeekAsync(string transactionId, long offset, SeekOrigin origin) + => Task.FromResult(_contentAccessManager.GetContentStream(transactionId).Seek(offset, origin)); + + public Task SetLengthAsync(string transactionId, long value) + { + _contentAccessManager.GetContentStream(transactionId).SetLength(value); + return Task.CompletedTask; + } + + public async Task WriteAsync(string transactionId, string buffer) + { + var data = GetDataFromString(buffer); + await _contentAccessManager.GetContentStream(transactionId).WriteAsync(data); + } + + public Task CanReadAsync(string transactionId) => Task.FromResult(_contentAccessManager.GetContentStream(transactionId).CanRead); + + public Task CanSeekAsync(string transactionId) => Task.FromResult(_contentAccessManager.GetContentStream(transactionId).CanSeek); + + public Task CanWriteAsync(string transactionId) => Task.FromResult(_contentAccessManager.GetContentStream(transactionId).CanWrite); + + public Task GetLengthAsync(string transactionId) => Task.FromResult(_contentAccessManager.GetContentStream(transactionId).Length); + + public Task GetPositionAsync(string transactionId) => Task.FromResult(_contentAccessManager.GetContentStream(transactionId).Position); + + public Task SetPositionAsync(string transactionId, long position) + { + _contentAccessManager.GetContentStream(transactionId).Position = position; + return Task.CompletedTask; + } + private IContentProvider GetContentProvider(string contentProviderId) => _contentProviderRegistry .ContentProviders .First(p => p.Name == contentProviderId); + + private static byte[] GetDataFromString(string data) => Convert.FromBase64String(data); + private static string GetStringFromData(ReadOnlySpan data) => Convert.ToBase64String(data); } \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.Compression.Core/CompressOperation.cs b/src/Tools/FileTime.Tools.Compression.Core/CompressOperation.cs index 95af719..71bd6e1 100644 --- a/src/Tools/FileTime.Tools.Compression.Core/CompressOperation.cs +++ b/src/Tools/FileTime.Tools.Compression.Core/CompressOperation.cs @@ -27,21 +27,14 @@ public class CompressOperation : ICompressOperation public async Task> CompressElement(IElement element, string key) { - if (element.Provider.SupportsContentStreams) - { - var contentReader = await _contentAccessorFactory.GetContentReaderFactory(element.Provider).CreateContentReaderAsync(element); + var contentReader = await _contentAccessorFactory.GetContentReaderFactory(element.Provider).CreateContentReaderAsync(element); + var contentReaderStream = contentReader.GetStream(); + _archive.AddEntry(key, contentReaderStream); - var contentReaderStream = contentReader.AsStream(); - - _archive.AddEntry(key, contentReaderStream); - - return new IDisposable[] {contentReader, contentReaderStream}; - } - - return Enumerable.Empty(); + return new IDisposable[] {contentReader, contentReaderStream}; } - public void SaveTo(Stream stream) + public void SaveTo(Stream stream) => _saveTo(stream); ~CompressOperation() diff --git a/src/Tools/FileTime.Tools.Compression/Compress/CompressCommand.cs b/src/Tools/FileTime.Tools.Compression/Compress/CompressCommand.cs index 58e6902..994b27a 100644 --- a/src/Tools/FileTime.Tools.Compression/Compress/CompressCommand.cs +++ b/src/Tools/FileTime.Tools.Compression/Compress/CompressCommand.cs @@ -113,7 +113,7 @@ public class CompressCommand : CommandBase, IExecutableCommand, ITransportationC await _contentAccessorFactory.GetItemCreator(resolvedParent.Provider).CreateElementAsync(resolvedParent.Provider, newItemName); var targetElement = (IElement) await _timelessContentProvider.GetItemByFullNameAsync(newItemName, PointInTime.Present); using var contentWriter = await _contentAccessorFactory.GetContentWriterFactory(resolvedParent.Provider).CreateContentWriterAsync(targetElement); - await using var contentWriterStream = contentWriter.AsStream(); + await using var contentWriterStream = contentWriter.GetStream(); compressOperation.SaveTo(contentWriterStream); await contentWriterStream.FlushAsync(_cancellationTokenSource.Token); diff --git a/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProvider.cs b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProvider.cs index 6caf3d4..ca4cbfe 100644 --- a/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProvider.cs +++ b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProvider.cs @@ -29,7 +29,7 @@ public sealed class CompressedContentProvider : SubContentProviderBase, ICompres var reader = parentElementContext.ContentReader; var subPath = parentElementContext.SubNativePath.Path; - await using var readerStream = reader.AsStream(); + await using var readerStream = reader.GetStream(); using var archive = ArchiveFactory.Open(readerStream); var entry = archive.Entries.First(e => e.Key == subPath); diff --git a/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentReader.cs b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentReader.cs index 66cba3a..698de9a 100644 --- a/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentReader.cs +++ b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentReader.cs @@ -8,9 +8,6 @@ public sealed class CompressedContentReader : IContentReader private readonly IDisposable[] _disposables; private readonly Stream _stream; - public int PreferredBufferSize => 1024 * 1024; - public long? Position => _stream.Position; - public CompressedContentReader(IArchiveEntry entry, IDisposable[] disposables) { _disposables = disposables; @@ -26,15 +23,5 @@ public sealed class CompressedContentReader : IContentReader } } - public async Task ReadBytesAsync(int bufferSize, int? offset = null) - { - var data = new byte[bufferSize]; - var read = await _stream.ReadAsync(data, offset ?? 0, bufferSize); - - return data[..read].ToArray(); - } - - public void SetPosition(long position) => _stream.Seek(position, SeekOrigin.Begin); - - public Stream AsStream() => _stream; + public Stream GetStream() => _stream; } \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentReaderFactory.cs b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentReaderFactory.cs index 1ef0ede..984f666 100644 --- a/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentReaderFactory.cs +++ b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentReaderFactory.cs @@ -22,7 +22,7 @@ public sealed class CompressedContentReaderFactory : SubContentReaderBase e.Key == subPath.Path); diff --git a/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedSubContentProvider.cs b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedSubContentProvider.cs index c314722..0c9f43e 100644 --- a/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedSubContentProvider.cs +++ b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedSubContentProvider.cs @@ -41,7 +41,7 @@ public sealed class CompressedSubContentProvider : ICompressedSubContentProvider ItemInitializationSettings itemInitializationSettings = default) { var parentContentReader = await _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider).CreateContentReaderAsync(parentElement); - var parentContentReaderStream = parentContentReader.AsStream(); + var parentContentReaderStream = parentContentReader.GetStream(); var archive = ArchiveFactory.Open(parentContentReaderStream); var disposables = new IDisposable[] {parentContentReader, parentContentReaderStream, archive}; diff --git a/src/Tools/FileTime.Tools.Compression/Decompress/DecompressCommand.cs b/src/Tools/FileTime.Tools.Compression/Decompress/DecompressCommand.cs index 7e5d975..8e1d788 100644 --- a/src/Tools/FileTime.Tools.Compression/Decompress/DecompressCommand.cs +++ b/src/Tools/FileTime.Tools.Compression/Decompress/DecompressCommand.cs @@ -95,7 +95,7 @@ public class DecompressCommand : CommandBase, IExecutableCommand, ITransportatio var newItem = (IElement) await _timelessContentProvider.GetItemByFullNameAsync(entryPath, PointInTime.Present); using var writer = await contentWriterFactory.CreateContentWriterAsync(newItem); - archiveEntry.WriteTo(writer.AsStream()); + archiveEntry.WriteTo(writer.GetStream()); } } } @@ -107,7 +107,7 @@ public class DecompressCommand : CommandBase, IExecutableCommand, ITransportatio { var targetElement = (IElement) await _timelessContentProvider.GetItemByFullNameAsync(source, PointInTime.Present); var contentReader = await _contentAccessorFactory.GetContentReaderFactory(targetElement.Provider).CreateContentReaderAsync(targetElement); - var contentReaderStream = contentReader.AsStream(); + var contentReaderStream = contentReader.GetStream(); _disposables.Add(contentReader); using var archive = ArchiveFactory.Open(contentReaderStream); diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProvider.cs b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProvider.cs index c2e354f..db61880 100644 --- a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProvider.cs +++ b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProvider.cs @@ -29,7 +29,7 @@ public sealed class VirtualDiskContentProvider : SubContentProviderBase, IVirtua var reader = parentElementContext.ContentReader; var subPath = parentElementContext.SubNativePath.Path; - await using var readerStream = reader.AsStream(); + await using var readerStream = reader.GetStream(); using var discReader = new UdfReader(readerStream); var fileInfo = discReader.GetFileInfo(subPath); diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReader.cs b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReader.cs index 684db96..fe9b94c 100644 --- a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReader.cs +++ b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReader.cs @@ -6,8 +6,6 @@ public sealed class VirtualDiskContentReader : IContentReader { private readonly Stream _stream; private readonly ICollection _disposables; - public int PreferredBufferSize => 1024 * 1024; - public long? Position => _stream.Position; public VirtualDiskContentReader(Stream stream, ICollection disposables) { @@ -15,18 +13,7 @@ public sealed class VirtualDiskContentReader : IContentReader _disposables = disposables; } - public async Task ReadBytesAsync(int bufferSize, int? offset = null) - { - var data = new byte[bufferSize]; - var read = await _stream.ReadAsync(data, offset ?? 0, bufferSize); - - return data[..read].ToArray(); - } - - public void SetPosition(long position) => _stream.Seek(position, SeekOrigin.Begin); - - public Stream AsStream() => _stream; - + public Stream GetStream() => _stream; public void Dispose() { diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReaderFactory.cs b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReaderFactory.cs index 30a602d..4ceefe8 100644 --- a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReaderFactory.cs +++ b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReaderFactory.cs @@ -22,7 +22,7 @@ public sealed class VirtualDiskContentReaderFactory : SubContentReaderBase