Description
When calling WriteByte on a BufferedStream an implicit Flush will be called on the wrapped stream if the internal buffer is full. This is different from all other Write methods on BufferedStream and is not even mentioned in the documentation.
This can be problematic if the wrapped stream or streams have properties that are suspencible to Flushes (see repo steps for an example).
The practical situation is even worse because for lots of writer abstractions (e.g. BinaryWriter) it is mostly an implementation specific if Write or WriteByte is used. So in effect it is mostly "random" if Flushes happen when writing to an underlying BufferedStream.
Reproduction Steps
Please consider the following code:
var latencyStream = new SomeStreamWhereCallingWithSmallBuffersOrFlushesIsExtremelyExpensive();
var specialLatencyBufferStream = new BufferedStream(latencyStream, 1024 * 1024 * 100);
var compressionStream = new DeflateStream(specialLatencyBufferStream, CompressionMode.Compress, true);
var bufferedStream = new BufferedStream(compressionStream, 1024 * 8);
If you are now serializing with a BinaryWriter on bufferedStream it will effectively bypass the specialLatencyBufferStream "randomly" (depending on whether WriteByte or any of the other Write methods are used when surpassing the 8kb bufferedStream)
Expected behavior
Write and WriteByte behave the same and don't Flush
Actual behavior
Write methods don't Flush, WriteByte does
Regression?
no
Known Workarounds
Not calling WriteByte
Risks of change
Applications might depend on the behavior. However given this is not documented and mostly "random" in effect this is very unlikely.
Other information
The reason for the behavior is in BufferedStream.cs:
private void WriteByteSlow(byte value)
{
EnsureNotClosed();
if (_writePos == 0)
{
EnsureCanWrite();
ClearReadBufferBeforeWrite();
EnsureBufferAllocated();
}
// We should not be flushing here, but only writing to the underlying stream, but previous version flushed, so we keep this.
if (_writePos >= _bufferSize - 1)
FlushWrite();
_buffer![_writePos++] = value;
Debug.Assert(_writePos < _bufferSize);
}
Description
When calling WriteByte on a BufferedStream an implicit Flush will be called on the wrapped stream if the internal buffer is full. This is different from all other Write methods on BufferedStream and is not even mentioned in the documentation.
This can be problematic if the wrapped stream or streams have properties that are suspencible to Flushes (see repo steps for an example).
The practical situation is even worse because for lots of writer abstractions (e.g. BinaryWriter) it is mostly an implementation specific if Write or WriteByte is used. So in effect it is mostly "random" if Flushes happen when writing to an underlying BufferedStream.
Reproduction Steps
Please consider the following code:
If you are now serializing with a BinaryWriter on bufferedStream it will effectively bypass the specialLatencyBufferStream "randomly" (depending on whether WriteByte or any of the other Write methods are used when surpassing the 8kb bufferedStream)
Expected behavior
Write and WriteByte behave the same and don't Flush
Actual behavior
Write methods don't Flush, WriteByte does
Regression?
no
Known Workarounds
Not calling WriteByte
Risks of change
Applications might depend on the behavior. However given this is not documented and mostly "random" in effect this is very unlikely.
Other information
The reason for the behavior is in BufferedStream.cs: