public override void Write(byte[] buffer, int offset, int count)
{
bool mustWait = false;
// This method does this:
// 0. handles any pending exceptions
// 1. write any buffers that are ready to be written,
// 2. fills a work buffer; when full, flip state to 'Filled',
// 3. if more data to be written, goto step 1
if (_isClosed)
throw new InvalidOperationException();
// dispense any exceptions that occurred on the BG threads
if (_pendingException != null)
{
_handlingException = true;
var pe = _pendingException;
_pendingException = null;
throw pe;
}
if (count == 0) return;
if (!_firstWriteDone)
{
// Want to do this on first Write, first session, and not in the
// constructor. We want to allow MaxBufferPairs to
// change after construction, but before first Write.
_InitializePoolOfWorkItems();
_firstWriteDone = true;
}
do
{
// may need to make buffers available
EmitPendingBuffers(false, mustWait);
mustWait = false;
// use current buffer, or get a new buffer to fill
int ix = -1;
if (_currentlyFilling >= 0)
{
ix = _currentlyFilling;
TraceOutput(TraceBits.WriteTake,
"Write notake wi({0}) lf({1})",
ix,
_lastFilled);
}
else
{
TraceOutput(TraceBits.WriteTake, "Write take?");
if (_toFill.Count == 0)
{
// no available buffers, so... need to emit
// compressed buffers.
mustWait = true;
continue;
}
ix = _toFill.Dequeue();
TraceOutput(TraceBits.WriteTake,
"Write take wi({0}) lf({1})",
ix,
_lastFilled);
++_lastFilled; // TODO: consider rollover?
}
WorkItem workitem = _pool[ix];
int limit = ((workitem.buffer.Length - workitem.inputBytesAvailable) > count)
? count
: (workitem.buffer.Length - workitem.inputBytesAvailable);
workitem.ordinal = _lastFilled;
TraceOutput(TraceBits.Write,
"Write lock wi({0}) ord({1}) iba({2})",
workitem.index,
workitem.ordinal,
workitem.inputBytesAvailable
);
// copy from the provided buffer to our workitem, starting at
// the tail end of whatever data we might have in there currently.
Buffer.BlockCopy(buffer,
offset,
workitem.buffer,
workitem.inputBytesAvailable,
limit);
count -= limit;
offset += limit;
workitem.inputBytesAvailable += limit;
if (workitem.inputBytesAvailable == workitem.buffer.Length)
{
// No need for interlocked.increment: the Write()
// method is documented as not multi-thread safe, so
// we can assume Write() calls come in from only one
// thread.
TraceOutput(TraceBits.Write,
"Write QUWI wi({0}) ord({1}) iba({2}) nf({3})",
workitem.index,
workitem.ordinal,
workitem.inputBytesAvailable );
#if !PCL
if (!ThreadPool.QueueUserWorkItem( _DeflateOne, workitem ))
throw new Exception("Cannot enqueue workitem");
#else
System.Threading.Tasks.Task.Run(() => _DeflateOne(workitem));
#endif
_currentlyFilling = -1; // will get a new buffer next time
}
else
_currentlyFilling = ix;
if (count > 0)
TraceOutput(TraceBits.WriteEnter, "Write more");
}
while (count > 0); // until no more to write
TraceOutput(TraceBits.WriteEnter, "Write exit");
return;
}