]> wimlib.net Git - wimlib/blob - src/compress.c
aed609aa7aff560a05e11a2021af3fabdb227749
[wimlib] / src / compress.c
1 /*
2  * compress.c
3  *
4  * Generic functions for compression, wrapping around actual compression
5  * implementations.
6  */
7
8 /*
9  * Copyright (C) 2013, 2014 Eric Biggers
10  *
11  * This file is part of wimlib, a library for working with WIM files.
12  *
13  * wimlib is free software; you can redistribute it and/or modify it under the
14  * terms of the GNU General Public License as published by the Free
15  * Software Foundation; either version 3 of the License, or (at your option)
16  * any later version.
17  *
18  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
19  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
20  * A PARTICULAR PURPOSE. See the GNU General Public License for more
21  * details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with wimlib; if not, see http://www.gnu.org/licenses/.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #  include "config.h"
29 #endif
30
31 #include "wimlib.h"
32 #include "wimlib/assert.h"
33 #include "wimlib/error.h"
34 #include "wimlib/compressor_ops.h"
35 #include "wimlib/util.h"
36
37 #include <stdlib.h>
38 #include <string.h>
39
40 struct wimlib_compressor {
41         const struct compressor_ops *ops;
42         void *private;
43         enum wimlib_compression_type ctype;
44         size_t max_block_size;
45 };
46
47 static const struct compressor_ops *compressor_ops[] = {
48         [WIMLIB_COMPRESSION_TYPE_XPRESS] = &xpress_compressor_ops,
49         [WIMLIB_COMPRESSION_TYPE_LZX]    = &lzx_compressor_ops,
50         [WIMLIB_COMPRESSION_TYPE_LZMS]   = &lzms_compressor_ops,
51 };
52
53 /* Scale: 10 = low, 50 = medium, 100 = high */
54
55 #define DEFAULT_COMPRESSION_LEVEL 50
56
57 static unsigned int default_compression_levels[ARRAY_LEN(compressor_ops)];
58
59 static bool
60 compressor_ctype_valid(int ctype)
61 {
62         return (ctype >= 0 &&
63                 ctype < ARRAY_LEN(compressor_ops) &&
64                 compressor_ops[ctype] != NULL);
65 }
66
67 WIMLIBAPI int
68 wimlib_set_default_compression_level(int ctype, unsigned int compression_level)
69 {
70         if (ctype == -1) {
71                 for (int i = 0; i < ARRAY_LEN(default_compression_levels); i++)
72                         default_compression_levels[i] = compression_level;
73         } else {
74                 if (!compressor_ctype_valid(ctype))
75                         return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
76
77                 default_compression_levels[ctype] = compression_level;
78         }
79         return 0;
80 }
81
82 WIMLIBAPI u64
83 wimlib_get_compressor_needed_memory(enum wimlib_compression_type ctype,
84                                     size_t max_block_size,
85                                     unsigned int compression_level)
86 {
87         const struct compressor_ops *ops;
88         u64 size;
89
90         if (!compressor_ctype_valid(ctype))
91                 return 0;
92
93         if (max_block_size == 0)
94                 return 0;
95
96         ops = compressor_ops[ctype];
97
98         if (compression_level == 0)
99                 compression_level = default_compression_levels[ctype];
100         if (compression_level == 0)
101                 compression_level = DEFAULT_COMPRESSION_LEVEL;
102
103         if (ops->get_needed_memory) {
104                 size = ops->get_needed_memory(max_block_size, compression_level);
105
106                 /* 0 is never valid and indicates an invalid max_block_size.  */
107                 if (size == 0)
108                         return 0;
109         } else {
110                 size = 0;
111         }
112         return size + sizeof(struct wimlib_compressor);
113 }
114
115 WIMLIBAPI int
116 wimlib_create_compressor(enum wimlib_compression_type ctype,
117                          size_t max_block_size,
118                          unsigned int compression_level,
119                          struct wimlib_compressor **c_ret)
120 {
121         struct wimlib_compressor *c;
122
123         if (!compressor_ctype_valid(ctype))
124                 return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
125
126         if (c_ret == NULL)
127                 return WIMLIB_ERR_INVALID_PARAM;
128
129         if (max_block_size == 0)
130                 return WIMLIB_ERR_INVALID_PARAM;
131
132         c = MALLOC(sizeof(*c));
133         if (c == NULL)
134                 return WIMLIB_ERR_NOMEM;
135         c->ops = compressor_ops[ctype];
136         c->private = NULL;
137         c->ctype = ctype;
138         c->max_block_size = max_block_size;
139         if (c->ops->create_compressor) {
140                 int ret;
141
142                 if (compression_level == 0)
143                         compression_level = default_compression_levels[ctype];
144                 if (compression_level == 0)
145                         compression_level = DEFAULT_COMPRESSION_LEVEL;
146
147                 ret = c->ops->create_compressor(max_block_size,
148                                                 compression_level,
149                                                 &c->private);
150                 if (ret) {
151                         FREE(c);
152                         return ret;
153                 }
154         }
155         *c_ret = c;
156         return 0;
157 }
158
159 WIMLIBAPI size_t
160 wimlib_compress(const void *uncompressed_data, size_t uncompressed_size,
161                 void *compressed_data, size_t compressed_size_avail,
162                 struct wimlib_compressor *c)
163 {
164         size_t compressed_size;
165
166         if (unlikely(uncompressed_size == 0 || uncompressed_size > c->max_block_size))
167                 return 0;
168
169         compressed_size = c->ops->compress(uncompressed_data,
170                                            uncompressed_size,
171                                            compressed_data,
172                                            compressed_size_avail,
173                                            c->private);
174
175         /* (Optional) Verify that we really get the same thing back when
176          * decompressing.  Should always be the case, unless there's a bug.  */
177 #ifdef ENABLE_VERIFY_COMPRESSION
178         if (compressed_size != 0) {
179                 struct wimlib_decompressor *d;
180                 int res;
181                 u8 *buf;
182
183                 buf = MALLOC(uncompressed_size);
184                 if (!buf) {
185                         WARNING("Unable to verify results of %s compression "
186                                 "(can't allocate buffer)",
187                                 wimlib_get_compression_type_string(c->ctype));
188                         return 0;
189                 }
190
191                 res = wimlib_create_decompressor(c->ctype,
192                                                  c->max_block_size, &d);
193                 if (res) {
194                         WARNING("Unable to verify results of %s compression "
195                                 "(can't create decompressor)",
196                                 wimlib_get_compression_type_string(c->ctype));
197                         FREE(buf);
198                         return 0;
199                 }
200
201                 res = wimlib_decompress(compressed_data, compressed_size,
202                                         buf, uncompressed_size, d);
203                 wimlib_free_decompressor(d);
204                 if (res) {
205                         ERROR("Failed to decompress our %s-compressed data",
206                               wimlib_get_compression_type_string(c->ctype));
207                         FREE(buf);
208                         abort();
209                 }
210
211                 res = memcmp(uncompressed_data, buf, uncompressed_size);
212                 FREE(buf);
213
214                 if (res) {
215                         ERROR("Our %s-compressed data did not decompress "
216                               "to original",
217                               wimlib_get_compression_type_string(c->ctype));
218                         abort();
219                 }
220         }
221 #endif /* ENABLE_VERIFY_COMPRESSION */
222
223         return compressed_size;
224 }
225
226 WIMLIBAPI void
227 wimlib_free_compressor(struct wimlib_compressor *c)
228 {
229         if (c) {
230                 if (c->ops->free_compressor)
231                         c->ops->free_compressor(c->private);
232                 FREE(c);
233         }
234 }