Example #1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="BrotliStream"/> class using the specified stream and
        /// compression mode, and optionally leaves the stream open.
        /// </summary>
        /// <param name="stream">The stream to compress or decompress.</param>
        /// <param name="mode">One of the enumeration values that indicates whether to compress or decompress the stream.</param>
        /// <param name="leaveOpen"><c>true</c> to leave the stream open after disposing the <see cref="BrotliStream"/> object; otherwise, <c>false</c>.</param>
        public BrotliStream(Stream stream, CompressionMode mode, bool leaveOpen) {
            if (stream == null)
                throw new ArgumentNullException(nameof(stream));

            if (CompressionMode.Compress != mode && CompressionMode.Decompress != mode)
                throw new ArgumentOutOfRangeException(nameof(mode));

            _stream = stream;
            _mode = mode;
            _leaveOpen = leaveOpen;

            switch (_mode) {
                case CompressionMode.Decompress:
                    if (!_stream.CanRead)
                        throw new ArgumentException("Stream does not support read", nameof(stream));

                    _decoderState = Brotli.BrotliCreateDecoderState();
                    Brotli.BrotliDecoderStateInit(ref _decoderState);
                    _buffer = new byte[0xfff0];
                    break;
                case CompressionMode.Compress:
                    if (!_stream.CanWrite)
                        throw new ArgumentException("Stream does not support write", nameof(stream));

                    _encoderState = Brotli.BrotliEncoderCreateInstance(null, null, null);
                    SetQuality(1);
                    break;
            }
        }
Example #2
0
        /// <summary>
        /// Releases the unmanaged resources used by the <see cref="BrotliStream"/> and optionally releases the managed resources.
        /// </summary>
        /// <param name="disposing"></param>
        protected override void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                FlushCompress(true);

                if (_mode == CompressionMode.Compress)
                {
                    Brotli.BrotliEncoderDestroyInstance(ref _encoderState);
                }
                else
                {
                    Brotli.BrotliDecoderStateCleanup(ref _decoderState);
                }
                if (_customDictionary != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(_customDictionary);
                    _customDictionary = IntPtr.Zero;
                }
                _disposed = true;
            }

            if (disposing && !_leaveOpen && _stream != null)
            {
                _stream.Dispose();
                _stream = null;
            }

            base.Dispose(disposing);
        }
Example #3
0
        private void WriteCore(byte[] buffer, int offset, int count, Brotli.BrotliEncoderOperation operation) {
            bool flush = operation == Brotli.BrotliEncoderOperation.BROTLI_OPERATION_FLUSH ||
                         operation == Brotli.BrotliEncoderOperation.BROTLI_OPERATION_FINISH;

            byte[] out_buf = new byte[0x1FFFE];
            size_t available_in = count, available_out = out_buf.Length;
            fixed (byte* out_buf_ptr = out_buf)
            fixed (byte* buf_ptr = buffer) {
                byte* next_in = buf_ptr + offset;
                byte* next_out = out_buf_ptr;

                while ((!flush && available_in > 0) || flush) {
                    if (!Brotli.BrotliEncoderCompressStream(ref _encoderState,
                        operation, &available_in, &next_in,
                        &available_out, &next_out, null)) {
                        throw new InvalidDataException("Compression failed");
                    }

                    bool hasData = available_out != out_buf.Length;
                    if (hasData) {
                        int out_size = (int)(out_buf.Length - available_out);
                        _stream.Write(out_buf, 0, out_size);
                        available_out = out_buf.Length;
                        next_out = out_buf_ptr;
                    }

                    if (Brotli.BrotliEncoderIsFinished(ref _encoderState))
                        break;

                    if (!hasData && flush)
                        break;
                }
            }
        }
Example #4
0
        /// <summary>
        /// Sets the dictionary for compression and decompression.
        /// </summary>
        /// <param name="dictionary">The dictionary as a byte array.</param>
        public void SetCustomDictionary(byte[] dictionary)
        {
            if (dictionary == null)
            {
                throw new ArgumentNullException(nameof(dictionary));
            }

            EnsureNotDisposed();

            if (_customDictionary != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(_customDictionary);
            }

            _customDictionary = Marshal.AllocHGlobal(dictionary.Length);
            Marshal.Copy(dictionary, 0, _customDictionary, dictionary.Length);

            if (_mode == CompressionMode.Compress)
            {
                Brotli.BrotliEncoderSetCustomDictionary(ref _encoderState, dictionary.Length,
                                                        (byte *)_customDictionary);
            }
            else
            {
                Brotli.BrotliDecoderSetCustomDictionary(ref _decoderState, dictionary.Length,
                                                        (byte *)_customDictionary);
            }
        }
Example #5
0
        /// <summary>
        /// Reads a number of decompressed bytes into the specified byte array.
        /// </summary>
        /// <param name="buffer">The array to store decompressed bytes.</param>
        /// <param name="offset">The byte offset in <paramref name="buffer"/> at which the read bytes will be placed.</param>
        /// <param name="count">The maximum number of decompressed bytes to read.</param>
        /// <returns>The number of bytes that were read into the byte array.</returns>
        public override int Read(byte[] buffer, int offset, int count) {
            if (_mode != CompressionMode.Decompress)
                throw new InvalidOperationException("Read is only supported in Decompress mode");

            EnsureNotDisposed();
            ValidateParameters(buffer, offset, count);

            int totalWritten = 0;
            while (offset < buffer.Length && _lastDecoderState != Brotli.BrotliDecoderResult.BROTLI_DECODER_RESULT_SUCCESS) {
                if (_lastDecoderState == Brotli.BrotliDecoderResult.BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT) {
                    if (_bufferCount > 0 && _bufferOffset != 0) {
                        Array.Copy(_buffer, _bufferOffset, _buffer, 0, _bufferCount);
                    }
                    _bufferOffset = 0;

                    int numRead = 0;
                    while (_bufferCount < _buffer.Length && ((numRead = _stream.Read(_buffer, _bufferCount, _buffer.Length - _bufferCount)) > 0)) {
                        _bufferCount += numRead;
                        if (_bufferCount > _buffer.Length)
                            throw new InvalidDataException("Invalid input stream detected, more bytes supplied than expected.");
                    }

                    if (_bufferCount <= 0)
                        break;
                }

                size_t available_in = _bufferCount;
                size_t available_in_old = available_in;
                size_t available_out = count;
                size_t available_out_old = available_out;

                fixed (byte* out_buf_ptr = buffer)
                fixed (byte* in_buf_ptr = _buffer) {
                    byte* in_buf = in_buf_ptr + _bufferOffset;
                    byte* out_buf = out_buf_ptr + offset;
                    _lastDecoderState = Brotli.BrotliDecoderDecompressStream(ref _decoderState, &available_in, &in_buf,
                        &available_out, &out_buf, null);
                }

                if (_lastDecoderState == Brotli.BrotliDecoderResult.BROTLI_DECODER_RESULT_ERROR)
                    throw new InvalidDataException("Decompression failed with error code: " + _decoderState.error_code);

                size_t bytesConsumed = available_in_old - available_in;
                size_t bytesWritten = available_out_old - available_out;

                if (bytesConsumed > 0) {
                    _bufferOffset += (int) bytesConsumed;
                    _bufferCount -= (int) bytesConsumed;
                }

                if (bytesWritten > 0) {
                    totalWritten += (int)bytesWritten;
                    offset += (int)bytesWritten;
                    count -= (int)bytesWritten;
                }
            }

            return totalWritten;
        }
Example #6
0
        /// <summary>
        /// Sets the quality for compression.
        /// </summary>
        /// <param name="quality">The quality value (a value from 0-11).</param>
        public void SetQuality(int quality) {
            if (_mode != CompressionMode.Compress)
                throw new InvalidOperationException("SetQuality is only valid for compress");

            if (quality < Brotli.BROTLI_MIN_QUALITY || quality > Brotli.BROTLI_MAX_QUALITY)
                throw new ArgumentOutOfRangeException(nameof(quality), "Quality should be a value between " +
                                                                       Brotli.BROTLI_MIN_QUALITY + "-" + Brotli
                                                                           .BROTLI_MAX_QUALITY);

            EnsureNotDisposed();

            Brotli.BrotliEncoderSetParameter(ref _encoderState, Brotli.BrotliEncoderParameter.BROTLI_PARAM_QUALITY,
                (uint) quality);
        }
Example #7
0
        private void FlushCompress(bool finish) {
            if (_mode != CompressionMode.Compress)
                return;

            if (Brotli.BrotliEncoderIsFinished(ref _encoderState))
                return;

            var op = finish
                ? Brotli.BrotliEncoderOperation.BROTLI_OPERATION_FINISH
                : Brotli.BrotliEncoderOperation.BROTLI_OPERATION_FLUSH;

            byte[] buffer = new byte[0];
            WriteCore(buffer, 0, 0, op);
        }
Example #8
0
        /// <summary>
        /// Sets the window size for the encoder
        /// </summary>
        /// <param name="windowSize">The window size in bits (a value from 10-24)</param>
        public void SetWindow(int windowSize) {
            if (_mode != CompressionMode.Compress)
                throw new InvalidOperationException("SetWindow is only valid for compress");

            if (windowSize < Brotli.BROTLI_MIN_WINDOW_BITS || windowSize > Brotli.BROTLI_MAX_WINDOW_BITS)
                throw new ArgumentOutOfRangeException(nameof(windowSize), "Window size should be a value between " +
                                                                          Brotli.BROTLI_MIN_WINDOW_BITS + "-" + Brotli
                                                                              .BROTLI_MAX_WINDOW_BITS);

            EnsureNotDisposed();

            Brotli.BrotliEncoderSetParameter(ref _encoderState, Brotli.BrotliEncoderParameter.BROTLI_PARAM_LGWIN,
                (uint) windowSize);
        }
Example #9
0
 public override unsafe void PrepareDistanceCache(HasherHandle handle, int *distance_cache)
 {
     Brotli.PrepareDistanceCache(distance_cache,
                                 GetHasherCommon(handle)->params_.num_last_distances_to_check);
 }
Example #10
0
        /// <summary>
        /// Reads a number of decompressed bytes into the specified byte array.
        /// </summary>
        /// <param name="buffer">The array to store decompressed bytes.</param>
        /// <param name="offset">The byte offset in <paramref name="buffer"/> at which the read bytes will be placed.</param>
        /// <param name="count">The maximum number of decompressed bytes to read.</param>
        /// <returns>The number of bytes that were read into the byte array.</returns>
        public override int Read(byte[] buffer, int offset, int count)
        {
            if (_mode != CompressionMode.Decompress)
            {
                throw new InvalidOperationException("Read is only supported in Decompress mode");
            }

            EnsureNotDisposed();
            ValidateParameters(buffer, offset, count);

            bool endOfStream = false;

            byte[] in_buf = new byte[0xffff];
            size_t available_in = 0, available_out = count;

            fixed(byte *in_buf_ptr = in_buf)
            fixed(byte *buf_ptr = buffer)
            {
                byte *next_in  = in_buf_ptr;
                byte *next_out = buf_ptr + offset;
                int   total    = 0;

                while (true)
                {
                    if (_lastDecoderState == Brotli.BrotliDecoderResult.BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT)
                    {
                        int len = _stream.Read(in_buf, 0, in_buf.Length);
                        if (len <= 0)
                        {
                            endOfStream = true;
                            break;
                        }
                        available_in = len;
                        next_in      = in_buf_ptr;
                    }
                    else if (_lastDecoderState == Brotli.BrotliDecoderResult.BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT)
                    {
                        /* Ignore */
                    }
                    else
                    {
                        endOfStream = true;
                        break;
                    }

                    size_t available_out_old = available_out;
                    _lastDecoderState = Brotli.BrotliDecoderDecompressStream(ref _decoderState, &available_in,
                                                                             &next_in, &available_out, &next_out, null);

                    total += (int)(available_out_old - available_out);
                    if (total >= count)
                    {
                        break;
                    }
                }

                if (endOfStream && _lastDecoderState != Brotli.BrotliDecoderResult.BROTLI_DECODER_RESULT_SUCCESS)
                {
                    throw new InvalidDataException("Decompression failed with error code: " + _decoderState.error_code);
                }

                return(total);
            }
        }