public override int Read(Span <byte> buffer) { if (_mode != CompressionMode.Decompress) { throw new InvalidOperationException(SR.BrotliStream_Compress_UnsupportedOperation); } EnsureNotDisposed(); int totalWritten = 0; Span <byte> source = Span <byte> .Empty; OperationStatus lastResult = OperationStatus.DestinationTooSmall; // We want to continue calling Decompress until we're either out of space for output or until Decompress indicates it is finished. while (buffer.Length > 0 && lastResult != OperationStatus.Done) { int bytesConsumed = 0; int bytesWritten = 0; if (lastResult == OperationStatus.NeedMoreData) { int readBytes = 0; int iter = 0; while (readBytes < _buffer.Length && ((iter = _stream.Read(_buffer, readBytes, _buffer.Length - readBytes)) > 0)) { readBytes += iter; if (readBytes > _buffer.Length) { // The stream is either malicious or poorly implemented and returned a number of // bytes larger than the buffer supplied to it. throw new InvalidDataException(SR.BrotliStream_Decompress_InvalidStream); } } if (readBytes <= 0) { break; } source = new Span <byte>(_buffer, 0, readBytes); } lastResult = _decoder.Decompress(source, buffer, out bytesConsumed, out bytesWritten); if (lastResult == OperationStatus.InvalidData) { throw new InvalidOperationException(SR.BrotliStream_Decompress_InvalidData); } if (bytesConsumed > 0) { source = source.Slice(bytesConsumed); } if (bytesWritten > 0) { totalWritten += bytesWritten; buffer = buffer.Slice(bytesWritten); } } return(totalWritten); }
public void Decompress_Canterbury_WithState(string uncompressedFileName) { int innerIterations = (int)Benchmark.InnerIterationCount; byte[] compressedBytes = File.ReadAllBytes(CompressedTestFile(uncompressedFileName)); ReadOnlySpan <byte> compressedData = new ReadOnlySpan <byte>(compressedBytes); List <byte[]> uncompressedDataArrays = new List <byte[]>(innerIterations); foreach (var iteration in Benchmark.Iterations) { for (int i = 0; i < innerIterations; i++) { uncompressedDataArrays.Add(new byte[65520]); } using (iteration.StartMeasurement()) { for (int i = 0; i < innerIterations; i++) { using (BrotliDecoder decoder = new BrotliDecoder()) { Span <byte> output = new Span <byte>(uncompressedDataArrays[i]); ReadOnlySpan <byte> input = compressedData; while (!input.IsEmpty && !output.IsEmpty) { decoder.Decompress(input, output, out int bytesConsumed, out int written); input = input.Slice(bytesConsumed); output = output.Slice(written); } } } } } }
public void RoundTrip_Chunks() { int chunkSize = 100; int totalSize = 20000; BrotliEncoder encoder = default; BrotliDecoder decoder = default; for (int i = 0; i < totalSize; i += chunkSize) { byte[] uncompressed = new byte[chunkSize]; new Random().NextBytes(uncompressed); byte[] compressed = new byte[BrotliEncoder.GetMaxCompressedLength(chunkSize)]; byte[] decompressed = new byte[chunkSize]; var uncompressedSpan = new ReadOnlySpan <byte>(uncompressed); var compressedSpan = new Span <byte>(compressed); var decompressedSpan = new Span <byte>(decompressed); int totalWrittenThisIteration = 0; var compress = encoder.Compress(uncompressedSpan, compressedSpan, out int bytesConsumed, out int bytesWritten, isFinalBlock: false); totalWrittenThisIteration += bytesWritten; compress = encoder.Flush(compressedSpan.Slice(bytesWritten), out bytesWritten); totalWrittenThisIteration += bytesWritten; var res = decoder.Decompress(compressedSpan.Slice(0, totalWrittenThisIteration), decompressedSpan, out int decompressbytesConsumed, out int decompressbytesWritten); Assert.Equal(totalWrittenThisIteration, decompressbytesConsumed); Assert.Equal(bytesConsumed, decompressbytesWritten); Assert.Equal <byte>(uncompressed, decompressedSpan.ToArray()); } }
private static void Decompress_WithState(ReadOnlySpan <byte> input, Span <byte> output) { BrotliDecoder decoder = default; while (!input.IsEmpty && !output.IsEmpty) { decoder.Decompress(input, output, out int bytesConsumed, out int written); input = input.Slice(bytesConsumed); output = output.Slice(written); } }
/// <summary>Tries to decode available data into the destination buffer.</summary> /// <param name="destination">The destination buffer for the decompressed data.</param> /// <param name="bytesWritten">The number of bytes written to destination.</param> /// <returns>true if the caller should consider the read operation completed; otherwise, false.</returns> private bool TryDecompress(Span <byte> destination, out int bytesWritten) { // Decompress any data we may have in our buffer. OperationStatus lastResult = _decoder.Decompress(new ReadOnlySpan <byte>(_buffer, _bufferOffset, _bufferCount), destination, out int bytesConsumed, out bytesWritten); if (lastResult == OperationStatus.InvalidData) { throw new InvalidOperationException(SR.BrotliStream_Decompress_InvalidData); } if (bytesConsumed != 0) { _bufferOffset += bytesConsumed; _bufferCount -= bytesConsumed; } // If we successfully decompressed any bytes, or if we've reached the end of the decompression, we're done. if (bytesWritten != 0 || lastResult == OperationStatus.Done) { return(true); } if (destination.IsEmpty) { // The caller provided a zero-byte buffer. This is typically done in order to avoid allocating/renting // a buffer until data is known to be available. We don't have perfect knowledge here, as _decoder.Decompress // will return DestinationTooSmall whether or not more data is required. As such, we assume that if there's // any data in our input buffer, it would have been decompressible into at least one byte of output, and // otherwise we need to do a read on the underlying stream. This isn't perfect, because having input data // doesn't necessarily mean it'll decompress into at least one byte of output, but it's a reasonable approximation // for the 99% case. If it's wrong, it just means that a caller using zero-byte reads as a way to delay // getting a buffer to use for a subsequent call may end up getting one earlier than otherwise preferred. Debug.Assert(lastResult == OperationStatus.DestinationTooSmall); if (_bufferCount != 0) { Debug.Assert(bytesWritten == 0); return(true); } } Debug.Assert( lastResult == OperationStatus.NeedMoreData || (lastResult == OperationStatus.DestinationTooSmall && destination.IsEmpty && _bufferCount == 0), $"{nameof(lastResult)} == {lastResult}, {nameof(destination.Length)} == {destination.Length}"); // Ensure any left over data is at the beginning of the array so we can fill the remainder. if (_bufferCount != 0 && _bufferOffset != 0) { new ReadOnlySpan <byte>(_buffer, _bufferOffset, _bufferCount).CopyTo(_buffer); } _bufferOffset = 0; return(false); }
public Span <byte> Decompress_WithState() // the level argument is not used here, but it describes how the data was compressed (in the benchmark id) { using (BrotliDecoder decoder = new BrotliDecoder()) { Span <byte> output = new Span <byte>(CompressedFile.UncompressedData); ReadOnlySpan <byte> input = CompressedFile.CompressedData; while (!input.IsEmpty && !output.IsEmpty) { decoder.Decompress(input, output, out int bytesConsumed, out int written); input = input.Slice(bytesConsumed); output = output.Slice(written); } return(output); } }
public void Decompress_WithEmptySource() { byte[] sourceBytes = new byte[0]; byte[] destinationBytes = new byte[100000]; ReadOnlySpan <byte> source = new ReadOnlySpan <byte>(sourceBytes); Span <byte> destination = new Span <byte>(destinationBytes); Assert.False(BrotliDecoder.TryDecompress(source, destination, out int bytesWritten), "TryDecompress completed successfully but should have failed due to too short of a source array"); Assert.Equal(0, bytesWritten); BrotliDecoder decoder = default; var result = decoder.Decompress(source, destination, out int bytesConsumed, out bytesWritten); Assert.Equal(0, bytesWritten); Assert.Equal(0, bytesConsumed); Assert.Equal(OperationStatus.NeedMoreData, result); }
public void Decompress_WithEmptyDestination() { string testFile = UncompressedTestFile(); byte[] sourceBytes = File.ReadAllBytes(CompressedTestFile(testFile)); byte[] destinationBytes = new byte[0]; ReadOnlySpan <byte> source = new ReadOnlySpan <byte>(sourceBytes); Span <byte> destination = new Span <byte>(destinationBytes); Assert.False(BrotliDecoder.TryDecompress(source, destination, out int bytesWritten), "TryDecompress completed successfully but should have failed due to too short of a destination array"); Assert.Equal(0, bytesWritten); BrotliDecoder decoder = default; var result = decoder.Decompress(source, destination, out int bytesConsumed, out bytesWritten); Assert.Equal(0, bytesWritten); Assert.Equal(0, bytesConsumed); Assert.Equal(OperationStatus.DestinationTooSmall, result); }
public override int Read(Span <byte> buffer) { if (_mode != CompressionMode.Decompress) { throw new InvalidOperationException(SR.BrotliStream_Compress_UnsupportedOperation); } EnsureNotDisposed(); int totalWritten = 0; OperationStatus lastResult = OperationStatus.DestinationTooSmall; // We want to continue calling Decompress until we're either out of space for output or until Decompress indicates it is finished. while (buffer.Length > 0 && lastResult != OperationStatus.Done) { if (lastResult == OperationStatus.NeedMoreData) { // Ensure any left over data is at the beginning of the array so we can fill the remainder. if (_bufferCount > 0 && _bufferOffset != 0) { _buffer.AsSpan(_bufferOffset, _bufferCount).CopyTo(_buffer); } _bufferOffset = 0; int numRead = 0; while (_bufferCount < _buffer.Length && ((numRead = _stream.Read(_buffer, _bufferCount, _buffer.Length - _bufferCount)) > 0)) { _bufferCount += numRead; if (_bufferCount > _buffer.Length) { // The stream is either malicious or poorly implemented and returned a number of // bytes larger than the buffer supplied to it. throw new InvalidDataException(SR.BrotliStream_Decompress_InvalidStream); } } if (_bufferCount <= 0) { break; } } lastResult = _decoder.Decompress(new ReadOnlySpan <byte>(_buffer, _bufferOffset, _bufferCount), buffer, out int bytesConsumed, out int bytesWritten); if (lastResult == OperationStatus.InvalidData) { throw new InvalidOperationException(SR.BrotliStream_Decompress_InvalidData); } if (bytesConsumed > 0) { _bufferOffset += bytesConsumed; _bufferCount -= bytesConsumed; } if (bytesWritten > 0) { totalWritten += bytesWritten; buffer = buffer.Slice(bytesWritten); } } return(totalWritten); }