Use variable length integers for the rewind state deltas to reduce size.
This commit is contained in:
parent
1a266a0518
commit
4a40408f1d
|
@ -263,17 +263,19 @@ namespace BizHawk.Client.Common
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var beginChangeSequence = -1;
|
int index = 0;
|
||||||
var inChangeSequence = false;
|
int stateLength = Math.Min(currentState.Length, _lastState.Length);
|
||||||
|
bool inChangeSequence = false;
|
||||||
|
int changeSequenceStartOffset = 0;
|
||||||
|
int lastChangeSequenceStartOffset = 0;
|
||||||
|
|
||||||
if (_tempBuf.Length < currentState.Length)
|
if (_tempBuf.Length < stateLength)
|
||||||
{
|
{
|
||||||
_tempBuf = new byte[currentState.Length];
|
_tempBuf = new byte[stateLength];
|
||||||
}
|
}
|
||||||
|
|
||||||
int offset = 0;
|
_tempBuf[index++] = 0; // Full state (false = delta)
|
||||||
_tempBuf[offset++] = 0; // Full state (false = delta)
|
|
||||||
int stateLength = Math.Min(currentState.Length, _lastState.Length); // Just to be safe :)
|
|
||||||
fixed (byte* pCurrentState = ¤tState[0])
|
fixed (byte* pCurrentState = ¤tState[0])
|
||||||
fixed (byte* pLastState = &_lastState[0])
|
fixed (byte* pLastState = &_lastState[0])
|
||||||
for (int i = 0; i < stateLength; i++)
|
for (int i = 0; i < stateLength; i++)
|
||||||
|
@ -288,54 +290,39 @@ namespace BizHawk.Client.Common
|
||||||
}
|
}
|
||||||
|
|
||||||
inChangeSequence = true;
|
inChangeSequence = true;
|
||||||
beginChangeSequence = i;
|
changeSequenceStartOffset = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (thisByteMatches || i - beginChangeSequence == 254 || i == currentState.Length - 1)
|
if (thisByteMatches || i == stateLength - 1)
|
||||||
{
|
{
|
||||||
const int maxHeaderSize = 5;
|
const int maxHeaderSize = 10;
|
||||||
int length = i - beginChangeSequence + (thisByteMatches ? 0 : 1);
|
int length = i - changeSequenceStartOffset + (thisByteMatches ? 0 : 1);
|
||||||
|
|
||||||
if (offset + length + maxHeaderSize >= stateLength)
|
if (index + length + maxHeaderSize >= stateLength)
|
||||||
{
|
{
|
||||||
// If the delta ends up being larger than the full state, capture the full state instead
|
// If the delta ends up being larger than the full state, capture the full state instead
|
||||||
CaptureRewindStateNonDelta(currentState);
|
CaptureRewindStateNonDelta(currentState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Length
|
// Delta Offset
|
||||||
_tempBuf[offset++] = (byte)length;
|
VLInteger.WriteUnsigned((uint)(changeSequenceStartOffset - lastChangeSequenceStartOffset), _tempBuf, ref index);
|
||||||
|
|
||||||
// Offset
|
// Length
|
||||||
if (isSmall)
|
VLInteger.WriteUnsigned((uint)length, _tempBuf, ref index);
|
||||||
{
|
|
||||||
BitConverterLE.WriteBytes((ushort)beginChangeSequence, _tempBuf, offset);
|
|
||||||
offset += 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BitConverterLE.WriteBytes((uint)beginChangeSequence, _tempBuf, offset);
|
|
||||||
offset += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
Buffer.BlockCopy(_lastState, beginChangeSequence, _tempBuf, offset, length);
|
Buffer.BlockCopy(_lastState, changeSequenceStartOffset, _tempBuf, index, length);
|
||||||
offset += length;
|
index += length;
|
||||||
|
|
||||||
inChangeSequence = false;
|
inChangeSequence = false;
|
||||||
|
lastChangeSequenceStartOffset = changeSequenceStartOffset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_lastState.Length == currentState.Length)
|
|
||||||
{
|
|
||||||
Buffer.BlockCopy(currentState, 0, _lastState, 0, _lastState.Length);
|
Buffer.BlockCopy(currentState, 0, _lastState, 0, _lastState.Length);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_lastState = (byte[])currentState.Clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
_rewindBuffer.Push(new ArraySegment<byte>(_tempBuf, 0, offset));
|
_rewindBuffer.Push(new ArraySegment<byte>(_tempBuf, 0, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RewindLarge()
|
private void RewindLarge()
|
||||||
|
@ -361,15 +348,21 @@ namespace BizHawk.Client.Common
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
byte[] buf = ms.GetBuffer();
|
||||||
var output = new MemoryStream(_lastState);
|
var output = new MemoryStream(_lastState);
|
||||||
while (ms.Position < ms.Length)
|
int index = 1;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
while (index < buf.Length)
|
||||||
{
|
{
|
||||||
var len = reader.ReadByte();
|
int deltaOffset = (int)VLInteger.ReadUnsigned(buf, ref index);
|
||||||
int offset = isSmall ? reader.ReadUInt16() : reader.ReadInt32();
|
int length = (int)VLInteger.ReadUnsigned(buf, ref index);
|
||||||
|
|
||||||
|
offset += deltaOffset;
|
||||||
|
|
||||||
output.Position = offset;
|
output.Position = offset;
|
||||||
output.Write(ms.GetBuffer(), (int)ms.Position, len);
|
output.Write(buf, index, length);
|
||||||
ms.Position += len;
|
index += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.Close();
|
reader.Close();
|
||||||
|
|
|
@ -376,18 +376,50 @@ namespace BizHawk.Common
|
||||||
|
|
||||||
public static class BitConverterLE
|
public static class BitConverterLE
|
||||||
{
|
{
|
||||||
public static void WriteBytes(ushort value, byte[] dst, int startIndex)
|
public static void WriteBytes(ushort value, byte[] dst, int index)
|
||||||
{
|
{
|
||||||
dst[startIndex ] = (byte)(value );
|
dst[index ] = (byte)(value );
|
||||||
dst[startIndex + 1] = (byte)(value >> 8);
|
dst[index + 1] = (byte)(value >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void WriteBytes(uint value, byte[] dst, int startIndex)
|
public static void WriteBytes(uint value, byte[] dst, int index)
|
||||||
{
|
{
|
||||||
dst[startIndex ] = (byte)(value );
|
dst[index ] = (byte)(value );
|
||||||
dst[startIndex + 1] = (byte)(value >> 8);
|
dst[index + 1] = (byte)(value >> 8);
|
||||||
dst[startIndex + 2] = (byte)(value >> 16);
|
dst[index + 2] = (byte)(value >> 16);
|
||||||
dst[startIndex + 3] = (byte)(value >> 24);
|
dst[index + 3] = (byte)(value >> 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class VLInteger
|
||||||
|
{
|
||||||
|
public static void WriteUnsigned(uint value, byte[] data, ref int index)
|
||||||
|
{
|
||||||
|
// This is optimized for good performance on both the x86 and x64 JITs. Don't change anything without benchmarking.
|
||||||
|
do
|
||||||
|
{
|
||||||
|
uint x = value & 0x7FU;
|
||||||
|
value >>= 7;
|
||||||
|
data[index++] = (byte)((value != 0U ? 0x80U : 0U) | x);
|
||||||
|
}
|
||||||
|
while (value != 0U);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static uint ReadUnsigned(byte[] data, ref int index)
|
||||||
|
{
|
||||||
|
// This is optimized for good performance on both the x86 and x64 JITs. Don't change anything without benchmarking.
|
||||||
|
uint value = 0U;
|
||||||
|
int shiftCount = 0;
|
||||||
|
bool isLastByte; // Negating the comparison and moving it earlier in the loop helps a lot on x86 for some reason
|
||||||
|
do
|
||||||
|
{
|
||||||
|
uint x = (uint)data[index++];
|
||||||
|
isLastByte = (x & 0x80U) == 0U;
|
||||||
|
value |= (x & 0x7FU) << shiftCount;
|
||||||
|
shiftCount += 7;
|
||||||
|
}
|
||||||
|
while (!isLastByte);
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue