master
c 163 lines 5.09 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "zstd.h"
4
5 #ifdef ENABLE_ZSTD
6 #include <zstd.h>
7
8 void stream_compressor_init_zstd(struct compressor_state *state) {
9 if(!state->initialized) {
10 state->initialized = true;
11 state->stream = ZSTD_createCStream();
12
13 if(state->level < 1)
14 state->level = 1;
15
16 if(state->level > ZSTD_maxCLevel())
17 state->level = ZSTD_maxCLevel();
18
19 size_t ret = ZSTD_initCStream(state->stream, state->level);
20 if(ZSTD_isError(ret))
21 netdata_log_error("STREAM_COMPRESS: ZSTD_initCStream() returned error: %s", ZSTD_getErrorName(ret));
22
23 // ZSTD_CCtx_setParameter(state->stream, ZSTD_c_compressionLevel, 1);
24 // ZSTD_CCtx_setParameter(state->stream, ZSTD_c_strategy, ZSTD_fast);
25 }
26 }
27
28 void stream_compressor_destroy_zstd(struct compressor_state *state) {
29 if(state->stream) {
30 ZSTD_freeCStream(state->stream);
31 state->stream = NULL;
32 }
33 }
34
35 size_t stream_compress_zstd(struct compressor_state *state, const char *data, size_t size, const char **out) {
36 if(unlikely(!state || !size || !out))
37 return 0;
38
39 ZSTD_inBuffer inBuffer = {
40 .pos = 0,
41 .size = size,
42 .src = data,
43 };
44
45 size_t wanted_size = MAX(ZSTD_compressBound(inBuffer.size - inBuffer.pos), ZSTD_CStreamOutSize());
46 simple_ring_buffer_make_room(&state->output, wanted_size);
47
48 ZSTD_outBuffer outBuffer = {
49 .pos = 0,
50 .size = state->output.size,
51 .dst = (void *)state->output.data,
52 };
53
54 // compress
55 size_t ret = ZSTD_compressStream(state->stream, &outBuffer, &inBuffer);
56
57 // error handling
58 if(ZSTD_isError(ret)) {
59 netdata_log_error("STREAM_COMPRESS: ZSTD_compressStream() return error: %s", ZSTD_getErrorName(ret));
60 return 0;
61 }
62
63 if(inBuffer.pos < inBuffer.size) {
64 netdata_log_error("STREAM_COMPRESS: ZSTD_compressStream() left unprocessed input (source payload %zu bytes, consumed %zu bytes)",
65 inBuffer.size, inBuffer.pos);
66 return 0;
67 }
68
69 if(outBuffer.pos == 0) {
70 // ZSTD needs more input to flush the output, so let's flush it manually
71 ret = ZSTD_flushStream(state->stream, &outBuffer);
72
73 if(ZSTD_isError(ret)) {
74 netdata_log_error("STREAM_COMPRESS: ZSTD_flushStream() return error: %s", ZSTD_getErrorName(ret));
75 return 0;
76 }
77
78 if(outBuffer.pos == 0) {
79 netdata_log_error("STREAM_COMPRESS: ZSTD_compressStream() returned zero compressed bytes "
80 "(source is %zu bytes, output buffer can fit %zu bytes) "
81 , size, outBuffer.size);
82 return 0;
83 }
84 }
85
86 state->sender_locked.total_compressions++;
87 state->sender_locked.total_uncompressed += size;
88 state->sender_locked.total_compressed += outBuffer.pos;
89
90 // return values
91 *out = state->output.data;
92 return outBuffer.pos;
93 }
94
95 void stream_decompressor_init_zstd(struct decompressor_state *state) {
96 if(!state->initialized) {
97 state->initialized = true;
98 state->stream = ZSTD_createDStream();
99
100 size_t ret = ZSTD_initDStream(state->stream);
101 if(ZSTD_isError(ret))
102 netdata_log_error("STREAM_DECOMPRESS: ZSTD_initDStream() returned error: %s", ZSTD_getErrorName(ret));
103
104 simple_ring_buffer_make_room(&state->output, MAX(COMPRESSION_MAX_CHUNK, ZSTD_DStreamOutSize()));
105 }
106 }
107
108 void stream_decompressor_destroy_zstd(struct decompressor_state *state) {
109 if (state->stream) {
110 ZSTD_freeDStream(state->stream);
111 state->stream = NULL;
112 }
113 }
114
115 size_t stream_decompress_zstd(struct decompressor_state *state, const char *compressed_data, size_t compressed_size) {
116 if (unlikely(!state || !compressed_data || !compressed_size))
117 return 0;
118
119 // The state.output ring buffer is always EMPTY at this point,
120 // meaning that (state->output.read_pos == state->output.write_pos)
121 // However, THEY ARE NOT ZERO.
122
123 ZSTD_inBuffer inBuffer = {
124 .pos = 0,
125 .size = compressed_size,
126 .src = compressed_data,
127 };
128
129 ZSTD_outBuffer outBuffer = {
130 .pos = 0,
131 .dst = (char *)state->output.data,
132 .size = state->output.size,
133 };
134
135 size_t ret = ZSTD_decompressStream(
136 state->stream
137 , &outBuffer
138 , &inBuffer);
139
140 if(ZSTD_isError(ret)) {
141 netdata_log_error("STREAM_DECOMPRESS: ZSTD_decompressStream() return error: %s", ZSTD_getErrorName(ret));
142 return 0;
143 }
144
145 if(inBuffer.pos < inBuffer.size)
146 fatal("STREAM_DECOMPRESS: ZSTD ZSTD_decompressStream() decompressed %zu bytes, "
147 "but %zu bytes of compressed data remain",
148 inBuffer.pos, inBuffer.size);
149
150 size_t decompressed_size = outBuffer.pos;
151
152 state->output.read_pos = 0;
153 state->output.write_pos = outBuffer.pos;
154
155 // statistics
156 state->total_compressed += compressed_size;
157 state->total_uncompressed += decompressed_size;
158 state->total_compressions++;
159
160 return decompressed_size;
161 }
162
163 #endif // ENABLE_ZSTD