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);
        }
Пример #2
0
        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);
                            }
                        }
                    }
                }
            }
        }
Пример #3
0
        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());
            }
        }
Пример #4
0
        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);
            }
        }
Пример #5
0
        /// <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);
        }
Пример #6
0
        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);
            }
        }
Пример #7
0
        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);
        }
Пример #8
0
        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);
        }
Пример #9
0
        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);
        }