feat(replay): add rewind buffer
This commit is contained in:
1 parent
7317326166
commit
855cf68993
1 file changed
+113
@@ -0,0 +1,113 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Chip8.Replay;
|
||||||
|
|
||||||
|
public sealed class RewindBuffer
|
||||||
|
{
|
||||||
|
readonly TimelineSnapshot?[] ring;
|
||||||
|
int head;
|
||||||
|
int count;
|
||||||
|
long memoryBytes;
|
||||||
|
|
||||||
|
public RewindBuffer(int capacity)
|
||||||
|
{
|
||||||
|
if (capacity < 1)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(capacity), "rewind buffer needs at least one slot");
|
||||||
|
}
|
||||||
|
ring = new TimelineSnapshot?[capacity];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int capacity => ring.Length;
|
||||||
|
|
||||||
|
public int keyframeCount => count;
|
||||||
|
|
||||||
|
public long memoryUsed => memoryBytes;
|
||||||
|
|
||||||
|
public bool isEmpty => count == 0;
|
||||||
|
|
||||||
|
public void store(int frameIndex, byte[] state)
|
||||||
|
{
|
||||||
|
var snapshot = new TimelineSnapshot(frameIndex, state);
|
||||||
|
|
||||||
|
if (count == ring.Length)
|
||||||
|
{
|
||||||
|
TimelineSnapshot evicted = ring[head]!;
|
||||||
|
memoryBytes -= evicted.stateSize;
|
||||||
|
ring[head] = snapshot;
|
||||||
|
head = (head + 1) % ring.Length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int slot = (head + count) % ring.Length;
|
||||||
|
ring[slot] = snapshot;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
memoryBytes += snapshot.stateSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimelineSnapshot? nearestAtOrBefore(int frameIndex)
|
||||||
|
{
|
||||||
|
TimelineSnapshot? best = null;
|
||||||
|
for (int offset = 0; offset < count; offset++)
|
||||||
|
{
|
||||||
|
TimelineSnapshot candidate = ring[(head + offset) % ring.Length]!;
|
||||||
|
if (candidate.frameIndex <= frameIndex && (best is null || candidate.frameIndex > best.frameIndex))
|
||||||
|
{
|
||||||
|
best = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int oldestFrame
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int oldest = int.MaxValue;
|
||||||
|
for (int offset = 0; offset < count; offset++)
|
||||||
|
{
|
||||||
|
int frame = ring[(head + offset) % ring.Length]!.frameIndex;
|
||||||
|
if (frame < oldest)
|
||||||
|
{
|
||||||
|
oldest = frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return oldest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int newestFrame
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int newest = -1;
|
||||||
|
for (int offset = 0; offset < count; offset++)
|
||||||
|
{
|
||||||
|
int frame = ring[(head + offset) % ring.Length]!.frameIndex;
|
||||||
|
if (frame > newest)
|
||||||
|
{
|
||||||
|
newest = frame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear()
|
||||||
|
{
|
||||||
|
Array.Clear(ring, 0, ring.Length);
|
||||||
|
head = 0;
|
||||||
|
count = 0;
|
||||||
|
memoryBytes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user