public override void Write(byte[] array, int offset, int count)
{
ValidateReadWriteArgs(array, offset, count);
if (_writePos == 0)
{
// Ensure we can write to the stream, and ready buffer for writing.
if (!CanWrite) throw Error.GetWriteNotSupported();
if (_readPos < _readLength) FlushReadBuffer();
_readPos = 0;
_readLength = 0;
}
// If our buffer has data in it, copy data from the user's array into
// the buffer, and if we can fit it all there, return. Otherwise, write
// the buffer to disk and copy any remaining data into our buffer.
// The assumption here is memcpy is cheaper than disk (or net) IO.
// (10 milliseconds to disk vs. ~20-30 microseconds for a 4K memcpy)
// So the extra copying will reduce the total number of writes, in
// non-pathological cases (i.e. write 1 byte, then write for the buffer
// size repeatedly)
if (_writePos > 0)
{
int numBytes = _bufferLength - _writePos; // space left in buffer
if (numBytes > 0)
{
if (numBytes > count)
numBytes = count;
Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, numBytes);
_writePos += numBytes;
if (count == numBytes) return;
offset += numBytes;
count -= numBytes;
}
// Reset our buffer. We essentially want to call FlushWrite
// without calling Flush on the underlying Stream.
if (_useAsyncIO)
{
WriteInternalCoreAsync(GetBuffer(), 0, _writePos, CancellationToken.None).GetAwaiter().GetResult();
}
else
{
WriteCore(GetBuffer(), 0, _writePos);
}
_writePos = 0;
}
// If the buffer would slow writes down, avoid buffer completely.
if (count >= _bufferLength)
{
Debug.Assert(_writePos == 0, "FileStream cannot have buffered data to write here! Your stream will be corrupted.");
WriteCore(array, offset, count);
return;
}
else if (count == 0)
{
return; // Don't allocate a buffer then call memcpy for 0 bytes.
}
// Copy remaining bytes into buffer, to write at a later date.
Buffer.BlockCopy(array, offset, GetBuffer(), _writePos, count);
_writePos = count;
return;
}