private unsafe void CaptureStateDelta(byte[] currentState) { // Keep in mind that everything captured here is intended to be played back in // reverse. The goal is, given the current state, how to get back to the previous // state. That's why the data portion of the delta comes from the previous state, // and also why the previous state is used if we have to bail out and capture the // full state instead. if (currentState.Length != _lastState.Length) { // If the state sizes mismatch, capture a full state rather than trying to do anything clever goto CaptureFullState; } if (currentState.Length == 0) { // handle empty states as a "full" (empty) state goto CaptureFullState; } int index = 0; int stateLength = Math.Min(currentState.Length, _lastState.Length); bool inChangeSequence = false; int changeSequenceStartOffset = 0; int lastChangeSequenceStartOffset = 0; if (_deltaBuffer.Length < stateLength + 1) { _deltaBuffer = new byte[stateLength + 1]; } _deltaBuffer[index++] = 0; // Full state = false (i.e. delta) fixed(byte *pCurrentState = ¤tState[0]) fixed(byte *pLastState = &_lastState[0]) for (int i = 0; i < stateLength; i++) { bool thisByteMatches = *(pCurrentState + i) == *(pLastState + i); if (inChangeSequence == false) { if (thisByteMatches) { continue; } inChangeSequence = true; changeSequenceStartOffset = i; } if (thisByteMatches || i == stateLength - 1) { const int MaxHeaderSize = 10; int length = i - changeSequenceStartOffset + (thisByteMatches ? 0 : 1); if (index + length + MaxHeaderSize >= stateLength) { // If the delta ends up being larger than the full state, capture the full state instead goto CaptureFullState; } // Offset Delta VLInteger.WriteUnsigned((uint)(changeSequenceStartOffset - lastChangeSequenceStartOffset), _deltaBuffer, ref index); // Length VLInteger.WriteUnsigned((uint)length, _deltaBuffer, ref index); // Data Buffer.BlockCopy(_lastState, changeSequenceStartOffset, _deltaBuffer, index, length); index += length; inChangeSequence = false; lastChangeSequenceStartOffset = changeSequenceStartOffset; } } _rewindBuffer.Push(new ArraySegment <byte>(_deltaBuffer, 0, index)); UpdateLastState(currentState); return; CaptureFullState: CaptureStateNonDelta(_lastState); UpdateLastState(currentState); }
private unsafe void CaptureRewindStateDelta(byte[] currentState) { // in case the state sizes mismatch, capture a full state rather than trying to do anything clever if (currentState.Length != _lastState.Length) { CaptureRewindStateNonDelta(_lastState); UpdateLastState(currentState); return; } int index = 0; int stateLength = Math.Min(currentState.Length, _lastState.Length); bool inChangeSequence = false; int changeSequenceStartOffset = 0; int lastChangeSequenceStartOffset = 0; if (_deltaBuffer.Length < stateLength) { _deltaBuffer = new byte[stateLength]; } _deltaBuffer[index++] = 0; // Full state = false (i.e. delta) fixed(byte *pCurrentState = ¤tState[0]) fixed(byte *pLastState = &_lastState[0]) for (int i = 0; i < stateLength; i++) { bool thisByteMatches = *(pCurrentState + i) == *(pLastState + i); if (inChangeSequence == false) { if (thisByteMatches) { continue; } inChangeSequence = true; changeSequenceStartOffset = i; } if (thisByteMatches || i == stateLength - 1) { const int maxHeaderSize = 10; int length = i - changeSequenceStartOffset + (thisByteMatches ? 0 : 1); if (index + length + maxHeaderSize >= stateLength) { // If the delta ends up being larger than the full state, capture the full state instead CaptureRewindStateNonDelta(_lastState); UpdateLastState(currentState); return; } // Offset Delta VLInteger.WriteUnsigned((uint)(changeSequenceStartOffset - lastChangeSequenceStartOffset), _deltaBuffer, ref index); // Length VLInteger.WriteUnsigned((uint)length, _deltaBuffer, ref index); // Data Buffer.BlockCopy(_lastState, changeSequenceStartOffset, _deltaBuffer, index, length); index += length; inChangeSequence = false; lastChangeSequenceStartOffset = changeSequenceStartOffset; } } _rewindBuffer.Push(new ArraySegment <byte>(_deltaBuffer, 0, index)); UpdateLastState(currentState); }