wabbajack/Wabbajack.Compiler/PatchCache/BinaryPatchCache.cs

115 lines
3.6 KiB
C#
Raw Normal View History

2021-09-27 12:42:46 +00:00
using System;
using System.Data.SQLite;
using System.IO;
2022-09-25 22:36:12 +00:00
using System.Threading;
2021-09-27 12:42:46 +00:00
using System.Threading.Tasks;
2022-09-25 22:36:12 +00:00
using Microsoft.Extensions.Logging;
using Wabbajack.Common;
2021-09-27 12:42:46 +00:00
using Wabbajack.Compiler.PatchCache;
using Wabbajack.Hashing.xxHash64;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
2022-05-27 05:41:11 +00:00
using Wabbajack.RateLimiter;
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
namespace Wabbajack.Compiler;
public class BinaryPatchCache : IBinaryPatchCache
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
private readonly SQLiteConnection _conn;
private readonly string _connectionString;
private readonly AbsolutePath _location;
2022-09-25 22:36:12 +00:00
private readonly ILogger<BinaryPatchCache> _logger;
2021-10-23 16:51:17 +00:00
2022-09-25 22:36:12 +00:00
public BinaryPatchCache(ILogger<BinaryPatchCache> logger, AbsolutePath location)
2021-09-27 12:42:46 +00:00
{
2022-09-25 22:36:12 +00:00
_logger = logger;
2021-10-23 16:51:17 +00:00
_location = location;
2022-09-25 22:36:12 +00:00
if (!_location.DirectoryExists())
_location.CreateDirectory();
}
2021-10-23 16:51:17 +00:00
2022-09-25 22:36:12 +00:00
public AbsolutePath PatchLocation(Hash srcHash, Hash destHash)
{
return _location.Combine($"{srcHash.ToHex()}_{destHash.ToHex()}.octodiff");
2021-10-23 16:51:17 +00:00
}
2022-05-27 05:41:11 +00:00
public async Task<CacheEntry> CreatePatch(Stream srcStream, Hash srcHash, Stream destStream, Hash destHash, IJob? job)
2021-10-23 16:51:17 +00:00
{
2022-09-25 22:36:12 +00:00
var location = PatchLocation(srcHash, destHash);
if (location.FileExists())
return new CacheEntry(srcHash, destHash, location.Size(), this);
2021-10-23 16:51:17 +00:00
await using var sigStream = new MemoryStream();
2022-09-25 22:36:12 +00:00
var tempName = _location.Combine(Guid.NewGuid().ToString()).WithExtension(Ext.Temp);
await using var patchStream = tempName.Open(FileMode.Create, FileAccess.ReadWrite, FileShare.None);
2021-10-23 16:51:17 +00:00
try
{
2022-09-25 22:36:12 +00:00
OctoDiff.Create(srcStream, destStream, sigStream, patchStream, job);
patchStream.Close();
await tempName.MoveToAsync(location, true, CancellationToken.None);
2021-09-27 12:42:46 +00:00
}
2022-09-25 22:36:12 +00:00
finally
2021-09-27 12:42:46 +00:00
{
2022-09-25 22:36:12 +00:00
await patchStream.DisposeAsync();
if (tempName.FileExists())
tempName.Delete();
2021-09-27 12:42:46 +00:00
}
2022-09-25 22:36:12 +00:00
return new CacheEntry(srcHash, destHash, location.Size(), this);
2021-10-23 16:51:17 +00:00
}
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
public async Task<CacheEntry?> GetPatch(Hash fromHash, Hash toHash)
{
2022-09-25 22:36:12 +00:00
var location = PatchLocation(fromHash, toHash);
if (location.FileExists())
return new CacheEntry(fromHash, toHash, location.Size(), this);
2021-10-23 16:51:17 +00:00
return null;
}
public async Task<byte[]> GetData(CacheEntry entry)
{
2022-09-25 22:36:12 +00:00
var location = PatchLocation(entry.From, entry.To);
if (location.FileExists())
return await location.ReadAllBytesAsync();
2021-10-23 16:51:17 +00:00
return Array.Empty<byte>();
}
public async Task CreatePatchCached(byte[] a, byte[] b, Stream output)
{
await using var cmd = new SQLiteCommand(_conn);
cmd.CommandText = @"INSERT INTO PatchCache (FromHash, ToHash, PatchSize, Patch)
VALUES (@fromHash, @toHash, @patchSize, @patch)";
xxHashAlgorithm aAl = new(0), bAl = new(0);
var dataA = Hash.FromULong(aAl.HashBytes(a));
;
var dataB = Hash.FromULong(bAl.HashBytes(b));
;
cmd.Parameters.AddWithValue("@fromHash", (long) dataA);
cmd.Parameters.AddWithValue("@toHash", (long) dataB);
await using var patch = new MemoryStream();
OctoDiff.Create(a, b, patch);
patch.Position = 0;
cmd.Parameters.AddWithValue("@patchSize", patch.Length);
cmd.Parameters.AddWithValue("@patch", patch.ToArray());
try
{
await cmd.ExecuteNonQueryAsync();
}
catch (SQLiteException ex)
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
if (!ex.Message.StartsWith("constraint failed"))
throw;
2021-09-27 12:42:46 +00:00
}
2021-10-23 16:51:17 +00:00
await patch.CopyToAsync(output);
2021-09-27 12:42:46 +00:00
}
}