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