]> wimlib.net Git - wimlib/blob - src/compress.c
4c99dab145db7b04c5f6fb80f6207d6537b95179
[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         ops = compressor_ops[ctype];
94
95         if (compression_level == 0)
96                 compression_level = default_compression_levels[ctype];
97         if (compression_level == 0)
98                 compression_level = DEFAULT_COMPRESSION_LEVEL;
99
100         size = sizeof(struct wimlib_compressor);
101         if (ops->get_needed_memory)
102                 size += ops->get_needed_memory(max_block_size, compression_level);
103         return size;
104 }
105
106 WIMLIBAPI int
107 wimlib_create_compressor(enum wimlib_compression_type ctype,
108                          size_t max_block_size,
109                          unsigned int compression_level,
110                          struct wimlib_compressor **c_ret)
111 {
112         struct wimlib_compressor *c;
113
114         if (c_ret == NULL)
115                 return WIMLIB_ERR_INVALID_PARAM;
116
117         if (max_block_size == 0)
118                 return WIMLIB_ERR_INVALID_PARAM;
119
120         if (!compressor_ctype_valid(ctype))
121                 return WIMLIB_ERR_INVALID_COMPRESSION_TYPE;
122
123         c = MALLOC(sizeof(*c));
124         if (c == NULL)
125                 return WIMLIB_ERR_NOMEM;
126         c->ops = compressor_ops[ctype];
127         c->private = NULL;
128         c->ctype = ctype;
129         c->max_block_size = max_block_size;
130         if (c->ops->create_compressor) {
131                 int ret;
132
133                 if (compression_level == 0)
134                         compression_level = default_compression_levels[ctype];
135                 if (compression_level == 0)
136                         compression_level = DEFAULT_COMPRESSION_LEVEL;
137
138                 ret = c->ops->create_compressor(max_block_size,
139                                                 compression_level,
140                                                 &c->private);
141                 if (ret) {
142                         FREE(c);
143                         return ret;
144                 }
145         }
146         *c_ret = c;
147         return 0;
148 }
149
150 WIMLIBAPI size_t
151 wimlib_compress(const void *uncompressed_data, size_t uncompressed_size,
152                 void *compressed_data, size_t compressed_size_avail,
153                 struct wimlib_compressor *c)
154 {
155         size_t compressed_size;
156
157         if (unlikely(uncompressed_size == 0 || uncompressed_size > c->max_block_size))
158                 return 0;
159
160         compressed_size = c->ops->compress(uncompressed_data,
161                                            uncompressed_size,
162                                            compressed_data,
163                                            compressed_size_avail,
164                                            c->private);
165
166         /* (Optional) Verify that we really get the same thing back when
167          * decompressing.  Should always be the case, unless there's a bug.  */
168 #ifdef ENABLE_VERIFY_COMPRESSION
169         if (compressed_size != 0) {
170                 struct wimlib_decompressor *d;
171                 int res;
172                 u8 *buf;
173
174                 buf = MALLOC(uncompressed_size);
175                 if (!buf) {
176                         WARNING("Unable to verify results of %s compression "
177                                 "(can't allocate buffer)",
178                                 wimlib_get_compression_type_string(c->ctype));
179                         return 0;
180                 }
181
182                 res = wimlib_create_decompressor(c->ctype,
183                                                  c->max_block_size, &d);
184                 if (res) {
185                         WARNING("Unable to verify results of %s compression "
186                                 "(can't create decompressor)",
187                                 wimlib_get_compression_type_string(c->ctype));
188                         FREE(buf);
189                         return 0;
190                 }
191
192                 res = wimlib_decompress(compressed_data, compressed_size,
193                                         buf, uncompressed_size, d);
194                 wimlib_free_decompressor(d);
195                 if (res) {
196                         ERROR("Failed to decompress our %s-compressed data",
197                               wimlib_get_compression_type_string(c->ctype));
198                         FREE(buf);
199                         abort();
200                 }
201
202                 res = memcmp(uncompressed_data, buf, uncompressed_size);
203                 FREE(buf);
204
205                 if (res) {
206                         ERROR("Our %s-compressed data did not decompress "
207                               "to original",
208                               wimlib_get_compression_type_string(c->ctype));
209                         abort();
210                 }
211         }
212 #endif /* ENABLE_VERIFY_COMPRESSION */
213
214         return compressed_size;
215 }
216
217 WIMLIBAPI void
218 wimlib_free_compressor(struct wimlib_compressor *c)
219 {
220         if (c) {
221                 if (c->ops->free_compressor)
222                         c->ops->free_compressor(c->private);
223                 FREE(c);
224         }
225 }