xref: /aosp_15_r20/external/erofs-utils/lib/compressor_libzstd.c (revision 33b1fccf6a0fada2c2875d400ed01119b7676ee5)
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 <zstd.h>
6*33b1fccfSAndroid Build Coastguard Worker #include <zstd_errors.h>
7*33b1fccfSAndroid Build Coastguard Worker #include <alloca.h>
8*33b1fccfSAndroid Build Coastguard Worker #include "compressor.h"
9*33b1fccfSAndroid Build Coastguard Worker #include "erofs/atomic.h"
10*33b1fccfSAndroid Build Coastguard Worker 
libzstd_compress_destsize(const struct erofs_compress * c,const void * src,unsigned int * srcsize,void * dst,unsigned int dstsize)11*33b1fccfSAndroid Build Coastguard Worker static int libzstd_compress_destsize(const struct erofs_compress *c,
12*33b1fccfSAndroid Build Coastguard Worker 				     const void *src, unsigned int *srcsize,
13*33b1fccfSAndroid Build Coastguard Worker 				     void *dst, unsigned int dstsize)
14*33b1fccfSAndroid Build Coastguard Worker {
15*33b1fccfSAndroid Build Coastguard Worker 	ZSTD_CCtx *cctx = c->private_data;
16*33b1fccfSAndroid Build Coastguard Worker 	size_t l = 0;		/* largest input that fits so far */
17*33b1fccfSAndroid Build Coastguard Worker 	size_t l_csize = 0;
18*33b1fccfSAndroid Build Coastguard Worker 	size_t r = *srcsize + 1; /* smallest input that doesn't fit so far */
19*33b1fccfSAndroid Build Coastguard Worker 	size_t m;
20*33b1fccfSAndroid Build Coastguard Worker 	u8 *fitblk_buffer = alloca(dstsize + 32);
21*33b1fccfSAndroid Build Coastguard Worker 
22*33b1fccfSAndroid Build Coastguard Worker 	m = dstsize * 4;
23*33b1fccfSAndroid Build Coastguard Worker 	for (;;) {
24*33b1fccfSAndroid Build Coastguard Worker 		size_t csize;
25*33b1fccfSAndroid Build Coastguard Worker 
26*33b1fccfSAndroid Build Coastguard Worker 		m = max(m, l + 1);
27*33b1fccfSAndroid Build Coastguard Worker 		m = min(m, r - 1);
28*33b1fccfSAndroid Build Coastguard Worker 
29*33b1fccfSAndroid Build Coastguard Worker 		csize = ZSTD_compress2(cctx, fitblk_buffer,
30*33b1fccfSAndroid Build Coastguard Worker 				       dstsize + 32, src, m);
31*33b1fccfSAndroid Build Coastguard Worker 		if (ZSTD_isError(csize)) {
32*33b1fccfSAndroid Build Coastguard Worker 			if (ZSTD_getErrorCode(csize) == ZSTD_error_dstSize_tooSmall)
33*33b1fccfSAndroid Build Coastguard Worker 				goto doesnt_fit;
34*33b1fccfSAndroid Build Coastguard Worker 			return -EFAULT;
35*33b1fccfSAndroid Build Coastguard Worker 		}
36*33b1fccfSAndroid Build Coastguard Worker 
37*33b1fccfSAndroid Build Coastguard Worker 		if (csize > 0 && csize <= dstsize) {
38*33b1fccfSAndroid Build Coastguard Worker 			/* Fits */
39*33b1fccfSAndroid Build Coastguard Worker 			memcpy(dst, fitblk_buffer, csize);
40*33b1fccfSAndroid Build Coastguard Worker 			l = m;
41*33b1fccfSAndroid Build Coastguard Worker 			l_csize = csize;
42*33b1fccfSAndroid Build Coastguard Worker 			if (r <= l + 1 || csize + 1 >= dstsize)
43*33b1fccfSAndroid Build Coastguard Worker 				break;
44*33b1fccfSAndroid Build Coastguard Worker 			/*
45*33b1fccfSAndroid Build Coastguard Worker 			 * Estimate needed input prefix size based on current
46*33b1fccfSAndroid Build Coastguard Worker 			 * compression ratio.
47*33b1fccfSAndroid Build Coastguard Worker 			 */
48*33b1fccfSAndroid Build Coastguard Worker 			m = (dstsize * m) / csize;
49*33b1fccfSAndroid Build Coastguard Worker 		} else {
50*33b1fccfSAndroid Build Coastguard Worker doesnt_fit:
51*33b1fccfSAndroid Build Coastguard Worker 			/* Doesn't fit */
52*33b1fccfSAndroid Build Coastguard Worker 			r = m;
53*33b1fccfSAndroid Build Coastguard Worker 			if (r <= l + 1)
54*33b1fccfSAndroid Build Coastguard Worker 				break;
55*33b1fccfSAndroid Build Coastguard Worker 			m = (l + r) / 2;
56*33b1fccfSAndroid Build Coastguard Worker 		}
57*33b1fccfSAndroid Build Coastguard Worker 	}
58*33b1fccfSAndroid Build Coastguard Worker 	*srcsize = l;
59*33b1fccfSAndroid Build Coastguard Worker 	return l_csize;
60*33b1fccfSAndroid Build Coastguard Worker }
61*33b1fccfSAndroid Build Coastguard Worker 
compressor_libzstd_exit(struct erofs_compress * c)62*33b1fccfSAndroid Build Coastguard Worker static int compressor_libzstd_exit(struct erofs_compress *c)
63*33b1fccfSAndroid Build Coastguard Worker {
64*33b1fccfSAndroid Build Coastguard Worker 	if (!c->private_data)
65*33b1fccfSAndroid Build Coastguard Worker 		return -EINVAL;
66*33b1fccfSAndroid Build Coastguard Worker 	ZSTD_freeCCtx(c->private_data);
67*33b1fccfSAndroid Build Coastguard Worker 	return 0;
68*33b1fccfSAndroid Build Coastguard Worker }
69*33b1fccfSAndroid Build Coastguard Worker 
erofs_compressor_libzstd_setlevel(struct erofs_compress * c,int compression_level)70*33b1fccfSAndroid Build Coastguard Worker static int erofs_compressor_libzstd_setlevel(struct erofs_compress *c,
71*33b1fccfSAndroid Build Coastguard Worker 					     int compression_level)
72*33b1fccfSAndroid Build Coastguard Worker {
73*33b1fccfSAndroid Build Coastguard Worker 	if (compression_level > erofs_compressor_libzstd.best_level) {
74*33b1fccfSAndroid Build Coastguard Worker 		erofs_err("invalid compression level %d", compression_level);
75*33b1fccfSAndroid Build Coastguard Worker 		return -EINVAL;
76*33b1fccfSAndroid Build Coastguard Worker 	}
77*33b1fccfSAndroid Build Coastguard Worker 	c->compression_level = compression_level;
78*33b1fccfSAndroid Build Coastguard Worker 	return 0;
79*33b1fccfSAndroid Build Coastguard Worker }
80*33b1fccfSAndroid Build Coastguard Worker 
erofs_compressor_libzstd_setdictsize(struct erofs_compress * c,u32 dict_size)81*33b1fccfSAndroid Build Coastguard Worker static int erofs_compressor_libzstd_setdictsize(struct erofs_compress *c,
82*33b1fccfSAndroid Build Coastguard Worker 						u32 dict_size)
83*33b1fccfSAndroid Build Coastguard Worker {
84*33b1fccfSAndroid Build Coastguard Worker 	if (!dict_size) {
85*33b1fccfSAndroid Build Coastguard Worker 		if (erofs_compressor_libzstd.default_dictsize) {
86*33b1fccfSAndroid Build Coastguard Worker 			dict_size = erofs_compressor_libzstd.default_dictsize;
87*33b1fccfSAndroid Build Coastguard Worker 		} else {
88*33b1fccfSAndroid Build Coastguard Worker 			dict_size = min_t(u32, Z_EROFS_ZSTD_MAX_DICT_SIZE,
89*33b1fccfSAndroid Build Coastguard Worker 					  cfg.c_mkfs_pclustersize_max << 3);
90*33b1fccfSAndroid Build Coastguard Worker 			dict_size = 1 << ilog2(dict_size);
91*33b1fccfSAndroid Build Coastguard Worker 		}
92*33b1fccfSAndroid Build Coastguard Worker 	}
93*33b1fccfSAndroid Build Coastguard Worker 	if (dict_size != 1 << ilog2(dict_size) ||
94*33b1fccfSAndroid Build Coastguard Worker 	    dict_size > Z_EROFS_ZSTD_MAX_DICT_SIZE) {
95*33b1fccfSAndroid Build Coastguard Worker 		erofs_err("invalid dictionary size %u", dict_size);
96*33b1fccfSAndroid Build Coastguard Worker 		return -EINVAL;
97*33b1fccfSAndroid Build Coastguard Worker 	}
98*33b1fccfSAndroid Build Coastguard Worker 	c->dict_size = dict_size;
99*33b1fccfSAndroid Build Coastguard Worker 	return 0;
100*33b1fccfSAndroid Build Coastguard Worker }
101*33b1fccfSAndroid Build Coastguard Worker 
compressor_libzstd_init(struct erofs_compress * c)102*33b1fccfSAndroid Build Coastguard Worker static int compressor_libzstd_init(struct erofs_compress *c)
103*33b1fccfSAndroid Build Coastguard Worker {
104*33b1fccfSAndroid Build Coastguard Worker 	static erofs_atomic_bool_t __warnonce;
105*33b1fccfSAndroid Build Coastguard Worker 	ZSTD_CCtx *cctx = c->private_data;
106*33b1fccfSAndroid Build Coastguard Worker 	size_t err;
107*33b1fccfSAndroid Build Coastguard Worker 
108*33b1fccfSAndroid Build Coastguard Worker 	ZSTD_freeCCtx(cctx);
109*33b1fccfSAndroid Build Coastguard Worker 	cctx = ZSTD_createCCtx();
110*33b1fccfSAndroid Build Coastguard Worker 	if (!cctx)
111*33b1fccfSAndroid Build Coastguard Worker 		return -ENOMEM;
112*33b1fccfSAndroid Build Coastguard Worker 
113*33b1fccfSAndroid Build Coastguard Worker 	err = ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, c->compression_level);
114*33b1fccfSAndroid Build Coastguard Worker 	if (ZSTD_isError(err)) {
115*33b1fccfSAndroid Build Coastguard Worker 		erofs_err("failed to set compression level: %s",
116*33b1fccfSAndroid Build Coastguard Worker 			  ZSTD_getErrorName(err));
117*33b1fccfSAndroid Build Coastguard Worker 		return -EINVAL;
118*33b1fccfSAndroid Build Coastguard Worker 	}
119*33b1fccfSAndroid Build Coastguard Worker 	err = ZSTD_CCtx_setParameter(cctx, ZSTD_c_windowLog, ilog2(c->dict_size));
120*33b1fccfSAndroid Build Coastguard Worker 	if (ZSTD_isError(err)) {
121*33b1fccfSAndroid Build Coastguard Worker 		erofs_err("failed to set window log: %s", ZSTD_getErrorName(err));
122*33b1fccfSAndroid Build Coastguard Worker 		return -EINVAL;
123*33b1fccfSAndroid Build Coastguard Worker 	}
124*33b1fccfSAndroid Build Coastguard Worker 	c->private_data = cctx;
125*33b1fccfSAndroid Build Coastguard Worker 
126*33b1fccfSAndroid Build Coastguard Worker 	if (!erofs_atomic_test_and_set(&__warnonce)) {
127*33b1fccfSAndroid Build Coastguard Worker 		erofs_warn("EXPERIMENTAL libzstd compressor in use. Note that `fitblk` isn't supported by upstream zstd for now.");
128*33b1fccfSAndroid Build Coastguard Worker 		erofs_warn("Therefore it will takes more time in order to get the optimal result.");
129*33b1fccfSAndroid Build Coastguard Worker 		erofs_info("You could clarify further needs in zstd repository <https://github.com/facebook/zstd/issues> for reference too.");
130*33b1fccfSAndroid Build Coastguard Worker 	}
131*33b1fccfSAndroid Build Coastguard Worker 	return 0;
132*33b1fccfSAndroid Build Coastguard Worker }
133*33b1fccfSAndroid Build Coastguard Worker 
134*33b1fccfSAndroid Build Coastguard Worker const struct erofs_compressor erofs_compressor_libzstd = {
135*33b1fccfSAndroid Build Coastguard Worker 	.default_level = ZSTD_CLEVEL_DEFAULT,
136*33b1fccfSAndroid Build Coastguard Worker 	.best_level = 22,
137*33b1fccfSAndroid Build Coastguard Worker 	.max_dictsize = Z_EROFS_ZSTD_MAX_DICT_SIZE,
138*33b1fccfSAndroid Build Coastguard Worker 	.init = compressor_libzstd_init,
139*33b1fccfSAndroid Build Coastguard Worker 	.exit = compressor_libzstd_exit,
140*33b1fccfSAndroid Build Coastguard Worker 	.setlevel = erofs_compressor_libzstd_setlevel,
141*33b1fccfSAndroid Build Coastguard Worker 	.setdictsize = erofs_compressor_libzstd_setdictsize,
142*33b1fccfSAndroid Build Coastguard Worker 	.compress_destsize = libzstd_compress_destsize,
143*33b1fccfSAndroid Build Coastguard Worker };
144