1/**
2 * Copyright (c) 2006-2023 LOVE Development Team
3 *
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 *
8 * Permission is granted to anyone to use this software for any purpose,
9 * including commercial applications, and to alter it and redistribute it
10 * freely, subject to the following restrictions:
11 *
12 * 1. The origin of this software must not be misrepresented; you must not
13 * claim that you wrote the original software. If you use this software
14 * in a product, an acknowledgment in the product documentation would be
15 * appreciated but is not required.
16 * 2. Altered source versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software.
18 * 3. This notice may not be removed or altered from any source distribution.
19 **/
20
21// LOVE
22#include "Compressor.h"
23#include "common/config.h"
24#include "common/int.h"
25
26#include "libraries/lz4/lz4.h"
27#include "libraries/lz4/lz4hc.h"
28
29#include <zlib.h>
30
31namespace love
32{
33namespace data
34{
35
36class LZ4Compressor : public Compressor
37{
38public:
39
40 char *compress(Format format, const char *data, size_t dataSize, int level, size_t &compressedSize) override
41 {
42 if (format != FORMAT_LZ4)
43 throw love::Exception("Invalid format (expecting LZ4)");
44
45 if (dataSize > LZ4_MAX_INPUT_SIZE)
46 throw love::Exception("Data is too large for LZ4 compressor.");
47
48 // We use a custom header to store some info with the compressed data.
49 const size_t headersize = sizeof(uint32);
50
51 int maxdestsize = LZ4_compressBound((int) dataSize);
52 size_t maxsize = headersize + (size_t) maxdestsize;
53 char *compressedbytes = nullptr;
54
55 try
56 {
57 compressedbytes = new char[maxsize];
58 }
59 catch (std::bad_alloc &)
60 {
61 throw love::Exception("Out of memory.");
62 }
63
64 // Store the size of the uncompressed data as a header.
65#ifdef LOVE_BIG_ENDIAN
66 // Make sure it's little-endian for storage.
67 *(uint32 *) compressedbytes = swapuint32((uint32) dataSize);
68#else
69 *(uint32 *) compressedbytes = (uint32) dataSize;
70#endif
71
72 // Use LZ4-HC for compression level 9 and higher.
73 int csize = 0;
74 if (level > 8)
75 csize = LZ4_compress_HC(data, compressedbytes + headersize, (int) dataSize, maxdestsize, LZ4HC_CLEVEL_DEFAULT);
76 else
77 csize = LZ4_compress_default(data, compressedbytes + headersize, (int) dataSize, maxdestsize);
78
79 if (csize <= 0)
80 {
81 delete[] compressedbytes;
82 throw love::Exception("Could not LZ4-compress data.");
83 }
84
85 // We allocated space for the maximum possible amount of data, but the
86 // actual compressed size might be much smaller, so we should shrink the
87 // data buffer if so.
88 if ((double) maxsize / (double) (csize + headersize) >= 1.2)
89 {
90 char *cbytes = new (std::nothrow) char[csize + headersize];
91 if (cbytes)
92 {
93 memcpy(cbytes, compressedbytes, csize + headersize);
94 delete[] compressedbytes;
95 compressedbytes = cbytes;
96 }
97 }
98
99 compressedSize = (size_t) csize + headersize;
100 return compressedbytes;
101 }
102
103 char *decompress(Format format, const char *data, size_t dataSize, size_t &decompressedSize) override
104 {
105 if (format != FORMAT_LZ4)
106 throw love::Exception("Invalid format (expecting LZ4)");
107
108 const size_t headersize = sizeof(uint32);
109 char *rawbytes = nullptr;
110
111 if (dataSize < headersize)
112 throw love::Exception("Invalid LZ4-compressed data size.");
113
114 // Extract the original uncompressed size (stored in our custom header.)
115#ifdef LOVE_BIG_ENDIAN
116 // Convert from stored little-endian to big-endian.
117 uint32 rawsize = swapuint32(*(uint32 *) data);
118#else
119 uint32 rawsize = *(uint32 *) data;
120#endif
121
122 try
123 {
124 rawbytes = new char[rawsize];
125 }
126 catch (std::bad_alloc &)
127 {
128 throw love::Exception("Out of memory.");
129 }
130
131 // If the uncompressed size is passed in as an argument (non-zero) and
132 // it matches the header's stored size, then we assume it's 100% accurate
133 // and we use a more efficient decompression function.
134 if (decompressedSize > 0 && decompressedSize == (size_t) rawsize)
135 {
136 // We don't use the header here, but we need to account for its size.
137 if (LZ4_decompress_fast(data + headersize, rawbytes, (int) decompressedSize) < 0)
138 {
139 delete[] rawbytes;
140 throw love::Exception("Could not decompress LZ4-compressed data.");
141 }
142 }
143 else
144 {
145 // Account for our custom header's size in the decompress arguments.
146 int result = LZ4_decompress_safe(data + headersize, rawbytes,
147 (int) (dataSize - headersize), rawsize);
148
149 if (result < 0)
150 {
151 delete[] rawbytes;
152 throw love::Exception("Could not decompress LZ4-compressed data.");
153 }
154
155 decompressedSize = (size_t) result;
156 }
157
158 return rawbytes;
159 }
160
161 bool isSupported(Format format) const override
162 {
163 return format == FORMAT_LZ4;
164 }
165
166}; // LZ4Compressor
167
168
169class zlibCompressor : public Compressor
170{
171private:
172
173 // The following three functions are mostly copied from the zlib source
174 // (compressBound, compress2, and uncompress), but modified to support both
175 // zlib and gzip.
176
177 uLong zlibCompressBound(Format format, uLong sourceLen)
178 {
179 uLong size = sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + (sourceLen >> 25) + 13;
180
181 // The gzip header is slightly larger than the zlib header.
182 if (format == FORMAT_GZIP)
183 size += 18 - 6;
184
185 return size;
186 }
187
188 int zlibCompress(Format format, Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen, int level)
189 {
190 z_stream stream = {};
191
192 stream.next_in = (Bytef *) source;
193 stream.avail_in = (uInt) sourceLen;
194
195 stream.next_out = dest;
196 stream.avail_out = (uInt) (*destLen);
197
198 int windowbits = 15;
199 if (format == FORMAT_GZIP)
200 windowbits += 16; // This tells zlib to use a gzip header.
201 else if (format == FORMAT_DEFLATE)
202 windowbits = -windowbits;
203
204 int err = deflateInit2(&stream, level, Z_DEFLATED, windowbits, 8, Z_DEFAULT_STRATEGY);
205
206 if (err != Z_OK)
207 return err;
208
209 err = deflate(&stream, Z_FINISH);
210
211 if (err != Z_STREAM_END)
212 {
213 deflateEnd(&stream);
214 return err == Z_OK ? Z_BUF_ERROR : err;
215 }
216
217 *destLen = stream.total_out;
218
219 return deflateEnd(&stream);
220 }
221
222 int zlibDecompress(Format format, Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen)
223 {
224 z_stream stream = {};
225
226 stream.next_in = (Bytef *) source;
227 stream.avail_in = (uInt) sourceLen;
228
229 stream.next_out = dest;
230 stream.avail_out = (uInt) (*destLen);
231
232 // 15 is the default. Adding 32 makes zlib auto-detect the header type.
233 int windowbits = 15 + 32;
234
235 if (format == FORMAT_DEFLATE)
236 windowbits = -15;
237
238 int err = inflateInit2(&stream, windowbits);
239
240 if (err != Z_OK)
241 return err;
242
243 err = inflate(&stream, Z_FINISH);
244
245 if (err != Z_STREAM_END)
246 {
247 inflateEnd(&stream);
248 if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
249 return Z_DATA_ERROR;
250 return err;
251 }
252
253 *destLen = stream.total_out;
254
255 return inflateEnd(&stream);
256 }
257
258public:
259
260 char *compress(Format format, const char *data, size_t dataSize, int level, size_t &compressedSize) override
261 {
262 if (!isSupported(format))
263 throw love::Exception("Invalid format (expecting zlib or gzip)");
264
265 if (level < 0)
266 level = Z_DEFAULT_COMPRESSION;
267 else if (level > 9)
268 level = 9;
269
270 uLong maxsize = zlibCompressBound(format, (uLong) dataSize);
271 char *compressedbytes = nullptr;
272
273 try
274 {
275 compressedbytes = new char[maxsize];
276 }
277 catch (std::bad_alloc &)
278 {
279 throw love::Exception("Out of memory.");
280 }
281
282 uLongf destlen = maxsize;
283 int status = zlibCompress(format, (Bytef *) compressedbytes, &destlen, (const Bytef *) data, (uLong) dataSize, level);
284
285 if (status != Z_OK)
286 {
287 delete[] compressedbytes;
288 throw love::Exception("Could not zlib/gzip-compress data.");
289 }
290
291 // We allocated space for the maximum possible amount of data, but the
292 // actual compressed size might be much smaller, so we should shrink the
293 // data buffer if so.
294 if ((double) maxsize / (double) destlen >= 1.3)
295 {
296 char *cbytes = new (std::nothrow) char[destlen];
297 if (cbytes)
298 {
299 memcpy(cbytes, compressedbytes, destlen);
300 delete[] compressedbytes;
301 compressedbytes = cbytes;
302 }
303 }
304
305 compressedSize = (size_t) destlen;
306 return compressedbytes;
307 }
308
309 char *decompress(Format format, const char *data, size_t dataSize, size_t &decompressedSize) override
310 {
311 if (!isSupported(format))
312 throw love::Exception("Invalid format (expecting zlib or gzip)");
313
314 char *rawbytes = nullptr;
315
316 // We might know the output size before decompression. If not, we guess.
317 size_t rawsize = decompressedSize > 0 ? decompressedSize : dataSize * 2;
318
319 // Repeatedly try to decompress with an increasingly large output buffer.
320 while (true)
321 {
322 try
323 {
324 rawbytes = new char[rawsize];
325 }
326 catch (std::bad_alloc &)
327 {
328 throw love::Exception("Out of memory.");
329 }
330
331 uLongf destLen = (uLongf) rawsize;
332 int status = zlibDecompress(format, (Bytef *) rawbytes, &destLen, (const Bytef *) data, (uLong) dataSize);
333
334 if (status == Z_OK)
335 {
336 decompressedSize = (size_t) destLen;
337 break;
338 }
339 else if (status != Z_BUF_ERROR)
340 {
341 // For any error other than "not enough room", throw an exception.
342 delete[] rawbytes;
343 throw love::Exception("Could not decompress zlib/gzip-compressed data.");
344 }
345
346 // Not enough room in the output buffer: try again with a larger size.
347 delete[] rawbytes;
348 rawsize *= 2;
349 }
350
351 return rawbytes;
352 }
353
354 bool isSupported(Format format) const override
355 {
356 return format == FORMAT_ZLIB || format == FORMAT_GZIP || format == FORMAT_DEFLATE;
357 }
358
359}; // zlibCompressor
360
361Compressor *Compressor::getCompressor(Format format)
362{
363 static LZ4Compressor lz4compressor;
364 static zlibCompressor zlibcompressor;
365
366 Compressor *compressors[] = {&lz4compressor, &zlibcompressor};
367
368 for (Compressor *c : compressors)
369 {
370 if (c->isSupported(format))
371 return c;
372 }
373
374 return nullptr;
375}
376
377bool Compressor::getConstant(const char *in, Format &out)
378{
379 return formatNames.find(in, out);
380}
381
382bool Compressor::getConstant(Format in, const char *&out)
383{
384 return formatNames.find(in, out);
385}
386
387std::vector<std::string> Compressor::getConstants(Format)
388{
389 return formatNames.getNames();
390}
391
392StringMap<Compressor::Format, Compressor::FORMAT_MAX_ENUM>::Entry Compressor::formatEntries[] =
393{
394 { "lz4", FORMAT_LZ4 },
395 { "zlib", FORMAT_ZLIB },
396 { "gzip", FORMAT_GZIP },
397 { "deflate", FORMAT_DEFLATE },
398};
399
400StringMap<Compressor::Format, Compressor::FORMAT_MAX_ENUM> Compressor::formatNames(Compressor::formatEntries, sizeof(Compressor::formatEntries));
401
402} // data
403} // love
404