Speed up decompression
This commit is contained in:
@@ -90,6 +90,26 @@ public sealed class FileCompactor
|
|||||||
CompactFile(filePath);
|
CompactFile(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RenameAndCompact(string filePath, string originalFilePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.Move(originalFilePath, filePath);
|
||||||
|
}
|
||||||
|
catch (IOException)
|
||||||
|
{
|
||||||
|
// File already exists
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_dalamudUtilService.IsWine || !_mareConfigService.Current.UseCompactor)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompactFile(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
[DllImport("kernel32.dll")]
|
||||||
private static extern int DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, out IntPtr lpBytesReturned, out IntPtr lpOverlapped);
|
private static extern int DeviceIoControl(IntPtr hDevice, uint dwIoControlCode, IntPtr lpInBuffer, uint nInBufferSize, IntPtr lpOutBuffer, uint nOutBufferSize, out IntPtr lpBytesReturned, out IntPtr lpOverlapped);
|
||||||
|
|
||||||
|
|||||||
@@ -305,6 +305,8 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
FileStream? fileBlockStream = null;
|
FileStream? fileBlockStream = null;
|
||||||
|
var threadCount = Math.Clamp((int)(Environment.ProcessorCount / 2.0f), 2, 8);
|
||||||
|
var tasks = new List<Task>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_downloadStatus.TryGetValue(fileGroup.Key, out var status))
|
if (_downloadStatus.TryGetValue(fileGroup.Key, out var status))
|
||||||
@@ -316,28 +318,49 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
while (fileBlockStream.Position < fileBlockStream.Length)
|
while (fileBlockStream.Position < fileBlockStream.Length)
|
||||||
{
|
{
|
||||||
(string fileHash, long fileLengthBytes) = ReadBlockFileHeader(fileBlockStream);
|
(string fileHash, long fileLengthBytes) = ReadBlockFileHeader(fileBlockStream);
|
||||||
|
var chunkPosition = fileBlockStream.Position;
|
||||||
|
fileBlockStream.Position += fileLengthBytes;
|
||||||
|
|
||||||
|
while (tasks.Count > threadCount && tasks.Where(t => !t.IsCompleted).Count() > 4)
|
||||||
|
await Task.Delay(10, CancellationToken.None);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var fileExtension = fileReplacement.First(f => string.Equals(f.Hash, fileHash, StringComparison.OrdinalIgnoreCase)).GamePaths[0].Split(".")[^1];
|
var fileExtension = fileReplacement.First(f => string.Equals(f.Hash, fileHash, StringComparison.OrdinalIgnoreCase)).GamePaths[0].Split(".")[^1];
|
||||||
|
var tmpPath = _fileDbManager.GetCacheFilePath(Guid.NewGuid().ToString(), "tmp");
|
||||||
var filePath = _fileDbManager.GetCacheFilePath(fileHash, fileExtension);
|
var filePath = _fileDbManager.GetCacheFilePath(fileHash, fileExtension);
|
||||||
|
|
||||||
Logger.LogDebug("{dlName}: Decompressing {file}:{le} => {dest}", fi.Name, fileHash, fileLengthBytes, filePath);
|
Logger.LogDebug("{dlName}: Decompressing {file}:{le} => {dest}", fi.Name, fileHash, fileLengthBytes, filePath);
|
||||||
|
|
||||||
using var decompressedFile = new MemoryStream(64 * 1024);
|
tasks.Add(Task.Run(() => {
|
||||||
using var innerFileStream = new LimitedStream(fileBlockStream, fileLengthBytes);
|
try
|
||||||
innerFileStream.DisposeUnderlying = false;
|
{
|
||||||
using var decStream = LZ4Stream.Decode(innerFileStream, 0, true);
|
using var tmpFileStream = new FileStream(tmpPath, new FileStreamOptions()
|
||||||
long startPos = fileBlockStream.Position;
|
{
|
||||||
await decStream.CopyToAsync(decompressedFile, CancellationToken.None).ConfigureAwait(false);
|
Mode = FileMode.CreateNew,
|
||||||
long readBytes = fileBlockStream.Position - startPos;
|
Access = FileAccess.Write,
|
||||||
|
Share = FileShare.None
|
||||||
|
});
|
||||||
|
|
||||||
|
using var fileChunkStream = new FileStream(blockFile, new FileStreamOptions()
|
||||||
|
{
|
||||||
|
BufferSize = 80000,
|
||||||
|
Mode = FileMode.Open,
|
||||||
|
Access = FileAccess.Read
|
||||||
|
});
|
||||||
|
fileChunkStream.Position = chunkPosition;
|
||||||
|
|
||||||
|
using var innerFileStream = new LimitedStream(fileChunkStream, fileLengthBytes);
|
||||||
|
using var decoder = LZ4Frame.Decode(innerFileStream);
|
||||||
|
long startPos = fileChunkStream.Position;
|
||||||
|
decoder.AsStream().CopyTo(tmpFileStream);
|
||||||
|
long readBytes = fileChunkStream.Position - startPos;
|
||||||
|
|
||||||
if (readBytes != fileLengthBytes)
|
if (readBytes != fileLengthBytes)
|
||||||
{
|
{
|
||||||
throw new EndOfStreamException();
|
throw new EndOfStreamException();
|
||||||
}
|
}
|
||||||
|
|
||||||
await _fileCompactor.WriteAllBytesAsync(filePath, decompressedFile.ToArray(), CancellationToken.None).ConfigureAwait(false);
|
tmpFileStream.Close();
|
||||||
|
_fileCompactor.RenameAndCompact(filePath, tmpPath);
|
||||||
PersistFileToStorage(fileHash, filePath, fileLengthBytes);
|
PersistFileToStorage(fileHash, filePath, fileLengthBytes);
|
||||||
}
|
}
|
||||||
catch (EndOfStreamException)
|
catch (EndOfStreamException)
|
||||||
@@ -351,7 +374,15 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
foreach (var fr in fileReplacement)
|
foreach (var fr in fileReplacement)
|
||||||
Logger.LogWarning(" - {h}: {x}", fr.Hash, fr.GamePaths[0]);
|
Logger.LogWarning(" - {h}: {x}", fr.Hash, fr.GamePaths[0]);
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (File.Exists(tmpPath))
|
||||||
|
File.Delete(tmpPath);
|
||||||
}
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.WaitAll([..tasks], CancellationToken.None);
|
||||||
}
|
}
|
||||||
catch (EndOfStreamException)
|
catch (EndOfStreamException)
|
||||||
{
|
{
|
||||||
@@ -363,6 +394,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
Task.WaitAll([..tasks], CancellationToken.None);
|
||||||
_orchestrator.ReleaseDownloadSlot();
|
_orchestrator.ReleaseDownloadSlot();
|
||||||
if (fileBlockStream != null)
|
if (fileBlockStream != null)
|
||||||
await fileBlockStream.DisposeAsync().ConfigureAwait(false);
|
await fileBlockStream.DisposeAsync().ConfigureAwait(false);
|
||||||
@@ -384,18 +416,6 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
|
|
||||||
private void PersistFileToStorage(string fileHash, string filePath, long? compressedSize = null)
|
private void PersistFileToStorage(string fileHash, string filePath, long? compressedSize = null)
|
||||||
{
|
{
|
||||||
var fi = new FileInfo(filePath);
|
|
||||||
Func<DateTime> RandomDayInThePast()
|
|
||||||
{
|
|
||||||
DateTime start = new(1995, 1, 1, 1, 1, 1, DateTimeKind.Local);
|
|
||||||
Random gen = new();
|
|
||||||
int range = (DateTime.Today - start).Days;
|
|
||||||
return () => start.AddDays(gen.Next(range));
|
|
||||||
}
|
|
||||||
|
|
||||||
fi.CreationTime = RandomDayInThePast().Invoke();
|
|
||||||
fi.LastAccessTime = DateTime.Today;
|
|
||||||
fi.LastWriteTime = RandomDayInThePast().Invoke();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var entry = _fileDbManager.CreateCacheEntry(filePath);
|
var entry = _fileDbManager.CreateCacheEntry(filePath);
|
||||||
@@ -404,6 +424,7 @@ public partial class FileDownloadManager : DisposableMediatorSubscriberBase
|
|||||||
Logger.LogError("Hash mismatch after extracting, got {hash}, expected {expectedHash}, deleting file", entry.Hash, fileHash);
|
Logger.LogError("Hash mismatch after extracting, got {hash}, expected {expectedHash}, deleting file", entry.Hash, fileHash);
|
||||||
File.Delete(filePath);
|
File.Delete(filePath);
|
||||||
_fileDbManager.RemoveHashedFile(entry.Hash, entry.PrefixedFilePath);
|
_fileDbManager.RemoveHashedFile(entry.Hash, entry.PrefixedFilePath);
|
||||||
|
entry = null;
|
||||||
}
|
}
|
||||||
if (entry != null)
|
if (entry != null)
|
||||||
entry.CompressedSize = compressedSize;
|
entry.CompressedSize = compressedSize;
|
||||||
|
|||||||
Reference in New Issue
Block a user