rip out NDBDatabase
This commit is contained in:
parent
ed85ce806b
commit
b1296dd9bb
|
@ -44,9 +44,6 @@ namespace BizHawk.Client.Common
|
||||||
// ********* Delete these **********
|
// ********* Delete these **********
|
||||||
void MountWriteAccess();
|
void MountWriteAccess();
|
||||||
|
|
||||||
// TODO: delete me, I don't work
|
|
||||||
NDBDatabase NdbDatabase { get; }
|
|
||||||
|
|
||||||
// *********** Reconsider these ************/
|
// *********** Reconsider these ************/
|
||||||
void LimitStateCount();
|
void LimitStateCount();
|
||||||
|
|
||||||
|
|
|
@ -8,102 +8,20 @@ namespace BizHawk.Client.Common
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class StateManagerState : IDisposable
|
internal class StateManagerState : IDisposable
|
||||||
{
|
{
|
||||||
private static long _stateId;
|
|
||||||
private readonly IStateManager _manager;
|
|
||||||
private readonly long _id;
|
|
||||||
|
|
||||||
private byte[] _state;
|
|
||||||
|
|
||||||
public int Frame { get; }
|
public int Frame { get; }
|
||||||
|
|
||||||
public static StateManagerState Read(BinaryReader r, IStateManager m)
|
public byte[] State { get; set; }
|
||||||
{
|
|
||||||
int frame = r.ReadInt32();
|
|
||||||
byte[] data = r.ReadBytes(r.ReadInt32());
|
|
||||||
return new StateManagerState(m, data, frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Write(BinaryWriter w)
|
|
||||||
{
|
|
||||||
w.Write(Frame);
|
|
||||||
w.Write(_state.Length);
|
|
||||||
w.Write(_state);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] State
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_state != null)
|
|
||||||
{
|
|
||||||
return _state;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _manager.NdbDatabase.FetchAll(_id.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_state != null)
|
|
||||||
{
|
|
||||||
_state = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception("Attempted to set a state to null.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Length => State.Length;
|
public int Length => State.Length;
|
||||||
|
|
||||||
public bool IsOnDisk => _state == null;
|
public StateManagerState(byte[] state, int frame)
|
||||||
|
|
||||||
public StateManagerState(IStateManager manager, byte[] state, int frame)
|
|
||||||
{
|
{
|
||||||
_manager = manager;
|
State = state;
|
||||||
_state = state;
|
|
||||||
Frame = frame;
|
Frame = frame;
|
||||||
|
|
||||||
if (_stateId > long.MaxValue - 100)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
_id = System.Threading.Interlocked.Increment(ref _stateId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MoveToDisk()
|
|
||||||
{
|
|
||||||
if (IsOnDisk)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_manager.NdbDatabase.Store(_id.ToString(), _state, 0, _state.Length);
|
|
||||||
_state = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MoveToRAM()
|
|
||||||
{
|
|
||||||
if (!IsOnDisk)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string key = _id.ToString();
|
|
||||||
_state = _manager.NdbDatabase.FetchAll(key);
|
|
||||||
_manager.NdbDatabase.Release(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (!IsOnDisk)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_manager.NdbDatabase.Release(_id.ToString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,19 +27,8 @@ namespace BizHawk.Client.Common
|
||||||
InvalidateCallback?.Invoke(index);
|
InvalidateCallback?.Invoke(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NDBDatabase NdbDatabase { get; private set; }
|
|
||||||
private Guid _guid = Guid.NewGuid();
|
|
||||||
private SortedList<int, StateManagerState> _states = new SortedList<int, StateManagerState>();
|
private SortedList<int, StateManagerState> _states = new SortedList<int, StateManagerState>();
|
||||||
|
|
||||||
private string StatePath
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var basePath = PathManager.MakeAbsolutePath(Global.Config.PathEntries["Global", "TAStudio states"].Path, null);
|
|
||||||
return Path.Combine(basePath, _guid.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _isMountedForWrite;
|
private bool _isMountedForWrite;
|
||||||
private readonly TasMovie _movie;
|
private readonly TasMovie _movie;
|
||||||
|
|
||||||
|
@ -66,8 +55,6 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// States and BranchStates don't need cleaning because they would only contain an ndbdatabase entry which was demolished by the below
|
|
||||||
NdbDatabase?.Dispose();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateStateFrequency()
|
public void UpdateStateFrequency()
|
||||||
|
@ -104,8 +91,6 @@ namespace BizHawk.Client.Common
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException();
|
throw new InvalidOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
NdbDatabase = new NDBDatabase(StatePath, Settings.DiskCapacitymb * 1024 * 1024, (int)_expectedStateSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public TasStateManagerSettings Settings { get; set; }
|
public TasStateManagerSettings Settings { get; set; }
|
||||||
|
@ -196,7 +181,7 @@ namespace BizHawk.Client.Common
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Used += (ulong)state.Length;
|
Used += (ulong)state.Length;
|
||||||
_states.Add(frame, new StateManagerState(this, state, frame));
|
_states.Add(frame, new StateManagerState(state, frame));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,16 +242,9 @@ namespace BizHawk.Client.Common
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
StateManagerState state = _states.Values[index];
|
StateManagerState state = _states.Values[index]; // TODO: remove .Values here?
|
||||||
|
|
||||||
if (state.IsOnDisk)
|
Used -= (ulong)state.Length;
|
||||||
{
|
|
||||||
state.Dispose();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Used -= (ulong)state.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
_states.RemoveAt(index);
|
_states.RemoveAt(index);
|
||||||
|
|
||||||
|
@ -279,7 +257,7 @@ namespace BizHawk.Client.Common
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void LimitStateCount()
|
public void LimitStateCount()
|
||||||
{
|
{
|
||||||
if (Count + 1 > MaxStates || DiskUsed > (ulong)Settings.DiskCapacitymb * 1024 * 1024)
|
if (Count + 1 > MaxStates)
|
||||||
{
|
{
|
||||||
_decay.Trigger(Count + 1 - MaxStates);
|
_decay.Trigger(Count + 1 - MaxStates);
|
||||||
}
|
}
|
||||||
|
@ -288,7 +266,7 @@ namespace BizHawk.Client.Common
|
||||||
private List<int> ExcludeStates()
|
private List<int> ExcludeStates()
|
||||||
{
|
{
|
||||||
List<int> ret = new List<int>();
|
List<int> ret = new List<int>();
|
||||||
ulong saveUsed = Used + DiskUsed;
|
ulong saveUsed = Used;
|
||||||
|
|
||||||
// respect state gap no matter how small the resulting size will be
|
// respect state gap no matter how small the resulting size will be
|
||||||
// still leave marker states
|
// still leave marker states
|
||||||
|
@ -303,14 +281,7 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
ret.Add(i);
|
ret.Add(i);
|
||||||
|
|
||||||
if (_states.Values[i].IsOnDisk)
|
saveUsed -= (ulong)_states.Values[i].Length;
|
||||||
{
|
|
||||||
saveUsed -= _expectedStateSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
saveUsed -= (ulong)_states.Values[i].Length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the size is still too big, exclude states form the beginning
|
// if the size is still too big, exclude states form the beginning
|
||||||
|
@ -333,15 +304,7 @@ namespace BizHawk.Client.Common
|
||||||
}
|
}
|
||||||
|
|
||||||
ret.Add(index);
|
ret.Add(index);
|
||||||
|
saveUsed -= (ulong)_states.Values[index].Length;
|
||||||
if (_states.Values[index].IsOnDisk)
|
|
||||||
{
|
|
||||||
saveUsed -= _expectedStateSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
saveUsed -= (ulong)_states.Values[index].Length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there are enough markers to still be over the limit, remove marker frames
|
// if there are enough markers to still be over the limit, remove marker frames
|
||||||
|
@ -353,14 +316,7 @@ namespace BizHawk.Client.Common
|
||||||
ret.Add(index);
|
ret.Add(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_states.Values[index].IsOnDisk)
|
saveUsed -= (ulong)_states.Values[index].Length;
|
||||||
{
|
|
||||||
saveUsed -= _expectedStateSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
saveUsed -= (ulong)_states.Values[index].Length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -378,7 +334,6 @@ namespace BizHawk.Client.Common
|
||||||
_states.Clear();
|
_states.Clear();
|
||||||
SetState(0, power.State);
|
SetState(0, power.State);
|
||||||
Used = (ulong)power.State.Length;
|
Used = (ulong)power.State.Length;
|
||||||
NdbDatabase?.Clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,19 +430,6 @@ namespace BizHawk.Client.Common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ulong DiskUsed
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (NdbDatabase == null)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (ulong)NdbDatabase.Consumed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Count => _states.Count;
|
public int Count => _states.Count;
|
||||||
|
|
||||||
public bool Any()
|
public bool Any()
|
||||||
|
|
|
@ -84,7 +84,6 @@
|
||||||
<Compile Include="Log.cs" />
|
<Compile Include="Log.cs" />
|
||||||
<Compile Include="MruStack.cs" />
|
<Compile Include="MruStack.cs" />
|
||||||
<Compile Include="MutableIntRange.cs" />
|
<Compile Include="MutableIntRange.cs" />
|
||||||
<Compile Include="NDBDatabase.cs" />
|
|
||||||
<Compile Include="OSTailoredCode.cs" />
|
<Compile Include="OSTailoredCode.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="QuickCollections.cs" />
|
<Compile Include="QuickCollections.cs" />
|
||||||
|
|
|
@ -1,200 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace BizHawk.Common
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Non-consecutive Disk Block Database
|
|
||||||
/// Opens a file and stores blocks in it.
|
|
||||||
/// Blocks can be differently sized than the basic block size. Wastage will occur.
|
|
||||||
/// TODO: Mount on memory as well?
|
|
||||||
/// </summary>
|
|
||||||
public class NDBDatabase : IDisposable
|
|
||||||
{
|
|
||||||
readonly int BlockSize;
|
|
||||||
readonly long BlockCount;
|
|
||||||
|
|
||||||
Dictionary<string, Item> Items = new Dictionary<string, Item>();
|
|
||||||
LinkedList<Block> FreeList = new LinkedList<Block>();
|
|
||||||
long FreeWatermark;
|
|
||||||
FileStream Stream;
|
|
||||||
|
|
||||||
class Block
|
|
||||||
{
|
|
||||||
public long Number;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Item
|
|
||||||
{
|
|
||||||
public LinkedList<Block> Blocks = new LinkedList<Block>();
|
|
||||||
public long Size;
|
|
||||||
}
|
|
||||||
|
|
||||||
Block AllocBlock()
|
|
||||||
{
|
|
||||||
if (FreeList.Count != 0)
|
|
||||||
{
|
|
||||||
var blocknode = FreeList.First;
|
|
||||||
FreeList.RemoveFirst();
|
|
||||||
Consumed += BlockSize;
|
|
||||||
return blocknode.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FreeWatermark == BlockCount)
|
|
||||||
throw new OutOfMemoryException($"{nameof(NDBDatabase)} out of reserved space");
|
|
||||||
|
|
||||||
var b = new Block() { Number = FreeWatermark };
|
|
||||||
FreeWatermark++;
|
|
||||||
Consumed += BlockSize;
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
long GetOffsetForBlock(Block b)
|
|
||||||
{
|
|
||||||
return b.Number * BlockSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance around a DeleteOnClose file of the provided path
|
|
||||||
/// </summary>
|
|
||||||
public NDBDatabase(string path, long size, int blocksize)
|
|
||||||
{
|
|
||||||
Capacity = size;
|
|
||||||
Consumed = 0;
|
|
||||||
BlockSize = blocksize;
|
|
||||||
BlockCount = size / BlockSize;
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
|
||||||
Stream = new FileStream(path, FileMode.Create, System.Security.AccessControl.FileSystemRights.FullControl, FileShare.None, 4 * 1024, FileOptions.DeleteOnClose);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clears the state of the datastructure to its original condition
|
|
||||||
/// </summary>
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
Consumed = 0;
|
|
||||||
Items.Clear();
|
|
||||||
FreeList.Clear();
|
|
||||||
FreeWatermark = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Stream.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Total reserved storage capacity. You may nto be able to fit that much data in here though (due to blockiness)
|
|
||||||
/// </summary>
|
|
||||||
public readonly long Capacity;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of bytes of storage consumed. Not necessarily equal to the total amount of data stored (due to blockiness)
|
|
||||||
/// </summary>
|
|
||||||
public long Consumed { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of bytes of storage available. Store operations <= Remain will always succeed
|
|
||||||
/// </summary>
|
|
||||||
public long Remain { get { return Capacity - Consumed; } }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stores an item with the given key
|
|
||||||
/// </summary>
|
|
||||||
public void Store(string name, byte[] buf, int offset, int length)
|
|
||||||
{
|
|
||||||
if (Items.ContainsKey(name))
|
|
||||||
throw new InvalidOperationException($"Can't add already existing key of name {name}");
|
|
||||||
|
|
||||||
if (length > Remain)
|
|
||||||
throw new OutOfMemoryException($"Insufficient storage reserved for {length} bytes");
|
|
||||||
|
|
||||||
long todo = length;
|
|
||||||
int src = offset;
|
|
||||||
Item item = new Item { Size = length };
|
|
||||||
Items[name] = item;
|
|
||||||
while (todo > 0)
|
|
||||||
{
|
|
||||||
var b = AllocBlock();
|
|
||||||
item.Blocks.AddLast(b);
|
|
||||||
|
|
||||||
long tocopy = todo;
|
|
||||||
if (tocopy > BlockSize)
|
|
||||||
tocopy = BlockSize;
|
|
||||||
|
|
||||||
Stream.Position = GetOffsetForBlock(b);
|
|
||||||
Stream.Write(buf, src, (int)tocopy);
|
|
||||||
|
|
||||||
todo -= tocopy;
|
|
||||||
src += (int)tocopy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fetches an item with the given key
|
|
||||||
/// </summary>
|
|
||||||
public byte[] FetchAll(string name)
|
|
||||||
{
|
|
||||||
var buf = new byte[GetSize(name)];
|
|
||||||
Fetch(name, buf, 0);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fetches an item with the given key
|
|
||||||
/// </summary>
|
|
||||||
public void Fetch(string name, byte[] buf, int offset)
|
|
||||||
{
|
|
||||||
Item item;
|
|
||||||
if (!Items.TryGetValue(name, out item))
|
|
||||||
throw new KeyNotFoundException();
|
|
||||||
|
|
||||||
long todo = item.Size;
|
|
||||||
var curr = item.Blocks.First;
|
|
||||||
while (todo > 0)
|
|
||||||
{
|
|
||||||
long tocopy = todo;
|
|
||||||
if (tocopy > BlockSize)
|
|
||||||
tocopy = BlockSize;
|
|
||||||
Stream.Position = GetOffsetForBlock(curr.Value);
|
|
||||||
Stream.Read(buf, offset, (int)tocopy);
|
|
||||||
|
|
||||||
todo -= tocopy;
|
|
||||||
offset += (int)tocopy;
|
|
||||||
|
|
||||||
curr = curr.Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.Diagnostics.Debug.Assert(curr == null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases the item with the given key.
|
|
||||||
/// Removing a non-existent item is benign, I guess
|
|
||||||
/// </summary>
|
|
||||||
public void Release(string name)
|
|
||||||
{
|
|
||||||
Item item;
|
|
||||||
if (!Items.TryGetValue(name, out item))
|
|
||||||
return;
|
|
||||||
Items.Remove(name);
|
|
||||||
var blocks = item.Blocks.ToArray();
|
|
||||||
item.Blocks.Clear();
|
|
||||||
foreach (var block in blocks)
|
|
||||||
FreeList.AddLast(block);
|
|
||||||
Consumed -= blocks.Length * BlockSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the size of the item with the given key
|
|
||||||
/// </summary>
|
|
||||||
public long GetSize(string name)
|
|
||||||
{
|
|
||||||
return Items[name].Size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue