Content Reader/Writer, StreamCopyCommandHandler

This commit is contained in:
2022-06-09 06:59:53 +02:00
parent e947282d7b
commit 6d9bf7ab32
27 changed files with 494 additions and 27 deletions

View File

@@ -2,14 +2,13 @@ using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Runtime.InteropServices;
using DynamicData;
using FileTime.App.Core.Models;
using FileTime.Core.ContentAccess;
using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Models.Extensions;
using FileTime.Core.Timeline;
namespace FileTime.Providers.Local;
public sealed partial class LocalContentProvider : ContentProviderBase, ILocalContentProvider
{
private readonly ITimelessContentProvider _timelessContentProvider;
@@ -21,6 +20,8 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
_timelessContentProvider = timelessContentProvider;
_isCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
SupportsContentStreams = true;
RefreshRootDirectories();
Items.OnNext(_rootDirectories.Connect());
@@ -50,11 +51,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
{
var rootDrive = _rootDirectories
.Items
.FirstOrDefault(r =>
.FirstOrDefault(r =>
path.Path.StartsWith(
GetNativePath(r.Path).Path,
_isCaseInsensitive
? StringComparison.InvariantCultureIgnoreCase
GetNativePath(r.Path).Path,
_isCaseInsensitive
? StringComparison.InvariantCultureIgnoreCase
: StringComparison.InvariantCulture
)
);
@@ -74,11 +75,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
{
if ((path?.Length ?? 0) == 0)
{
return Task.FromResult((IItem) this);
return Task.FromResult((IItem)this);
}
else if (Directory.Exists(path))
{
return Task.FromResult((IItem) DirectoryToContainer(
return Task.FromResult((IItem)DirectoryToContainer(
new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar),
pointInTime,
!itemInitializationSettings.SkipChildInitialization)
@@ -86,7 +87,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
}
else if (File.Exists(path))
{
return Task.FromResult((IItem) FileToElement(new FileInfo(path), pointInTime));
return Task.FromResult((IItem)FileToElement(new FileInfo(path), pointInTime));
}
var type = forceResolvePathType switch
@@ -118,10 +119,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
return forceResolvePathType switch
{
AbsolutePathType.Container => Task.FromResult(
(IItem) CreateEmptyContainer(
(IItem)CreateEmptyContainer(
nativePath,
pointInTime,
Observable.Return(new List<Exception>() {innerException})
Observable.Return(new List<Exception>() { innerException })
)
),
AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)),
@@ -236,7 +237,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
SourceCache<AbsolutePath, string>? result = null;
try
{
var items = initializeChildren ? (List<AbsolutePath>?) GetItemsByContainer(directoryInfo, pointInTime) : null;
var items = initializeChildren ? (List<AbsolutePath>?)GetItemsByContainer(directoryInfo, pointInTime) : null;
if (items != null)
{
result = new SourceCache<AbsolutePath, string>(i => i.Path.Path);
@@ -245,7 +246,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
}
catch (Exception e)
{
exceptions.OnNext(new List<Exception>() {e});
exceptions.OnNext(new List<Exception>() { e });
}
return Task.FromResult(result?.Connect());
@@ -319,10 +320,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
var size = maxLength ?? realFileSize switch
{
> int.MaxValue => int.MaxValue,
_ => (int) realFileSize
_ => (int)realFileSize
};
var buffer = new byte[size];
await reader.ReadAsync(buffer, 0, size);
await reader.ReadAsync(buffer.AsMemory(0, size), cancellationToken);
return buffer;
}

View File

@@ -0,0 +1,71 @@
using FileTime.Core.ContentAccess;
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<byte[]> 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;
}
~LocalContentReader()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_readerStream.Dispose();
_binaryReader.Dispose();
}
}
_disposed = true;
}
}

View File

@@ -0,0 +1,10 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
namespace FileTime.Providers.Local;
public class LocalContentReaderFactory : IContentReaderFactory<ILocalContentProvider>
{
public Task<IContentReader> CreateContentReaderAsync(IElement element)
=> Task.FromResult((IContentReader)new LocalContentReader(File.OpenRead(element.NativePath!.Path)));
}

View File

@@ -0,0 +1,60 @@
using FileTime.Core.ContentAccess;
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)
{
if (index != null)
{
_binaryWriter.Write(data, index.Value, data.Length);
}
else
{
_binaryWriter.Write(data);
}
return Task.CompletedTask;
}
public Task FlushAsync()
{
_binaryWriter.Flush();
return Task.CompletedTask;
}
~LocalContentWriter()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
_writerStream.Dispose();
_binaryWriter.Dispose();
}
}
disposed = true;
}
}

View File

@@ -0,0 +1,10 @@
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
namespace FileTime.Providers.Local;
public class LocalContentWriterFactory : IContentWriterFactory<ILocalContentProvider>
{
public Task<IContentWriter> CreateContentWriterAsync(IElement element)
=> Task.FromResult((IContentWriter)new LocalContentWriter(File.OpenRead(element.NativePath!.Path)));
}

View File

@@ -12,6 +12,10 @@ public static class Startup
serviceCollection.TryAddSingleton<IContentProvider>(sp => sp.GetRequiredService<ILocalContentProvider>());
serviceCollection.TryAddSingleton<IItemCreator<ILocalContentProvider>, LocalItemCreator>();
serviceCollection.TryAddSingleton<IItemCreator<LocalContentProvider>>(sp => sp.GetRequiredService<IItemCreator<ILocalContentProvider>>());
serviceCollection.TryAddSingleton<IContentReaderFactory<ILocalContentProvider>, LocalContentReaderFactory>();
serviceCollection.TryAddSingleton<IContentReaderFactory<LocalContentProvider>>(sp => sp.GetRequiredService<IContentReaderFactory<ILocalContentProvider>>());
serviceCollection.TryAddSingleton<IContentWriterFactory<ILocalContentProvider>, LocalContentWriterFactory>();
serviceCollection.TryAddSingleton<IContentWriterFactory<LocalContentProvider>>(sp => sp.GetRequiredService<IContentWriterFactory<ILocalContentProvider>>());
return serviceCollection;
}
}