Created
November 26, 2013 13:07
-
-
Save demdxx/7658003 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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