Line data Source code
1 : #include "source/extensions/compression/brotli/compressor/brotli_compressor_impl.h" 2 : 3 : #include "source/common/buffer/buffer_impl.h" 4 : 5 : namespace Envoy { 6 : namespace Extensions { 7 : namespace Compression { 8 : namespace Brotli { 9 : namespace Compressor { 10 : 11 : BrotliCompressorImpl::BrotliCompressorImpl(const uint32_t quality, const uint32_t window_bits, 12 : const uint32_t input_block_bits, 13 : const bool disable_literal_context_modeling, 14 : const EncoderMode mode, const uint32_t chunk_size) 15 : : chunk_size_{chunk_size}, state_(BrotliEncoderCreateInstance(nullptr, nullptr, nullptr), 16 0 : &BrotliEncoderDestroyInstance) { 17 0 : RELEASE_ASSERT(quality <= BROTLI_MAX_QUALITY, ""); 18 0 : BROTLI_BOOL result = BrotliEncoderSetParameter(state_.get(), BROTLI_PARAM_QUALITY, quality); 19 0 : RELEASE_ASSERT(result == BROTLI_TRUE, ""); 20 : 21 0 : RELEASE_ASSERT(window_bits >= BROTLI_MIN_WINDOW_BITS && window_bits <= BROTLI_MAX_WINDOW_BITS, 22 0 : ""); 23 0 : result = BrotliEncoderSetParameter(state_.get(), BROTLI_PARAM_LGWIN, window_bits); 24 0 : RELEASE_ASSERT(result == BROTLI_TRUE, ""); 25 : 26 0 : RELEASE_ASSERT(input_block_bits >= BROTLI_MIN_INPUT_BLOCK_BITS && 27 0 : input_block_bits <= BROTLI_MAX_INPUT_BLOCK_BITS, 28 0 : ""); 29 0 : result = BrotliEncoderSetParameter(state_.get(), BROTLI_PARAM_LGBLOCK, input_block_bits); 30 0 : RELEASE_ASSERT(result == BROTLI_TRUE, ""); 31 : 32 0 : result = BrotliEncoderSetParameter(state_.get(), BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING, 33 0 : disable_literal_context_modeling); 34 0 : RELEASE_ASSERT(result == BROTLI_TRUE, ""); 35 : 36 0 : result = BrotliEncoderSetParameter(state_.get(), BROTLI_PARAM_MODE, static_cast<uint32_t>(mode)); 37 0 : RELEASE_ASSERT(result == BROTLI_TRUE, ""); 38 0 : } 39 : 40 : void BrotliCompressorImpl::compress(Buffer::Instance& buffer, 41 0 : Envoy::Compression::Compressor::State state) { 42 0 : Common::BrotliContext ctx(chunk_size_); 43 : 44 0 : Buffer::OwnedImpl accumulation_buffer; 45 0 : for (const Buffer::RawSlice& input_slice : buffer.getRawSlices()) { 46 0 : ctx.avail_in_ = input_slice.len_; 47 0 : ctx.next_in_ = static_cast<uint8_t*>(input_slice.mem_); 48 : 49 0 : while (ctx.avail_in_ > 0) { 50 0 : process(ctx, accumulation_buffer, BROTLI_OPERATION_PROCESS); 51 0 : } 52 : 53 0 : buffer.drain(input_slice.len_); 54 0 : } 55 : 56 0 : ASSERT(buffer.length() == 0); 57 0 : buffer.move(accumulation_buffer); 58 : 59 : // The encoder's internal buffer can still hold data not flushed to the 60 : // output chunk which in turn can be almost full and not fit to accommodate 61 : // the flushed data. And in case of the `Finish` operation the encoder may add 62 : // `ISLAST` and `ISLASTEMPTY` headers to the output. Thus keep processing 63 : // until the encoder's output is fully depleted. 64 0 : do { 65 0 : process(ctx, buffer, 66 0 : state == Envoy::Compression::Compressor::State::Finish ? BROTLI_OPERATION_FINISH 67 0 : : BROTLI_OPERATION_FLUSH); 68 0 : } while (BrotliEncoderHasMoreOutput(state_.get())); 69 : 70 0 : ctx.finalizeOutput(buffer); 71 0 : } 72 : 73 : void BrotliCompressorImpl::process(Common::BrotliContext& ctx, Buffer::Instance& output_buffer, 74 0 : const BrotliEncoderOperation op) { 75 0 : BROTLI_BOOL result = BrotliEncoderCompressStream(state_.get(), op, &ctx.avail_in_, &ctx.next_in_, 76 0 : &ctx.avail_out_, &ctx.next_out_, nullptr); 77 0 : RELEASE_ASSERT(result == BROTLI_TRUE, "unable to compress"); 78 0 : ctx.updateOutput(output_buffer); 79 0 : } 80 : } // namespace Compressor 81 : } // namespace Brotli 82 : } // namespace Compression 83 : } // namespace Extensions 84 : } // namespace Envoy