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