|
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace QiHe.CodeLib.Compress
{
/// <summary>
/// pattern:
/// Use two sections of rails to carry a moving train.
/// </summary>
public class DoubleBufferedStreamReader
{
Stream baseStream;
int bufferSize;
int markerPos;
int position;
public int dataLength;
long basePosition;
byte[] firstBuffer;
byte[] secondBuffer;
public enum WorkMode { Reading, Seeking }
bool isReading = true;
/// <summary>
/// In Reading mode, when cursor goes beyond backwardSize the bufer swap and new data is fetched.
/// In Seeking mode, the cursor only goes within the buffer.
/// </summary>
public WorkMode Mode
{
get
{
if (isReading)
{
return WorkMode.Reading;
}
else
{
return WorkMode.Seeking;
}
}
set
{
switch (value)
{
case WorkMode.Reading:
isReading = true;
break;
case WorkMode.Seeking:
isReading = false;
break;
default:
throw new Exception("Invalid WorkMode value.");
}
}
}
/// <summary>
/// constrain:
/// At any reading position, the seeking range should
/// between CurrentPosition - backwardSize and CurrentPosition + forwardSize
/// and within the BaseStream.
/// </summary>
/// <param name="stream"></param>
/// <param name="forwardSize"></param>
/// <param name="backwardSize"></param>
public DoubleBufferedStreamReader(Stream stream, int forwardSize, int backwardSize)
{
this.baseStream = stream;
this.bufferSize = forwardSize + backwardSize;
this.markerPos = bufferSize + backwardSize;
this.firstBuffer = new byte[bufferSize];
this.secondBuffer = new byte[bufferSize];
BeginRead();
}
public void BeginRead()
{
if (dataLength == 0)
{
if (baseStream.Position > 0) //need test
{
long pos = Math.Max(0, baseStream.Position - bufferSize);
int count = (int)(baseStream.Position - pos);
baseStream.Position = pos;
baseStream.Read(firstBuffer, bufferSize - count, count);
}
FillBuffer();
position = bufferSize;
}
}
public byte PeekByte()
{
if (position < bufferSize)
{
return firstBuffer[position];
}
else
{
return secondBuffer[position - bufferSize];
}
}
public byte ReadByte()
{
byte value = PeekByte();
position++;
CheckPosition();
return value;
}
private void CheckPosition()
{
if (ShouldSwapBuffer())
{
SwapBuffer();
}
}
bool ShouldSwapBuffer()
{
return isReading && position >= markerPos && dataLength == bufferSize;
}
bool OnFirstBuffer(int position)
{
if (position >= 0 && position < bufferSize)
{
return true;
}
else if (position >= bufferSize && position <= bufferSize + dataLength)
{
return false;
}
else //Invalid buffer position.
{
throw new Exception("Out of seeking range.");
}
}
private void SwapBuffer()
{
byte[] temp = firstBuffer;
firstBuffer = secondBuffer;
secondBuffer = temp;
position -= bufferSize;
FillBuffer();
}
private void FillBuffer()
{
dataLength = baseStream.Read(secondBuffer, 0, bufferSize);
basePosition = baseStream.Position;
}
public byte[] ReadBytes(int count)
{
byte[] data = PeekBytes(count);
position += count;
CheckPosition();
return data;
}
public byte[] PeekBytes(int count)
{
if (count < 0)
{
throw new ArgumentOutOfRangeException("count can't be negative");
}
else if (position + count > bufferSize + dataLength)
{
//count too big
count = bufferSize + dataLength - position;
}
byte[] data = new byte[count];
if (position + count <= bufferSize)
{
Buffer.BlockCopy(firstBuffer, position, data, 0, count);
}
else// if (position + count <= bufferSize + dataLength)
{
if (position < bufferSize)
{
int firstBytes = bufferSize - position;
Buffer.BlockCopy(firstBuffer, position, data, 0, firstBytes);
Buffer.BlockCopy(secondBuffer, 0, data, firstBytes, count - firstBytes);
}
else
{
Buffer.BlockCopy(secondBuffer, position - bufferSize, data, 0, count);
}
}
return data;
}
public byte[] ReadToEnd()
{
int bytesRemained = (int)(Length - Position);
return ReadBytes(bytesRemained);
}
public long Position
{
get
{
long delta = bufferSize + dataLength - position;
return basePosition - delta;
}
set
{
int delta = (int)(value - this.Position);
position += delta;
CheckPosition();
}
}
public long Length
{
get { return baseStream.Length; }
}
public bool IsEndReached
{
get { return Position == Length; }
}
public Stream BaseStream
{
get { return baseStream; }
set { baseStream = value; }
}
/// <summary>
/// clear buffer and fix stream position.
/// To read again, call BeginRead().
/// </summary>
public void EndRead()
{
baseStream.Position = this.Position;
position = -1;
dataLength = 0;
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.