163 lines
4.9 KiB
C#
163 lines
4.9 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Flawless.Core.BinaryDataFormat;
|
|
using Flawless.Core.Modal;
|
|
|
|
namespace Flawless.Client.Service;
|
|
|
|
public class RepositoryFileTreeAccessor : IDisposable, IAsyncDisposable, IEnumerable<WorkspaceFile>
|
|
{
|
|
|
|
private struct FileReadInfoCache
|
|
{
|
|
public readonly Guid DepotId { get; init; }
|
|
|
|
public readonly DepotFileInfo FileInfo { get; init; }
|
|
}
|
|
|
|
private readonly Dictionary<Guid, Stream> _mappings;
|
|
|
|
private readonly CommitManifest _manifest;
|
|
|
|
private readonly Dictionary<Guid, StandardDepotHeaderV1> _headers;
|
|
|
|
private readonly Dictionary<string, FileReadInfoCache> _cached;
|
|
|
|
private object _readonlyLock = new(); // todo support async method.
|
|
|
|
private bool _disposed;
|
|
|
|
public CommitManifest Manifest => _manifest;
|
|
|
|
public bool IsCached { get; private set; }
|
|
|
|
public RepositoryFileTreeAccessor(Dictionary<Guid, Stream> currentReadFs, CommitManifest manifest)
|
|
{
|
|
IsCached = false;
|
|
_mappings = currentReadFs;
|
|
_manifest = manifest;
|
|
_headers = new Dictionary<Guid, StandardDepotHeaderV1>();
|
|
_cached = new Dictionary<string, FileReadInfoCache>();
|
|
_disposed = false;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (_disposed) return;
|
|
_disposed = true;
|
|
|
|
foreach (var mv in _mappings.Values)
|
|
{
|
|
try { mv.Dispose(); }
|
|
catch (Exception e) { Console.WriteLine(e); }
|
|
}
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (_disposed) return;
|
|
_disposed = true;
|
|
|
|
foreach (var mv in _mappings.Values)
|
|
{
|
|
try { await mv.DisposeAsync(); }
|
|
catch (Exception e) { Console.WriteLine(e); }
|
|
}
|
|
}
|
|
|
|
public async Task CreateCacheAsync()
|
|
{
|
|
DisposeCheck();
|
|
lock (_readonlyLock) if (IsCached) return;
|
|
|
|
var ms = _manifest.FilePaths.ToImmutableSortedDictionary(x => x.WorkPath, x => x.ModifyTime);
|
|
|
|
foreach (var (id, stream) in _mappings)
|
|
{
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
var header = DataTransformer.ExtractStandardDepotHeaderV1(stream);
|
|
_headers.Add(id, header);
|
|
await foreach (var inf in DataTransformer.ExtractDepotFileInfoMapAsync(stream, header.FileMapSize))
|
|
{
|
|
if (ms.TryGetValue(inf.Path, out var targetTime) && targetTime == inf.ModifyTime)
|
|
{
|
|
_cached.Add(inf.Path, new FileReadInfoCache { DepotId = id, FileInfo = inf });
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ms.Count != _cached.Count)
|
|
throw new FileNotFoundException("Some of those files are not able to be fount in any depots!");
|
|
|
|
lock (_readonlyLock) IsCached = true;
|
|
}
|
|
|
|
public bool TryGetFileInfo(string workPath, out DepotFileInfo info)
|
|
{
|
|
DisposeCheck();
|
|
lock (_readonlyLock) if (!IsCached) throw new InvalidOperationException("Not cached!");
|
|
|
|
if (_cached.TryGetValue(workPath, out var r))
|
|
{
|
|
info = r.FileInfo;
|
|
return true;
|
|
}
|
|
|
|
info = default;
|
|
return false;
|
|
}
|
|
|
|
public bool TryWriteDataIntoStream(string workPath, Stream stream, out DateTime modifyTime)
|
|
{
|
|
DisposeCheck();
|
|
if (stream == null || !stream.CanWrite) throw new ArgumentException("Stream is not writable!");
|
|
|
|
if (_cached.TryGetValue(workPath, out var r))
|
|
{
|
|
var baseStream = DataTransformer.ExtractStandardDepotFile(_mappings[r.DepotId], r.FileInfo);
|
|
baseStream.CopyTo(stream);
|
|
modifyTime = r.FileInfo.ModifyTime;
|
|
return true;
|
|
}
|
|
|
|
modifyTime = DateTime.MinValue;
|
|
return false;
|
|
}
|
|
|
|
public bool TryWriteDataIntoStream(string workPath, string destinationPath)
|
|
{
|
|
DisposeCheck();
|
|
|
|
if (_cached.TryGetValue(workPath, out var r))
|
|
{
|
|
using var ws = new FileStream(destinationPath, FileMode.OpenOrCreate, FileAccess.Write);
|
|
var baseStream = DataTransformer.ExtractStandardDepotFile(_mappings[r.DepotId], r.FileInfo);
|
|
baseStream.CopyTo(ws);
|
|
File.SetLastWriteTimeUtc(destinationPath, r.FileInfo.ModifyTime);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void DisposeCheck()
|
|
{
|
|
if (_disposed) throw new ObjectDisposedException("Accessor has already been disposed");
|
|
}
|
|
|
|
public IEnumerator<WorkspaceFile> GetEnumerator()
|
|
{
|
|
return _cached.Values
|
|
.Select(cv => new WorkspaceFile(cv.FileInfo.ModifyTime, cv.FileInfo.Path)).GetEnumerator();
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
} |