• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "compressor.h"
7 
libdeflate_compress_destsize(const struct erofs_compress * c,const void * src,unsigned int * srcsize,void * dst,unsigned int dstsize)8 static int libdeflate_compress_destsize(const struct erofs_compress *c,
9 				        const void *src, unsigned int *srcsize,
10 				        void *dst, unsigned int dstsize)
11 {
12 	static size_t last_uncompressed_size = 0;
13 	size_t l = 0; /* largest input that fits so far */
14 	size_t l_csize = 0;
15 	size_t r = *srcsize + 1; /* smallest input that doesn't fit so far */
16 	size_t m;
17 	u8 tmpbuf[dstsize + 9];
18 
19 	if (last_uncompressed_size)
20 		m = last_uncompressed_size * 15 / 16;
21 	else
22 		m = dstsize * 4;
23 	for (;;) {
24 		size_t csize;
25 
26 		m = max(m, l + 1);
27 		m = min(m, r - 1);
28 
29 		csize = libdeflate_deflate_compress(c->private_data, src, m,
30 						    tmpbuf, dstsize + 9);
31 		/*printf("Tried %zu => %zu\n", m, csize);*/
32 		if (csize > 0 && csize <= dstsize) {
33 			/* Fits */
34 			memcpy(dst, tmpbuf, csize);
35 			l = m;
36 			l_csize = csize;
37 			if (r <= l + 1 || csize +
38 				(22 - 2*(int)c->compression_level) >= dstsize)
39 				break;
40 			/*
41 			 * Estimate needed input prefix size based on current
42 			 * compression ratio.
43 			 */
44 			m = (dstsize * m) / csize;
45 		} else {
46 			/* Doesn't fit */
47 			r = m;
48 			if (r <= l + 1)
49 				break;
50 			m = (l + r) / 2;
51 		}
52 	}
53 
54 	/*
55 	 * Since generic EROFS on-disk compressed data will be filled with
56 	 * leading 0s (but no more than one block, 4KB for example, even the
57 	 * whole pcluster is 128KB) if not filled, it will be used to identify
58 	 * the actual compressed length as well without taking more reserved
59 	 * compressed bytes or some extra metadata to record this.
60 	 *
61 	 * DEFLATE streams can also be used in this way, if it starts from a
62 	 * non-last stored block, flag an unused bit instead to avoid the zero
63 	 * byte. It's still a valid one according to the DEFLATE specification.
64 	 */
65 	if (l_csize && !((u8 *)dst)[0])
66 	       ((u8 *)dst)[0] = 1 << (2 + 1);
67 
68 	/*printf("Choosing %zu => %zu\n", l, l_csize);*/
69 	*srcsize = l;
70 	last_uncompressed_size = l;
71 	return l_csize;
72 }
73 
compressor_libdeflate_exit(struct erofs_compress * c)74 static int compressor_libdeflate_exit(struct erofs_compress *c)
75 {
76 	if (!c->private_data)
77 		return -EINVAL;
78 
79 	libdeflate_free_compressor(c->private_data);
80 	return 0;
81 }
82 
compressor_libdeflate_init(struct erofs_compress * c)83 static int compressor_libdeflate_init(struct erofs_compress *c)
84 {
85 	c->private_data = NULL;
86 
87 	erofs_warn("EXPERIMENTAL libdeflate compressor in use. Use at your own risk!");
88 	return 0;
89 }
90 
erofs_compressor_libdeflate_setlevel(struct erofs_compress * c,int compression_level)91 static int erofs_compressor_libdeflate_setlevel(struct erofs_compress *c,
92 						int compression_level)
93 {
94 	if (compression_level < 0)
95 		compression_level = erofs_compressor_deflate.default_level;
96 
97 	libdeflate_free_compressor(c->private_data);
98 	c->private_data = libdeflate_alloc_compressor(compression_level);
99 	if (!c->private_data)
100 		return -ENOMEM;
101 	c->compression_level = compression_level;
102 	return 0;
103 }
104 
105 const struct erofs_compressor erofs_compressor_libdeflate = {
106 	.default_level = 1,
107 	.best_level = 12,
108 	.init = compressor_libdeflate_init,
109 	.exit = compressor_libdeflate_exit,
110 	.setlevel = erofs_compressor_libdeflate_setlevel,
111 	.compress_destsize = libdeflate_compress_destsize,
112 };
113