jellyfin-server/Emby.Common.Implementations/IO/SharpCifs/Util/Sharpen/PipedInputStream.cs
2017-04-01 20:36:06 -04:00

173 lines
3.7 KiB
C#

using System;
using System.Threading;
namespace SharpCifs.Util.Sharpen
{
internal class PipedInputStream : InputStream
{
private byte[] _oneBuffer;
public const int PipeSize = 1024;
protected byte[] Buffer;
private bool _closed;
private ManualResetEvent _dataEvent;
private int _end;
private int _start;
private object _thisLock;
private bool _allowGrow = false;
public int In {
get { return _start; }
set { _start = value; }
}
public int Out {
get { return _end; }
set { _end = value; }
}
public PipedInputStream ()
{
_thisLock = new object ();
_dataEvent = new ManualResetEvent (false);
Buffer = new byte[PipeSize + 1];
}
public PipedInputStream (PipedOutputStream os): this ()
{
os.Attach (this);
}
public override void Close ()
{
lock (_thisLock) {
_closed = true;
_dataEvent.Set ();
}
}
public override int Available ()
{
lock (_thisLock) {
if (_start <= _end) {
return (_end - _start);
}
return ((Buffer.Length - _start) + _end);
}
}
public override int Read ()
{
if (_oneBuffer == null)
_oneBuffer = new byte[1];
if (Read (_oneBuffer, 0, 1) == -1)
return -1;
return _oneBuffer[0];
}
public override int Read (byte[] b, int offset, int len)
{
int length = 0;
do {
_dataEvent.WaitOne ();
lock (_thisLock) {
if (_closed && Available () == 0) {
return -1;
}
if (_start < _end) {
length = Math.Min (len, _end - _start);
Array.Copy (Buffer, _start, b, offset, length);
_start += length;
} else if (_start > _end) {
length = Math.Min (len, Buffer.Length - _start);
Array.Copy (Buffer, _start, b, offset, length);
len -= length;
_start = (_start + length) % Buffer.Length;
if (len > 0) {
int i = Math.Min (len, _end);
Array.Copy (Buffer, 0, b, offset + length, i);
_start += i;
length += i;
}
}
if (_start == _end && !_closed) {
_dataEvent.Reset ();
}
Monitor.PulseAll (_thisLock);
}
} while (length == 0);
return length;
}
private int Allocate (int len)
{
int alen;
while ((alen = TryAllocate (len)) == 0) {
// Wait until somebody reads data
try {
Monitor.Wait (_thisLock);
} catch {
_closed = true;
_dataEvent.Set ();
throw;
}
}
return alen;
}
int TryAllocate (int len)
{
int free;
if (_start <= _end) {
free = (Buffer.Length - _end) + _start;
} else {
free = _start - _end;
}
if (free <= len) {
if (!_allowGrow)
return free > 0 ? free - 1 : 0;
int sizeInc = (len - free) + 1;
byte[] destinationArray = new byte[Buffer.Length + sizeInc];
if (_start <= _end) {
Array.Copy (Buffer, _start, destinationArray, _start, _end - _start);
} else {
Array.Copy (Buffer, 0, destinationArray, 0, _end);
Array.Copy (Buffer, _start, destinationArray, _start + sizeInc, Buffer.Length - _start);
_start += sizeInc;
}
Buffer = destinationArray;
}
return len;
}
internal void Write (int b)
{
lock (_thisLock) {
Allocate (1);
Buffer[_end] = (byte)b;
_end = (_end + 1) % Buffer.Length;
_dataEvent.Set ();
}
}
internal void Write (byte[] b, int offset, int len)
{
do {
lock (_thisLock) {
int alen = Allocate (len);
int length = Math.Min (Buffer.Length - _end, alen);
Array.Copy (b, offset, Buffer, _end, length);
_end = (_end + length) % Buffer.Length;
if (length < alen) {
Array.Copy (b, offset + length, Buffer, 0, alen - length);
_end += alen - length;
}
_dataEvent.Set ();
len -= alen;
offset += alen;
}
} while (len > 0);
}
}
}