wabbajack/Wabbajack.VFS/VFSCache.cs

101 lines
3.0 KiB
C#
Raw Normal View History

2021-09-27 12:42:46 +00:00
using System;
using System.Collections.Immutable;
using System.Data;
using System.Data.SQLite;
using System.IO;
using System.Linq;
2022-06-22 01:38:42 +00:00
using System.Threading;
2021-09-27 12:42:46 +00:00
using System.Threading.Tasks;
using Wabbajack.Common;
using Wabbajack.DTOs.Streams;
2022-06-22 01:38:42 +00:00
using Wabbajack.DTOs.Vfs;
2021-09-27 12:42:46 +00:00
using Wabbajack.Hashing.xxHash64;
using Wabbajack.Paths;
using Wabbajack.Paths.IO;
2022-06-22 01:38:42 +00:00
using Wabbajack.VFS.Interfaces;
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
namespace Wabbajack.VFS;
2022-06-22 01:38:42 +00:00
public class VFSDiskCache : IVfsCache
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 _path;
2022-06-22 01:38:42 +00:00
public VFSDiskCache(AbsolutePath path)
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
_path = path;
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
if (!_path.Parent.DirectoryExists())
_path.Parent.CreateDirectory();
_connectionString = string.Intern($"URI=file:{path};Pooling=True;Max Pool Size=100; Journal Mode=Memory;");
_conn = new SQLiteConnection(_connectionString);
_conn.Open();
using var cmd = new SQLiteCommand(_conn);
cmd.CommandText = @"CREATE TABLE IF NOT EXISTS VFSCache (
2021-09-27 12:42:46 +00:00
Hash BIGINT PRIMARY KEY,
Contents BLOB)
WITHOUT ROWID";
2021-10-23 16:51:17 +00:00
cmd.ExecuteNonQuery();
}
2021-09-27 12:42:46 +00:00
2022-06-22 01:38:42 +00:00
public async Task<IndexedVirtualFile?> Get(Hash hash, CancellationToken token)
2021-10-23 16:51:17 +00:00
{
if (hash == default)
throw new ArgumentException("Cannot cache default hashes");
2022-06-22 01:38:42 +00:00
await using var cmd = new SQLiteCommand(_conn);
2021-10-23 16:51:17 +00:00
cmd.CommandText = @"SELECT Contents FROM VFSCache WHERE Hash = @hash";
cmd.Parameters.AddWithValue("@hash", (long) hash);
2022-06-22 01:38:42 +00:00
await using var rdr = cmd.ExecuteReader();
2021-10-23 16:51:17 +00:00
while (rdr.Read())
2021-09-27 12:42:46 +00:00
{
2022-06-22 01:38:42 +00:00
var data = IndexedVirtualFileExtensions.Read(rdr.GetStream(0));
return data;
2021-09-27 12:42:46 +00:00
}
2022-06-22 01:38:42 +00:00
return null;
2021-10-23 16:51:17 +00:00
}
2022-06-22 01:38:42 +00:00
public async Task Put(IndexedVirtualFile ivf, CancellationToken token)
2021-10-23 16:51:17 +00:00
{
await using var ms = new MemoryStream();
// Top level path gets renamed when read, we don't want the absolute path
// here else the reader will blow up when it tries to convert the value
ivf.Name = (RelativePath) "not/applicable";
ivf.Write(ms);
ms.Position = 0;
2022-06-22 01:38:42 +00:00
await InsertIntoVFSCache(ivf.Hash, ms);
2021-10-23 16:51:17 +00:00
}
private async Task InsertIntoVFSCache(Hash hash, MemoryStream data)
{
await using var cmd = new SQLiteCommand(_conn);
cmd.CommandText = @"INSERT INTO VFSCache (Hash, Contents) VALUES (@hash, @contents)";
cmd.Parameters.AddWithValue("@hash", (long) hash);
var val = new SQLiteParameter("@contents", DbType.Binary) {Value = data.ToArray()};
cmd.Parameters.Add(val);
try
2021-09-27 12:42:46 +00:00
{
2021-10-23 16:51:17 +00:00
await cmd.ExecuteNonQueryAsync();
2021-09-27 12:42:46 +00:00
}
2021-10-23 16:51:17 +00:00
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"))
return;
throw;
2021-09-27 12:42:46 +00:00
}
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 void VacuumDatabase()
{
using var cmd = new SQLiteCommand(_conn);
cmd.CommandText = @"VACUUM";
cmd.PrepareAsync();
2021-09-27 12:42:46 +00:00
2021-10-23 16:51:17 +00:00
cmd.ExecuteNonQuery();
2021-09-27 12:42:46 +00:00
}
}