master
c 142 lines 5.1 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "brotli.h"
4
5 #ifdef ENABLE_BROTLI
6 #include <brotli/encode.h>
7 #include <brotli/decode.h>
8
9 void stream_compressor_init_brotli(struct compressor_state *state) {
10 if (!state->initialized) {
11 state->initialized = true;
12 state->stream = BrotliEncoderCreateInstance(NULL, NULL, NULL);
13
14 if (state->level < BROTLI_MIN_QUALITY) {
15 state->level = BROTLI_MIN_QUALITY;
16 } else if (state->level > BROTLI_MAX_QUALITY) {
17 state->level = BROTLI_MAX_QUALITY;
18 }
19
20 BrotliEncoderSetParameter(state->stream, BROTLI_PARAM_QUALITY, state->level);
21 }
22 }
23
24 void stream_compressor_destroy_brotli(struct compressor_state *state) {
25 if (state->stream) {
26 BrotliEncoderDestroyInstance(state->stream);
27 state->stream = NULL;
28 }
29 }
30
31 size_t stream_compress_brotli(struct compressor_state *state, const char *data, size_t size, const char **out) {
32 if (unlikely(!state || !size || !out))
33 return 0;
34
35 simple_ring_buffer_make_room(&state->output, MAX(BrotliEncoderMaxCompressedSize(size), COMPRESSION_MAX_CHUNK));
36
37 size_t available_out = state->output.size;
38
39 size_t available_in = size;
40 const uint8_t *next_in = (const uint8_t *)data;
41 uint8_t *next_out = (uint8_t *)state->output.data;
42
43 if (!BrotliEncoderCompressStream(state->stream, BROTLI_OPERATION_FLUSH, &available_in, &next_in, &available_out, &next_out, NULL)) {
44 netdata_log_error("STREAM_COMPRESS: Brotli compression failed.");
45 return 0;
46 }
47
48 if(available_in != 0) {
49 netdata_log_error("STREAM_COMPRESS: BrotliEncoderCompressStream() did not use all the input buffer, %zu bytes out of %zu remain",
50 available_in, size);
51 return 0;
52 }
53
54 size_t compressed_size = state->output.size - available_out;
55 if(available_out == 0) {
56 netdata_log_error("STREAM_COMPRESS: BrotliEncoderCompressStream() needs a bigger output buffer than the one we provided "
57 "(output buffer %zu bytes, compressed payload %zu bytes)",
58 state->output.size, size);
59 return 0;
60 }
61
62 if(compressed_size == 0) {
63 netdata_log_error("STREAM_COMPRESS: BrotliEncoderCompressStream() did not produce any output from the input provided "
64 "(input buffer %zu bytes)",
65 size);
66 return 0;
67 }
68
69 state->sender_locked.total_compressions++;
70 state->sender_locked.total_uncompressed += size - available_in;
71 state->sender_locked.total_compressed += compressed_size;
72
73 *out = state->output.data;
74 return compressed_size;
75 }
76
77 void stream_decompressor_init_brotli(struct decompressor_state *state) {
78 if (!state->initialized) {
79 state->initialized = true;
80 state->stream = BrotliDecoderCreateInstance(NULL, NULL, NULL);
81
82 simple_ring_buffer_make_room(&state->output, COMPRESSION_MAX_CHUNK);
83 }
84 }
85
86 void stream_decompressor_destroy_brotli(struct decompressor_state *state) {
87 if (state->stream) {
88 BrotliDecoderDestroyInstance(state->stream);
89 state->stream = NULL;
90 }
91 }
92
93 size_t stream_decompress_brotli(struct decompressor_state *state, const char *compressed_data, size_t compressed_size) {
94 if (unlikely(!state || !compressed_data || !compressed_size))
95 return 0;
96
97 // The state.output ring buffer is always EMPTY at this point,
98 // meaning that (state->output.read_pos == state->output.write_pos)
99 // However, THEY ARE NOT ZERO.
100
101 size_t available_out = state->output.size;
102 size_t available_in = compressed_size;
103 const uint8_t *next_in = (const uint8_t *)compressed_data;
104 uint8_t *next_out = (uint8_t *)state->output.data;
105
106 if (BrotliDecoderDecompressStream(state->stream, &available_in, &next_in, &available_out, &next_out, NULL) == BROTLI_DECODER_RESULT_ERROR) {
107 netdata_log_error("STREAM_DECOMPRESS: Brotli decompression failed.");
108 return 0;
109 }
110
111 if(available_in != 0) {
112 netdata_log_error("STREAM_DECOMPRESS: BrotliDecoderDecompressStream() did not use all the input buffer, %zu bytes out of %zu remain",
113 available_in, compressed_size);
114 return 0;
115 }
116
117 size_t decompressed_size = state->output.size - available_out;
118 if(available_out == 0) {
119 netdata_log_error("STREAM_DECOMPRESS: BrotliDecoderDecompressStream() needs a bigger output buffer than the one we provided "
120 "(output buffer %zu bytes, compressed payload %zu bytes)",
121 state->output.size, compressed_size);
122 return 0;
123 }
124
125 if(decompressed_size == 0) {
126 netdata_log_error("STREAM_DECOMPRESS: BrotliDecoderDecompressStream() did not produce any output from the input provided "
127 "(input buffer %zu bytes)",
128 compressed_size);
129 return 0;
130 }
131
132 state->output.read_pos = 0;
133 state->output.write_pos = decompressed_size;
134
135 state->total_compressed += compressed_size - available_in;
136 state->total_uncompressed += decompressed_size;
137 state->total_compressions++;
138
139 return decompressed_size;
140 }
141
142 #endif // ENABLE_BROTLI