Skip to content

Instantly share code, notes, and snippets.

@demdxx
Created November 26, 2013 13:07
Show Gist options
  • Save demdxx/7658003 to your computer and use it in GitHub Desktop.
Save demdxx/7658003 to your computer and use it in GitHub Desktop.
using System;
using System.IO;
namespace MonoCache.Android
{
public class SeekableBufferedStream : Stream
{
private long _BufferOffset = 0;
private long _Position = 0;
private long _BufferSize = 0;
private byte[] _Buffer;
private Stream _ManagedStream;
public static Stream GetStream (Stream stream, int bufferSize = 4096)
{
if (stream.CanSeek) {
return stream;
}
return new SeekableBufferedStream (stream, bufferSize);
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { return _ManagedStream.Length; }
}
public override long Position
{
get {
return _Position + _BufferOffset;
}
set {
Seek (value, SeekOrigin.Begin);
}
}
public SeekableBufferedStream (Stream stream, int bufferSize = 4096) : base ()
{
if (bufferSize < 32) {
bufferSize = 32;
}
_ManagedStream = stream;
_Buffer = new byte[bufferSize];
_Position = 0;
_BufferOffset = 0;
_BufferSize = 0;
}
protected override void Dispose (bool disposing)
{
Flush ();
this._ManagedStream.Close ();
this._Buffer = null;
}
public override void Flush ()
{
_ManagedStream.Flush ();
_Position = 0;
_BufferOffset = 0;
_BufferSize = 0;
}
public virtual int ReadFromBuffer (byte[] buffer, int offset, int count, bool changePosition = true)
{
int ReadCount = Math.Min (count, (int)_BufferSize - (int)_BufferOffset);
if (ReadCount > 0) {
Buffer.BlockCopy (_Buffer, (int)_BufferOffset, buffer, offset, ReadCount);
if (changePosition) {
_BufferOffset += (long)ReadCount;
}
} else {
return Position >= Length ? -1 : 0;
}
return Math.Max (ReadCount, 0);
}
public virtual bool LoadNextBlock ()
{
if (_BufferSize > 0) {
_Position += _Buffer.Length;
}
_BufferSize = _ManagedStream.Read (_Buffer, 0, _Buffer.Length);
_BufferOffset = 0;
return 0 < _BufferSize;
}
public override int Read (byte[] buffer, int offset, int count)
{
int len = 0;
do {
int l = (int)ReadFromBuffer (buffer, offset, count - len, true);
if (l < 0) { break; }
len += l; offset += l;
if (len >= count) { break; }
} while (LoadNextBlock ());
return len;
}
protected virtual long BlockNumber (long offset)
{
return offset / (long)_Buffer.Length;
}
protected virtual long RealOffset (long offset, SeekOrigin origin)
{
if (SeekOrigin.Current == origin) {
offset += Position;
} else if (SeekOrigin.End == origin) {
offset = Length - offset;
}
return offset;
}
public override long Seek (long offset, SeekOrigin origin)
{
offset = RealOffset (offset, origin);
if (offset != Position) {
if (_ManagedStream.CanSeek) {
long nOffset = BlockNumber (offset) * (long)_Buffer.Length;
_Position = _ManagedStream.Seek (nOffset, SeekOrigin.Begin);
if (nOffset < offset) {
LoadNextBlock ();
_BufferOffset = offset - nOffset;
} else {
_BufferOffset = 0;
_BufferSize = 0;
}
} else if (offset >= _Position) {
if (offset <= _Position + _BufferSize) {
_BufferOffset = offset - _Position;
} else {
while (LoadNextBlock ()) {
if (offset <= _Position + _BufferSize) {
_BufferOffset = offset - _Position;
}
}
}
} else {
throw new IndexOutOfRangeException (String.Format (
"Do not fall into the range buffer. Buffer start: {0} Move from {1} to {2}",
_Position, Position, offset
));
}
}
if (Position != offset) {
throw new ArgumentOutOfRangeException ("Invalid offset.");
}
return Position;
}
public override void SetLength (long value)
{
throw new NotSupportedException ("Can`t change buffer size");
}
public override void Write (byte[] buffer, int offset, int count)
{
throw new NotSupportedException ("Unsupport method");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment