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
97
    : chunk_size_{chunk_size}, state_(BrotliEncoderCreateInstance(nullptr, nullptr, nullptr),
16
97
                                      &BrotliEncoderDestroyInstance) {
17
97
  RELEASE_ASSERT(quality <= BROTLI_MAX_QUALITY, "");
18
97
  BROTLI_BOOL result = BrotliEncoderSetParameter(state_.get(), BROTLI_PARAM_QUALITY, quality);
19
97
  RELEASE_ASSERT(result == BROTLI_TRUE, "");
20

            
21
97
  RELEASE_ASSERT(window_bits >= BROTLI_MIN_WINDOW_BITS && window_bits <= BROTLI_MAX_WINDOW_BITS,
22
97
                 "");
23
97
  result = BrotliEncoderSetParameter(state_.get(), BROTLI_PARAM_LGWIN, window_bits);
24
97
  RELEASE_ASSERT(result == BROTLI_TRUE, "");
25

            
26
97
  RELEASE_ASSERT(input_block_bits >= BROTLI_MIN_INPUT_BLOCK_BITS &&
27
97
                     input_block_bits <= BROTLI_MAX_INPUT_BLOCK_BITS,
28
97
                 "");
29
97
  result = BrotliEncoderSetParameter(state_.get(), BROTLI_PARAM_LGBLOCK, input_block_bits);
30
97
  RELEASE_ASSERT(result == BROTLI_TRUE, "");
31

            
32
97
  result = BrotliEncoderSetParameter(state_.get(), BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING,
33
97
                                     disable_literal_context_modeling);
34
97
  RELEASE_ASSERT(result == BROTLI_TRUE, "");
35

            
36
97
  result = BrotliEncoderSetParameter(state_.get(), BROTLI_PARAM_MODE, static_cast<uint32_t>(mode));
37
97
  RELEASE_ASSERT(result == BROTLI_TRUE, "");
38
97
}
39

            
40
void BrotliCompressorImpl::compress(Buffer::Instance& buffer,
41
2728
                                    Envoy::Compression::Compressor::State state) {
42
2728
  Common::BrotliContext ctx(chunk_size_);
43

            
44
2728
  Buffer::OwnedImpl accumulation_buffer;
45
2728
  for (const Buffer::RawSlice& input_slice : buffer.getRawSlices()) {
46
2564
    ctx.avail_in_ = input_slice.len_;
47
2564
    ctx.next_in_ = static_cast<uint8_t*>(input_slice.mem_);
48

            
49
5128
    while (ctx.avail_in_ > 0) {
50
2564
      process(ctx, accumulation_buffer, BROTLI_OPERATION_PROCESS);
51
2564
    }
52

            
53
2564
    buffer.drain(input_slice.len_);
54
2564
  }
55

            
56
2728
  ASSERT(buffer.length() == 0);
57
2728
  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
6900
  do {
65
6900
    process(ctx, buffer,
66
6900
            state == Envoy::Compression::Compressor::State::Finish ? BROTLI_OPERATION_FINISH
67
6900
                                                                   : BROTLI_OPERATION_FLUSH);
68
6900
  } while (BrotliEncoderHasMoreOutput(state_.get()));
69

            
70
2728
  ctx.finalizeOutput(buffer);
71
2728
}
72

            
73
void BrotliCompressorImpl::process(Common::BrotliContext& ctx, Buffer::Instance& output_buffer,
74
9464
                                   const BrotliEncoderOperation op) {
75
9464
  BROTLI_BOOL result = BrotliEncoderCompressStream(state_.get(), op, &ctx.avail_in_, &ctx.next_in_,
76
9464
                                                   &ctx.avail_out_, &ctx.next_out_, nullptr);
77
9464
  RELEASE_ASSERT(result == BROTLI_TRUE, "unable to compress");
78
9464
  ctx.updateOutput(output_buffer);
79
9464
}
80
} // namespace Compressor
81
} // namespace Brotli
82
} // namespace Compression
83
} // namespace Extensions
84
} // namespace Envoy