1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
2 #include "erofs/internal.h"
3 #include "erofs/print.h"
4 #include "erofs/config.h"
5 #include <libdeflate.h>
6 #include <stdlib.h>
7 #include "compressor.h"
8 #include "erofs/atomic.h"
9
10 struct erofs_libdeflate_context {
11 struct libdeflate_compressor *strm;
12 size_t last_uncompressed_size;
13 };
14
libdeflate_compress_destsize(const struct erofs_compress * c,const void * src,unsigned int * srcsize,void * dst,unsigned int dstsize)15 static int libdeflate_compress_destsize(const struct erofs_compress *c,
16 const void *src, unsigned int *srcsize,
17 void *dst, unsigned int dstsize)
18 {
19 struct erofs_libdeflate_context *ctx = c->private_data;
20 size_t l = 0; /* largest input that fits so far */
21 size_t l_csize = 0;
22 size_t r = *srcsize + 1; /* smallest input that doesn't fit so far */
23 size_t m;
24 u8 tmpbuf[dstsize + 9];
25
26 if (ctx->last_uncompressed_size)
27 m = ctx->last_uncompressed_size * 15 / 16;
28 else
29 m = dstsize * 4;
30 for (;;) {
31 size_t csize;
32
33 m = max(m, l + 1);
34 m = min(m, r - 1);
35
36 csize = libdeflate_deflate_compress(ctx->strm, src, m,
37 tmpbuf, dstsize + 9);
38 /*printf("Tried %zu => %zu\n", m, csize);*/
39 if (csize > 0 && csize <= dstsize) {
40 /* Fits */
41 memcpy(dst, tmpbuf, csize);
42 l = m;
43 l_csize = csize;
44 if (r <= l + 1 || csize +
45 (22 - 2*(int)c->compression_level) >= dstsize)
46 break;
47 /*
48 * Estimate needed input prefix size based on current
49 * compression ratio.
50 */
51 m = (dstsize * m) / csize;
52 } else {
53 /* Doesn't fit */
54 r = m;
55 if (r <= l + 1)
56 break;
57 m = (l + r) / 2;
58 }
59 }
60
61 /*
62 * Since generic EROFS on-disk compressed data will be filled with
63 * leading 0s (but no more than one block, 4KB for example, even the
64 * whole pcluster is 128KB) if not filled, it will be used to identify
65 * the actual compressed length as well without taking more reserved
66 * compressed bytes or some extra metadata to record this.
67 *
68 * DEFLATE streams can also be used in this way, if it starts from a
69 * non-last stored block, flag an unused bit instead to avoid the zero
70 * byte. It's still a valid one according to the DEFLATE specification.
71 */
72 if (l_csize && !((u8 *)dst)[0])
73 ((u8 *)dst)[0] = 1 << (2 + 1);
74
75 /*printf("Choosing %zu => %zu\n", l, l_csize);*/
76 *srcsize = l;
77 ctx->last_uncompressed_size = l;
78 return l_csize;
79 }
80
compressor_libdeflate_exit(struct erofs_compress * c)81 static int compressor_libdeflate_exit(struct erofs_compress *c)
82 {
83 struct erofs_libdeflate_context *ctx = c->private_data;
84
85 if (!ctx)
86 return -EINVAL;
87 libdeflate_free_compressor(ctx->strm);
88 free(ctx);
89 return 0;
90 }
91
compressor_libdeflate_init(struct erofs_compress * c)92 static int compressor_libdeflate_init(struct erofs_compress *c)
93 {
94 static erofs_atomic_bool_t __warnonce;
95 struct erofs_libdeflate_context *ctx;
96
97 DBG_BUGON(c->private_data);
98 ctx = calloc(1, sizeof(struct erofs_libdeflate_context));
99 if (!ctx)
100 return -ENOMEM;
101 ctx->strm = libdeflate_alloc_compressor(c->compression_level);
102 if (!ctx->strm) {
103 free(ctx);
104 return -ENOMEM;
105 }
106 c->private_data = ctx;
107 if (!erofs_atomic_test_and_set(&__warnonce))
108 erofs_warn("EXPERIMENTAL libdeflate compressor in use. Use at your own risk!");
109 return 0;
110 }
111
compressor_libdeflate_reset(struct erofs_compress * c)112 static void compressor_libdeflate_reset(struct erofs_compress *c)
113 {
114 struct erofs_libdeflate_context *ctx = c->private_data;
115
116 ctx->last_uncompressed_size = 0;
117 }
118
erofs_compressor_libdeflate_setlevel(struct erofs_compress * c,int compression_level)119 static int erofs_compressor_libdeflate_setlevel(struct erofs_compress *c,
120 int compression_level)
121 {
122 if (compression_level < 0)
123 compression_level = erofs_compressor_libdeflate.default_level;
124
125 if (compression_level > erofs_compressor_libdeflate.best_level) {
126 erofs_err("invalid compression level %d", compression_level);
127 return -EINVAL;
128 }
129 c->compression_level = compression_level;
130 return 0;
131 }
132
133 const struct erofs_compressor erofs_compressor_libdeflate = {
134 .default_level = 1,
135 .best_level = 12,
136 .init = compressor_libdeflate_init,
137 .exit = compressor_libdeflate_exit,
138 .reset = compressor_libdeflate_reset,
139 .setlevel = erofs_compressor_libdeflate_setlevel,
140 .compress_destsize = libdeflate_compress_destsize,
141 };
142