Remove stream-like functionality from ContentReader/Writer

Use streams instead
This commit is contained in:
2023-09-15 21:46:17 +02:00
parent f8d759f044
commit d8c9929a97
31 changed files with 313 additions and 310 deletions

View File

@@ -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();
}
}

View File

@@ -7,8 +7,6 @@ namespace FileTime.Core.ContentAccess;
public interface IContentProvider : IContainer, IOnContainerEnter
{
bool SupportsContentStreams { get; }
Task<IItem> GetItemByFullNameAsync(
FullName fullName,
PointInTime pointInTime,

View File

@@ -1,11 +1,5 @@
namespace FileTime.Core.ContentAccess;
public interface IContentReader : IDisposable
public interface IContentReader : IStreamContainer
{
int PreferredBufferSize { get; }
long? Position { get; }
Task<byte[]> ReadBytesAsync(int bufferSize, int? offset = null);
void SetPosition(long position);
Stream AsStream();
}

View File

@@ -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();
}

View File

@@ -0,0 +1,6 @@
namespace FileTime.Core.ContentAccess;
public interface IStreamContainer : IDisposable
{
Stream GetStream();
}

View File

@@ -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<bool> 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<bool> 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();
}
}
}