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
|
|
|
}
|
|
|
|
}
|