namespace RsyncNet.Libraries
{
using System;
using System.IO;
using Helpers;
public class SlidingStreamBuffer
{
private readonly byte[] _buffer;
private readonly int _extraBuffer;
private readonly Stream _stream;
private readonly int _window;
private int _bufferPosition;
private int _totalValidBytesInBuffer;
private bool _warmupCalled;
public SlidingStreamBuffer(Stream stream, int window)
: this(stream, window, window)
{
}
public SlidingStreamBuffer(Stream stream, int window, int extraBuffer)
{
if (stream == null) throw new ArgumentNullException("stream");
if (window < 1) throw new ArgumentException("window must be > 0");
_bufferPosition = 0;
_totalValidBytesInBuffer = 0;
_warmupCalled = false;
_stream = stream;
_window = window;
_extraBuffer = extraBuffer;
_buffer = new byte[window + extraBuffer];
}
#region Properties, indexers, events and operators: public
///
/// Returns the byte at position i
///
///
///
public byte this[int i]
{
get { return GetByteAt(i); }
}
#endregion
#region Methods: public
public byte[] GetBuffer()
{
if (!_warmupCalled) Warmup();
// wait for async read, if any, to complete
var retBuffer = new byte[GetNumBytesAvailable()];
Array.Copy(_buffer, _bufferPosition, retBuffer, 0, retBuffer.Length);
return retBuffer;
}
///
/// Gets a byte at an offset between [0, window>
///
///
///
public byte GetByteAt(int offset)
{
if (!_warmupCalled) Warmup();
// wait for async read, if any, to complete
if (offset < 0) throw new IndexOutOfRangeException();
if (offset > GetNumBytesAvailable() - 1) throw new IndexOutOfRangeException();
return _buffer[offset + _bufferPosition];
}
///
/// Returns value between [0, window]
///
///
public long GetNumBytesAvailable()
{
return MathEx.Bounded(0, _window, _totalValidBytesInBuffer - _bufferPosition);
}
///
/// Slides the window n byte(s) forward
///
public void MoveForward(int n)
{
if (n <= 0) throw new ArgumentException("n must be a positive number larger than zero");
_bufferPosition += n;
if (_totalValidBytesInBuffer - _bufferPosition < _window)
{
// begin data fetch
LeftShiftBuffer();
FillBuffer();
}
}
///
/// Pulls some initial data from the stream
///
public void Warmup()
{
FillBuffer();
_warmupCalled = true;
}
#endregion
#region Methods: private
private void FillBuffer()
{
int bytesToRead = (_window + _extraBuffer) - _totalValidBytesInBuffer;
if (bytesToRead <= 0) return;
int read = _stream.Read(_buffer, _totalValidBytesInBuffer, bytesToRead);
_totalValidBytesInBuffer += read;
}
private void LeftShiftBuffer()
{
if (_bufferPosition < _totalValidBytesInBuffer)
{
Array.Copy(_buffer, _bufferPosition, _buffer, 0, _totalValidBytesInBuffer - _bufferPosition);
_totalValidBytesInBuffer -= _bufferPosition;
}
else
{
_totalValidBytesInBuffer = 0;
}
_bufferPosition = 0;
}
#endregion
}
}