libclod
C library for interacting with NBTs, region files, LOD data and other things.
Loading...
Searching...
No Matches
compress.c
1#include <limits.h>
2
3#include "compression_config.h"
4#include <clod/compression.h>
5#include <stdlib.h>
6#include <string.h>
7
8#if HAVE_LIBDEFLATE
9#include <libdeflate.h>
10#endif
11
12#if HAVE_LIBLZ4
13#include <lz4hc.h>
14#include <lz4frame.h>
15#endif
16
17#if HAVE_LIBLZMA
18#include <lzma.h>
19#endif
20
21#if HAVE_LIBZSTD
22#include <zstd.h>
23#endif
24
25#if HAVE_LIBBZ2
26#include <bzlib.h>
27#endif
28
30 void *(*malloc_func)(size_t);
31 void (*free_func)(void *);
32
33#if HAVE_LIBDEFLATE
34 struct libdeflate_compressor *libdeflate_compressor[CLOD_COMPRESSION_LEVELS];
35#endif
36
37#if HAVE_LIBZSTD
38 ZSTD_CCtx *zstd_cctx;
39#endif
40};
41
42#if HAVE_LIBLZMA
43void *compressor_lzma_malloc(void *user, size_t n, size_t size) {
44 auto const ctx = (struct clod_compressor *)user;
45 return ctx->malloc_func(n * size);
46}
47void compressor_lzma_free(void *user, void *address) {
48 auto const ctx = (struct clod_compressor *)user;
49 ctx->free_func(address);
50}
51#endif
52
53#if HAVE_LIBBZ2
54void *compressor_bz2_malloc(void *user, int n, int size) {
55 auto const ctx = (struct clod_compressor *)user;
56 return ctx->malloc_func((size_t)n * (size_t)size);
57}
58void compressor_bz2_free(void *user, void *address) {
59 auto const ctx = (struct clod_compressor *)user;
60 ctx->free_func(address);
61}
62#endif
63
65 struct clod_compressor *ctx = malloc(sizeof(struct clod_compressor));
66 memset(ctx, 0, sizeof(*ctx));
67 // LZ4 and ZSTD are missing custom memory allocation methods.
68 ctx->malloc_func = malloc;
69 ctx->free_func = free;
70 return ctx;
71}
72
74#if HAVE_LIBDEFLATE
75 for (int i = 0; i < CLOD_COMPRESSION_LEVELS; i++)
76 if (ctx->libdeflate_compressor[i])
77 libdeflate_free_compressor(ctx->libdeflate_compressor[i]);
78#endif
79
80#if HAVE_LIBZSTD
81 if (ctx->zstd_cctx)
82 ZSTD_freeCCtx(ctx->zstd_cctx);
83#endif
84
85 ctx->free_func(ctx);
86}
87
90 void *dst, const size_t dst_max_size,
91 const void *src, const size_t src_size,
92 size_t *actual_size,
93 const enum clod_compression_method method,
94 const enum clod_compression_level level
95) {
96 switch (method) {
97 case CLOD_UNCOMPRESSED: {
98 if (dst_max_size < src_size) {
100 }
101 memcpy(dst, src, src_size);
102 *actual_size = src_size;
104 }
105 case CLOD_GZIP: {
106 #if HAVE_LIBDEFLATE
107
108 struct libdeflate_compressor *compressor = ctx->libdeflate_compressor[level];
109 if (!compressor) {
110 const int libdeflate_level =
111 level == CLOD_COMPRESSION_HIGHEST ? 12 :
112 level == CLOD_COMPRESSION_HIGH ? 9 :
113 level == CLOD_COMPRESSION_NORMAL ? 6 :
114 level == CLOD_COMPRESSION_LOW ? 3 :
115 level == CLOD_COMPRESSION_LOWEST ? 1 :
116 6;
117
118 struct libdeflate_options opts = {0};
119 opts.sizeof_options = sizeof(opts);
120 opts.free_func = ctx->free_func;
121 opts.malloc_func = ctx->malloc_func;
122
123 compressor = libdeflate_alloc_compressor_ex(libdeflate_level, &opts);
124 if (!compressor) {
126 }
127 ctx->libdeflate_compressor[level] = compressor;
128 }
129
130 const size_t size = libdeflate_gzip_compress(compressor,
131 src, src_size,
132 dst, dst_max_size);
133
134 if (size == 0) return CLOD_COMPRESSION_SHORT_BUFFER;
135
136 *actual_size = size;
138 #else
140 #endif
141 }
142 case CLOD_ZLIB: {
143 #if HAVE_LIBDEFLATE
144
145 struct libdeflate_compressor *compressor = ctx->libdeflate_compressor[level];
146 if (!compressor) {
147 const int libdeflate_level =
148 level == CLOD_COMPRESSION_HIGHEST ? 12 :
149 level == CLOD_COMPRESSION_HIGH ? 9 :
150 level == CLOD_COMPRESSION_NORMAL ? 6 :
151 level == CLOD_COMPRESSION_LOW ? 3 :
152 level == CLOD_COMPRESSION_LOWEST ? 1 :
153 6;
154
155 struct libdeflate_options opts = {0};
156 opts.sizeof_options = sizeof(opts);
157 opts.free_func = ctx->free_func;
158 opts.malloc_func = ctx->malloc_func;
159
160 compressor = libdeflate_alloc_compressor_ex(libdeflate_level, &opts);
161 if (!compressor) {
163 }
164 ctx->libdeflate_compressor[level] = compressor;
165 }
166
167 const size_t size = libdeflate_zlib_compress(compressor,
168 src, src_size,
169 dst, dst_max_size);
170
171 if (size == 0) return CLOD_COMPRESSION_SHORT_BUFFER;
172
173 *actual_size = size;
175 #else
177 #endif
178 }
179 case CLOD_DEFLATE: {
180 #if HAVE_LIBDEFLATE
181
182 struct libdeflate_compressor *compressor = ctx->libdeflate_compressor[level];
183 if (!compressor) {
184 const int libdeflate_level =
185 level == CLOD_COMPRESSION_HIGHEST ? 12 :
186 level == CLOD_COMPRESSION_HIGH ? 9 :
187 level == CLOD_COMPRESSION_NORMAL ? 6 :
188 level == CLOD_COMPRESSION_LOW ? 3 :
189 level == CLOD_COMPRESSION_LOWEST ? 1 :
190 6;
191
192 struct libdeflate_options opts = {0};
193 opts.sizeof_options = sizeof(opts);
194 opts.free_func = ctx->free_func;
195 opts.malloc_func = ctx->malloc_func;
196
197 compressor = libdeflate_alloc_compressor_ex(libdeflate_level, &opts);
198 if (!compressor) {
200 }
201 ctx->libdeflate_compressor[level] = compressor;
202 }
203
204 const size_t size = libdeflate_deflate_compress(compressor,
205 src, src_size,
206 dst, dst_max_size);
207
208 if (size == 0) return CLOD_COMPRESSION_SHORT_BUFFER;
209
210 *actual_size = size;
212 #else
214 #endif
215 }
216 case CLOD_LZ4F: {
217 #if HAVE_LIBLZ4
218
219 LZ4F_preferences_t prefs = LZ4F_INIT_PREFERENCES;
220 prefs.frameInfo.contentSize = src_size;
221 prefs.compressionLevel =
222 level == CLOD_COMPRESSION_HIGHEST ? LZ4HC_CLEVEL_MAX :
223 level == CLOD_COMPRESSION_HIGH ? LZ4HC_CLEVEL_OPT_MIN :
224 level == CLOD_COMPRESSION_NORMAL ? LZ4HC_CLEVEL_DEFAULT :
225 level == CLOD_COMPRESSION_LOW ? LZ4HC_CLEVEL_MIN :
226 level == CLOD_COMPRESSION_LOWEST ? 0 :
227 LZ4HC_CLEVEL_DEFAULT;
228
229 const size_t size = LZ4F_compressFrame(
230 dst, dst_max_size,
231 src, src_size,
232 &prefs);
233
234 if (LZ4F_isError(size)) {
236 }
237
238 *actual_size = size;
240
241 #else
243 #endif
244 }
245 case CLOD_XZ: {
246 #if HAVE_LIBLZMA
247
248 const lzma_allocator allocator = {
249 .alloc = compressor_lzma_malloc,
250 .free = compressor_lzma_free,
251 .opaque = ctx,
252 };
253
254 lzma_stream stream = LZMA_STREAM_INIT;
255 stream.allocator = &allocator;
256
257 const uint32_t preset =
258 level == CLOD_COMPRESSION_HIGHEST ? LZMA_PRESET_EXTREME | 9 :
259 level == CLOD_COMPRESSION_HIGH ? 8 :
260 level == CLOD_COMPRESSION_NORMAL ? 6 :
261 level == CLOD_COMPRESSION_LOW ? 3 :
262 level == CLOD_COMPRESSION_LOWEST ? 1 :
263 6;
264
265 lzma_ret ret = lzma_easy_encoder(&stream, preset, LZMA_CHECK_CRC64);
266 if (ret != LZMA_OK) {
267 return ret == LZMA_MEM_ERROR ? CLOD_COMPRESSION_ALLOC_FAILED : CLOD_COMPRESSION_UNSUPPORTED;
268 }
269
270 stream.next_in = (uint8_t *)src;
271 stream.avail_in = src_size;
272 stream.next_out = (uint8_t *)dst;
273 stream.avail_out = dst_max_size;
274
275 do {
276 ret = lzma_code(&stream, LZMA_FINISH);
277 } while (ret == LZMA_OK);
278
279 lzma_end(&stream);
280
281 switch (ret) {
282 case LZMA_STREAM_END:
283 *actual_size = stream.total_out;
285 case LZMA_BUF_ERROR:
286 if (stream.avail_out == 0) {
287 *actual_size = 0;
289 }
291 case LZMA_MEM_ERROR: return CLOD_COMPRESSION_ALLOC_FAILED;
292 default: return CLOD_COMPRESSION_UNSUPPORTED;
293 }
294
295 #else
297 #endif
298 }
299 case CLOD_ZSTD: {
300 #if HAVE_LIBZSTD
301 if (!ctx->zstd_cctx) {
302 ctx->zstd_cctx = ZSTD_createCCtx();
303 if (!ctx->zstd_cctx) return CLOD_COMPRESSION_ALLOC_FAILED;
304 }
305
306 const int compression_level =
307 level == CLOD_COMPRESSION_HIGHEST ? ZSTD_maxCLevel() :
308 level == CLOD_COMPRESSION_HIGH ? 14 :
309 level == CLOD_COMPRESSION_NORMAL ? 5 :
310 level == CLOD_COMPRESSION_LOW ? 3 :
311 level == CLOD_COMPRESSION_LOWEST ? 1 :
312 3;
313
314 const size_t size = ZSTD_compressCCtx(ctx->zstd_cctx,
315 dst, dst_max_size,
316 src, src_size,
317 compression_level
318 );
319 switch (ZSTD_getErrorCode(size)) {
320 case ZSTD_error_no_error:
321 *actual_size = size;
323 case ZSTD_error_dstSize_tooSmall:
325 case ZSTD_error_memory_allocation:
327 default:
329 }
330 #else
332 #endif
333 }
334 case CLOD_BZIP2: {
335 #if HAVE_LIBBZ2
336 // TODO: support sizes > UINT32_MAX
337 if (src_size > UINT_MAX)
339
340 bz_stream stream = {0};
341 stream.next_in = (char *)src;
342 stream.avail_in = (unsigned)src_size;
343 stream.next_out = (char *)dst;
344 stream.avail_out = (unsigned)dst_max_size;
345 stream.bzalloc = compressor_bz2_malloc;
346 stream.bzfree = compressor_bz2_free;
347 stream.opaque = ctx;
348
349 const int block_size =
350 level == CLOD_COMPRESSION_HIGHEST ? 9 :
351 level == CLOD_COMPRESSION_HIGH ? 7 :
352 level == CLOD_COMPRESSION_NORMAL ? 5 :
353 level == CLOD_COMPRESSION_LOW ? 3 :
354 level == CLOD_COMPRESSION_LOWEST ? 1 :
355 6;
356
357 int res = BZ2_bzCompressInit(&stream, block_size, 0, 0);
358 if (res == BZ_MEM_ERROR) return CLOD_COMPRESSION_ALLOC_FAILED;
359 if (res != BZ_OK) return CLOD_COMPRESSION_UNSUPPORTED;
360
361 do {
362 if (stream.avail_out == 0) {
363 BZ2_bzCompressEnd(&stream);
364 *actual_size = 0;
366 }
367 res = BZ2_bzCompress(&stream, BZ_FINISH);
368 } while (res == BZ_FINISH_OK);
369
370 BZ2_bzCompressEnd(&stream);
371
372 switch (res) {
373 case BZ_STREAM_END:
374 *actual_size = (size_t)stream.total_out_lo32 + ((size_t)stream.total_out_hi32 << 32);
376 case BZ_MEM_ERROR: return CLOD_COMPRESSION_ALLOC_FAILED;
377 default: return CLOD_COMPRESSION_UNSUPPORTED;
378 }
379
380 #else
382 #endif
383 }
384 default: {
386 }
387 }
388}
void clod_compressor_free(struct clod_compressor *ctx)
Definition compress.c:73
struct clod_compressor * clod_compressor_init()
Definition compress.c:64
clod_compression_level
Definition compression.h:74
clod_compression_result
clod_compression_method
Definition compression.h:32
enum clod_compression_result clod_compress(struct clod_compressor *ctx, void *dst, const size_t dst_max_size, const void *src, const size_t src_size, size_t *actual_size, const enum clod_compression_method method, const enum clod_compression_level level)
Definition compress.c:89
@ CLOD_COMPRESSION_HIGH
Definition compression.h:82
@ CLOD_COMPRESSION_NORMAL
Definition compression.h:80
@ CLOD_COMPRESSION_LOW
Definition compression.h:78
@ CLOD_COMPRESSION_LOWEST
Definition compression.h:76
@ CLOD_COMPRESSION_HIGHEST
Definition compression.h:84
@ CLOD_COMPRESSION_LEVELS
Definition compression.h:86
@ CLOD_COMPRESSION_ALLOC_FAILED
@ CLOD_COMPRESSION_UNSUPPORTED
@ CLOD_COMPRESSION_SUCCESS
@ CLOD_COMPRESSION_SHORT_BUFFER
@ CLOD_GZIP
Definition compression.h:38
@ CLOD_UNCOMPRESSED
Definition compression.h:34
@ CLOD_BZIP2
Definition compression.h:63
@ CLOD_ZSTD
Definition compression.h:59
@ CLOD_LZ4F
Definition compression.h:50
@ CLOD_XZ
Definition compression.h:54
@ CLOD_DEFLATE
Definition compression.h:46
@ CLOD_ZLIB
Definition compression.h:42