1
#include "source/extensions/compression/gzip/compressor/zlib_compressor_impl.h"
2

            
3
#include <memory>
4

            
5
#include "envoy/common/exception.h"
6

            
7
#include "source/common/common/assert.h"
8

            
9
#include "absl/container/fixed_array.h"
10

            
11
namespace Envoy {
12
namespace Extensions {
13
namespace Compression {
14
namespace Gzip {
15
namespace Compressor {
16

            
17
44
ZlibCompressorImpl::ZlibCompressorImpl() : ZlibCompressorImpl(4096) {}
18

            
19
ZlibCompressorImpl::ZlibCompressorImpl(uint64_t chunk_size)
20
83
    : Common::Base(chunk_size, [](z_stream* z) {
21
83
        deflateEnd(z);
22
83
        delete z;
23
83
      }) {
24
83
  zstream_ptr_->zalloc = Z_NULL;
25
83
  zstream_ptr_->zfree = Z_NULL;
26
83
  zstream_ptr_->opaque = Z_NULL;
27
83
  zstream_ptr_->avail_out = chunk_size_;
28
83
  zstream_ptr_->next_out = chunk_char_ptr_.get();
29
83
}
30

            
31
void ZlibCompressorImpl::init(CompressionLevel comp_level, CompressionStrategy comp_strategy,
32
83
                              int64_t window_bits, uint64_t memory_level = 8) {
33
83
  ASSERT(initialized_ == false);
34
  // The deflateInit2 macro from zlib.h contains an old-style cast, so we need to suppress the
35
  // warning for this call.
36
83
#pragma GCC diagnostic push
37
83
#pragma GCC diagnostic ignored "-Wold-style-cast"
38
83
  const int result = deflateInit2(zstream_ptr_.get(), static_cast<int>(comp_level), Z_DEFLATED,
39
83
                                  static_cast<int>(window_bits), static_cast<int>(memory_level),
40
83
                                  static_cast<int>(comp_strategy));
41
83
#pragma GCC diagnostic pop
42
83
  RELEASE_ASSERT(result >= 0, "");
43
83
  initialized_ = true;
44
83
}
45

            
46
void ZlibCompressorImpl::compress(Buffer::Instance& buffer,
47
1240
                                  Envoy::Compression::Compressor::State state) {
48
1240
  for (const Buffer::RawSlice& input_slice : buffer.getRawSlices()) {
49
1164
    zstream_ptr_->avail_in = input_slice.len_;
50
1164
    zstream_ptr_->next_in = static_cast<Bytef*>(input_slice.mem_);
51
    // Z_NO_FLUSH tells the compressor to take the data in and compresses it as much as possible
52
    // without flushing it out. However, if the data output is greater or equal to the allocated
53
    // chunk size, process() outputs it to the end of the buffer. This is fine, since at the next
54
    // step, the buffer is drained from the beginning of the buffer by the size of input.
55
1164
    process(buffer, Z_NO_FLUSH);
56
1164
    buffer.drain(input_slice.len_);
57
1164
  }
58

            
59
1240
  process(buffer, state == Envoy::Compression::Compressor::State::Finish ? Z_FINISH : Z_SYNC_FLUSH);
60
1240
}
61

            
62
8648
bool ZlibCompressorImpl::deflateNext(int64_t flush_state) {
63
8648
  const int result = deflate(zstream_ptr_.get(), flush_state);
64
8648
  switch (flush_state) {
65
65
  case Z_FINISH:
66
65
    if (result != Z_OK && result != Z_BUF_ERROR) {
67
62
      RELEASE_ASSERT(result == Z_STREAM_END, "");
68
62
      return false;
69
62
    }
70
3
    FALLTHRU;
71
8586
  default:
72
8586
    if (result == Z_BUF_ERROR && zstream_ptr_->avail_in == 0) {
73
2342
      return false; // This means that zlib needs more input, so stop here.
74
2342
    }
75
8648
    RELEASE_ASSERT(result == Z_OK, "");
76
8648
  }
77

            
78
6244
  return true;
79
8648
}
80

            
81
2404
void ZlibCompressorImpl::process(Buffer::Instance& output_buffer, int64_t flush_state) {
82
8648
  while (deflateNext(flush_state)) {
83
6244
    if (zstream_ptr_->avail_out == 0) {
84
3642
      updateOutput(output_buffer);
85
3642
    }
86
6244
  }
87

            
88
2404
  if (flush_state == Z_SYNC_FLUSH || flush_state == Z_FINISH) {
89
1240
    updateOutput(output_buffer);
90
1240
  }
91
2404
}
92

            
93
} // namespace Compressor
94
} // namespace Gzip
95
} // namespace Compression
96
} // namespace Extensions
97
} // namespace Envoy