public override int EndRead(IAsyncResult asyncResult) =>
TaskToApm.End<int>(asyncResult);
- public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ private ValueTask<int> ReadAsyncInternal(Memory<byte> buffer, CancellationToken cancellationToken)
{
- // We use this checking order for compat to earlier versions:
- if (_asyncOperations != 0)
- throw new InvalidOperationException(SR.InvalidBeginCall);
-
- ValidateBufferArguments(buffer, offset, count);
- EnsureNotDisposed();
-
if (cancellationToken.IsCancellationRequested)
{
- return Task.FromCanceled<int>(cancellationToken);
+ return ValueTask.FromCanceled<int>(cancellationToken);
}
Interlocked.Increment(ref _asyncOperations);
- Task<int>? readTask = null;
+ bool startedAsyncWork = false;
try
{
// Try to read decompressed data in output buffer
- int bytesRead = _inflater.Inflate(buffer, offset, count);
+ int bytesRead = _inflater.Inflate(buffer);
if (bytesRead != 0)
{
// If decompression output buffer is not empty, return immediately.
- return Task.FromResult(bytesRead);
+ return ValueTask.FromResult(bytesRead);
}
if (_inflater.Finished())
{
// end of compression stream
- return Task.FromResult(0);
+ return ValueTask.FromResult(0);
}
// If there is no data on the output buffer and we are not at
// the end of the stream, we need to get more data from the base stream
- readTask = _stream!.ReadAsync(_buffer, 0, _buffer.Length, cancellationToken);
- if (readTask == null)
- {
- throw new InvalidOperationException(SR.NotSupported_UnreadableStream);
- }
+ ValueTask<int> readTask = _stream!.ReadAsync(_buffer.AsMemory(), cancellationToken);
+ startedAsyncWork = true;
- return ReadAsyncCore(readTask, buffer, offset, count, cancellationToken);
+ return ReadAsyncCore(readTask, buffer, cancellationToken);
}
finally
{
// if we haven't started any async work, decrement the counter to end the transaction
- if (readTask == null)
+ if (!startedAsyncWork)
{
Interlocked.Decrement(ref _asyncOperations);
}
}
}
- private async Task<int> ReadAsyncCore(Task<int> readTask, byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ private async ValueTask<int> ReadAsyncCore(ValueTask<int> readTask, Memory<byte> buffer, CancellationToken cancellationToken)
{
try
{
// Feed the data from base stream into decompression engine
_inflater.SetInput(_buffer, 0, bytesRead);
- bytesRead = _inflater.Inflate(buffer, offset, count);
+ bytesRead = _inflater.Inflate(buffer);
if (bytesRead == 0 && !_inflater.Finished())
{
// We could have read in head information and didn't get any data.
// Read from the base stream again.
- readTask = _stream!.ReadAsync(_buffer, 0, _buffer.Length, cancellationToken);
- if (readTask == null)
- {
- throw new InvalidOperationException(SR.NotSupported_UnreadableStream);
- }
+ readTask = _stream!.ReadAsync(_buffer.AsMemory(), cancellationToken);
}
else
{
}
}
+ public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ // We use this checking order for compat to earlier versions:
+ if (_asyncOperations != 0)
+ throw new InvalidOperationException(SR.InvalidBeginCall);
+
+ ValidateBufferArguments(buffer, offset, count);
+ EnsureNotDisposed();
+
+ return ReadAsyncInternal(buffer.AsMemory(offset, count), cancellationToken).AsTask();
+ }
+
+ public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+ {
+ // We use this checking order for compat to earlier versions:
+ if (_asyncOperations != 0)
+ throw new InvalidOperationException(SR.InvalidBeginCall);
+
+ EnsureNotDisposed();
+
+ return ReadAsyncInternal(buffer, cancellationToken);
+ }
+
public override void Write(byte[] buffer, int offset, int count)
{
throw new InvalidOperationException(SR.CannotWriteToDeflateStream);
_state = InflaterState.ReadingBFinal; // start by reading BFinal bit
}
+ public void SetInput(Memory<byte> inputBytes) => _input.SetInput(inputBytes);
+
public void SetInput(byte[] inputBytes, int offset, int length) =>
_input.SetInput(inputBytes, offset, length); // append the bytes
public int AvailableOutput => _output.AvailableBytes;
- public int Inflate(byte[] bytes, int offset, int length)
+ public int Inflate(Memory<byte> bytes)
{
// copy bytes from output to outputbytes if we have available bytes
// if buffer is not filled up. keep decoding until no input are available
int copied = 0;
if (_uncompressedSize == -1)
{
- copied = _output.CopyTo(bytes, offset, length);
+ copied = _output.CopyTo(bytes);
}
else
{
if (_uncompressedSize > _currentInflatedCount)
{
- length = (int)Math.Min(length, _uncompressedSize - _currentInflatedCount);
- copied = _output.CopyTo(bytes, offset, length);
+ bytes = bytes.Slice(0, (int)Math.Min(bytes.Length, _uncompressedSize - _currentInflatedCount));
+ copied = _output.CopyTo(bytes);
_currentInflatedCount += copied;
}
else
}
if (copied > 0)
{
- offset += copied;
+ bytes = bytes.Slice(copied, bytes.Length - copied);
count += copied;
- length -= copied;
}
- if (length == 0)
- { // filled in the bytes array
+ if (bytes.IsEmpty)
+ {
+ // filled in the bytes buffer
break;
}
// Decode will return false when more input is needed
return count;
}
+ public int Inflate(byte[] bytes, int offset, int length) => Inflate(bytes.AsMemory(offset, length));
+
//Each block of compressed data begins with 3 header bits
// containing the following data:
// first bit BFINAL
internal sealed class InputBuffer
{
- private byte[]? _buffer; // byte array to store input
- private int _start; // start poisition of the buffer
- private int _end; // end position of the buffer
+ private Memory<byte> _buffer; // memory to store input
private uint _bitBuffer; // store the bits here, we can quickly shift in this buffer
private int _bitsInBuffer; // number of bits available in bitBuffer
public int AvailableBits => _bitsInBuffer;
/// <summary>Total bytes available in the input buffer.</summary>
- public int AvailableBytes => (_end - _start) + (_bitsInBuffer / 8);
+ public int AvailableBytes => _buffer.Length + (_bitsInBuffer / 8);
/// <summary>Ensure that count bits are in the bit buffer.</summary>
/// <param name="count">Can be up to 16.</param>
{
return false;
}
- Debug.Assert(_buffer != null);
+
// insert a byte to bitbuffer
- _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
+ _bitBuffer |= (uint)_buffer.Span[0] << _bitsInBuffer;
+ _buffer = _buffer.Slice(1);
_bitsInBuffer += 8;
if (_bitsInBuffer < count)
return false;
}
// insert a byte to bitbuffer
- _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
+ _bitBuffer |= (uint)_buffer.Span[0] << _bitsInBuffer;
+ _buffer = _buffer.Slice(1);
_bitsInBuffer += 8;
}
}
/// </summary>
public uint TryLoad16Bits()
{
- Debug.Assert(_buffer != null);
if (_bitsInBuffer < 8)
{
- if (_start < _end)
+ if (_buffer.Length > 1)
{
- _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
- _bitsInBuffer += 8;
+ Span<byte> span = _buffer.Span;
+ _bitBuffer |= (uint)span[0] << _bitsInBuffer;
+ _bitBuffer |= (uint)span[1] << (_bitsInBuffer + 8);
+ _buffer = _buffer.Slice(2);
+ _bitsInBuffer += 16;
}
-
- if (_start < _end)
+ else if (_buffer.Length != 0)
{
- _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
+ _bitBuffer |= (uint)_buffer.Span[0] << _bitsInBuffer;
+ _buffer = _buffer.Slice(1);
_bitsInBuffer += 8;
}
}
else if (_bitsInBuffer < 16)
{
- if (_start < _end)
+ if (!_buffer.IsEmpty)
{
- _bitBuffer |= (uint)_buffer[_start++] << _bitsInBuffer;
+ _bitBuffer |= (uint)_buffer.Span[0] << _bitsInBuffer;
+ _buffer = _buffer.Slice(1);
_bitsInBuffer += 8;
}
}
}
/// <summary>
- /// Copies length bytes from input buffer to output buffer starting at output[offset].
+ /// Copies bytes from input buffer to output buffer.
/// You have to make sure, that the buffer is byte aligned. If not enough bytes are
/// available, copies fewer bytes.
/// </summary>
/// <returns>Returns the number of bytes copied, 0 if no byte is available.</returns>
- public int CopyTo(byte[] output, int offset, int length)
+ public int CopyTo(Memory<byte> output)
{
- Debug.Assert(output != null);
- Debug.Assert(offset >= 0);
- Debug.Assert(length >= 0);
- Debug.Assert(offset <= output.Length - length);
- Debug.Assert((_bitsInBuffer % 8) == 0);
+ Debug.Assert(_bitsInBuffer % 8 == 0);
// Copy the bytes in bitBuffer first.
int bytesFromBitBuffer = 0;
- while (_bitsInBuffer > 0 && length > 0)
+ while (_bitsInBuffer > 0 && !output.IsEmpty)
{
- output[offset++] = (byte)_bitBuffer;
+ output.Span[0] = (byte)_bitBuffer;
+ output = output.Slice(1);
_bitBuffer >>= 8;
_bitsInBuffer -= 8;
- length--;
bytesFromBitBuffer++;
}
- if (length == 0)
+ if (output.IsEmpty)
{
return bytesFromBitBuffer;
}
- int avail = _end - _start;
- if (length > avail)
- {
- length = avail;
- }
-
- Debug.Assert(_buffer != null);
- Array.Copy(_buffer, _start, output, offset, length);
- _start += length;
+ int length = Math.Min(output.Length, _buffer.Length);
+ _buffer.Slice(0, length).CopyTo(output);
+ _buffer = _buffer.Slice(length);
return bytesFromBitBuffer + length;
}
/// <summary>
+ /// Copies length bytes from input buffer to output buffer starting at output[offset].
+ /// You have to make sure, that the buffer is byte aligned. If not enough bytes are
+ /// available, copies fewer bytes.
+ /// </summary>
+ /// <returns>Returns the number of bytes copied, 0 if no byte is available.</returns>
+ public int CopyTo(byte[] output, int offset, int length)
+ {
+ Debug.Assert(output != null);
+ Debug.Assert(offset >= 0);
+ Debug.Assert(length >= 0);
+ Debug.Assert(offset <= output.Length - length);
+ Debug.Assert((_bitsInBuffer % 8) == 0);
+
+ return CopyTo(output.AsMemory(offset, length));
+ }
+
+ /// <summary>
/// Return true is all input bytes are used.
/// This means the caller can call SetInput to add more input.
/// </summary>
- public bool NeedsInput() => _start == _end;
+ public bool NeedsInput() => _buffer.IsEmpty;
+
+ /// <summary>
+ /// Set the byte buffer to be processed.
+ /// All the bits remained in bitbuffer will be processed before the new bytes.
+ /// We don't clone the byte buffer here since it is expensive.
+ /// The caller should make sure after a buffer is passed in, that
+ /// it will not be changed before calling this function again.
+ /// </summary>
+ public void SetInput(Memory<byte> buffer)
+ {
+ if (_buffer.IsEmpty)
+ {
+ _buffer = buffer;
+ }
+ }
/// <summary>
/// Set the byte array to be processed.
Debug.Assert(length >= 0);
Debug.Assert(offset <= buffer.Length - length);
- if (_start == _end)
- {
- _buffer = buffer;
- _start = offset;
- _end = offset + length;
- }
+ SetInput(buffer.AsMemory(offset, length));
}
/// <summary>Skip n bits in the buffer.</summary>
/// <summary>Bytes not consumed in output window.</summary>
public int AvailableBytes => _bytesUsed;
- /// <summary>Copy the decompressed bytes to output array.</summary>
- public int CopyTo(byte[] output, int offset, int length)
+ /// <summary>Copy the decompressed bytes to output buffer.</summary>
+ public int CopyTo(Memory<byte> output)
{
int copy_end;
- if (length > _bytesUsed)
+ if (output.Length > _bytesUsed)
{
// we can copy all the decompressed bytes out
copy_end = _end;
- length = _bytesUsed;
+ output = output.Slice(0, _bytesUsed);
}
else
{
- copy_end = (_end - _bytesUsed + length) & WindowMask; // copy length of bytes
+ copy_end = (_end - _bytesUsed + output.Length) & WindowMask; // copy length of bytes
}
- int copied = length;
+ int copied = output.Length;
- int tailLen = length - copy_end;
+ int tailLen = output.Length - copy_end;
if (tailLen > 0)
{
// this means we need to copy two parts separately
- // copy tailLen bytes from the end of output window
- Array.Copy(_window, WindowSize - tailLen,
- output, offset, tailLen);
- offset += tailLen;
- length = copy_end;
+ // copy the taillen bytes from the end of the output window
+ _window.AsSpan(WindowSize - tailLen, tailLen).CopyTo(output.Span);
+ output = output.Slice(tailLen, copy_end);
}
- Array.Copy(_window, copy_end - length, output, offset, length);
+ _window.AsSpan(copy_end - output.Length, output.Length).CopyTo(output.Span);
_bytesUsed -= copied;
Debug.Assert(_bytesUsed >= 0, "check this function and find why we copied more bytes than we have");
return copied;
}
+
+ /// <summary>Copy the decompressed bytes to output array.</summary>
+ public int CopyTo(byte[] output, int offset, int length)
+ {
+ return CopyTo(output.AsMemory(offset, length));
+ }
}
}