compress.c revision 7ec681f3
17ec681f3Smrg/* 27ec681f3Smrg * Copyright © 2021 Valve Corporation 37ec681f3Smrg * 47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 57ec681f3Smrg * copy of this software and associated documentation files (the "Software"), 67ec681f3Smrg * to deal in the Software without restriction, including without limitation 77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the 97ec681f3Smrg * Software is furnished to do so, subject to the following conditions: 107ec681f3Smrg * 117ec681f3Smrg * The above copyright notice and this permission notice (including the next 127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the 137ec681f3Smrg * Software. 147ec681f3Smrg * 157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 207ec681f3Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 217ec681f3Smrg * IN THE SOFTWARE. 227ec681f3Smrg */ 237ec681f3Smrg 247ec681f3Smrg#ifdef HAVE_COMPRESSION 257ec681f3Smrg 267ec681f3Smrg#include <assert.h> 277ec681f3Smrg 287ec681f3Smrg/* Ensure that zlib uses 'const' in 'z_const' declarations. */ 297ec681f3Smrg#ifndef ZLIB_CONST 307ec681f3Smrg#define ZLIB_CONST 317ec681f3Smrg#endif 327ec681f3Smrg 337ec681f3Smrg#ifdef HAVE_ZLIB 347ec681f3Smrg#include "zlib.h" 357ec681f3Smrg#endif 367ec681f3Smrg 377ec681f3Smrg#ifdef HAVE_ZSTD 387ec681f3Smrg#include "zstd.h" 397ec681f3Smrg#endif 407ec681f3Smrg 417ec681f3Smrg#include "util/compress.h" 427ec681f3Smrg#include "macros.h" 437ec681f3Smrg 447ec681f3Smrg/* 3 is the recomended level, with 22 as the absolute maximum */ 457ec681f3Smrg#define ZSTD_COMPRESSION_LEVEL 3 467ec681f3Smrg 477ec681f3Smrgsize_t 487ec681f3Smrgutil_compress_max_compressed_len(size_t in_data_size) 497ec681f3Smrg{ 507ec681f3Smrg#ifdef HAVE_ZSTD 517ec681f3Smrg /* from the zstd docs (https://facebook.github.io/zstd/zstd_manual.html): 527ec681f3Smrg * compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. 537ec681f3Smrg */ 547ec681f3Smrg return ZSTD_compressBound(in_data_size); 557ec681f3Smrg#elif defined(HAVE_ZLIB) 567ec681f3Smrg /* From https://zlib.net/zlib_tech.html: 577ec681f3Smrg * 587ec681f3Smrg * "In the worst possible case, where the other block types would expand 597ec681f3Smrg * the data, deflation falls back to stored (uncompressed) blocks. Thus 607ec681f3Smrg * for the default settings used by deflateInit(), compress(), and 617ec681f3Smrg * compress2(), the only expansion is an overhead of five bytes per 16 KB 627ec681f3Smrg * block (about 0.03%), plus a one-time overhead of six bytes for the 637ec681f3Smrg * entire stream." 647ec681f3Smrg */ 657ec681f3Smrg size_t num_blocks = (in_data_size + 16383) / 16384; /* round up blocks */ 667ec681f3Smrg return in_data_size + 6 + (num_blocks * 5); 677ec681f3Smrg#else 687ec681f3Smrg STATIC_ASSERT(false); 697ec681f3Smrg#endif 707ec681f3Smrg} 717ec681f3Smrg 727ec681f3Smrg/* Compress data and return the size of the compressed data */ 737ec681f3Smrgsize_t 747ec681f3Smrgutil_compress_deflate(const uint8_t *in_data, size_t in_data_size, 757ec681f3Smrg uint8_t *out_data, size_t out_buff_size) 767ec681f3Smrg{ 777ec681f3Smrg#ifdef HAVE_ZSTD 787ec681f3Smrg size_t ret = ZSTD_compress(out_data, out_buff_size, in_data, in_data_size, 797ec681f3Smrg ZSTD_COMPRESSION_LEVEL); 807ec681f3Smrg if (ZSTD_isError(ret)) 817ec681f3Smrg return 0; 827ec681f3Smrg 837ec681f3Smrg return ret; 847ec681f3Smrg#elif defined(HAVE_ZLIB) 857ec681f3Smrg size_t compressed_size = 0; 867ec681f3Smrg 877ec681f3Smrg /* allocate deflate state */ 887ec681f3Smrg z_stream strm; 897ec681f3Smrg strm.zalloc = Z_NULL; 907ec681f3Smrg strm.zfree = Z_NULL; 917ec681f3Smrg strm.opaque = Z_NULL; 927ec681f3Smrg strm.next_in = in_data; 937ec681f3Smrg strm.next_out = out_data; 947ec681f3Smrg strm.avail_in = in_data_size; 957ec681f3Smrg strm.avail_out = out_buff_size; 967ec681f3Smrg 977ec681f3Smrg int ret = deflateInit(&strm, Z_BEST_COMPRESSION); 987ec681f3Smrg if (ret != Z_OK) { 997ec681f3Smrg (void) deflateEnd(&strm); 1007ec681f3Smrg return 0; 1017ec681f3Smrg } 1027ec681f3Smrg 1037ec681f3Smrg /* compress until end of in_data */ 1047ec681f3Smrg ret = deflate(&strm, Z_FINISH); 1057ec681f3Smrg 1067ec681f3Smrg /* stream should be complete */ 1077ec681f3Smrg assert(ret == Z_STREAM_END); 1087ec681f3Smrg if (ret == Z_STREAM_END) { 1097ec681f3Smrg compressed_size = strm.total_out; 1107ec681f3Smrg } 1117ec681f3Smrg 1127ec681f3Smrg /* clean up and return */ 1137ec681f3Smrg (void) deflateEnd(&strm); 1147ec681f3Smrg return compressed_size; 1157ec681f3Smrg#else 1167ec681f3Smrg STATIC_ASSERT(false); 1177ec681f3Smrg# endif 1187ec681f3Smrg} 1197ec681f3Smrg 1207ec681f3Smrg/** 1217ec681f3Smrg * Decompresses data, returns true if successful. 1227ec681f3Smrg */ 1237ec681f3Smrgbool 1247ec681f3Smrgutil_compress_inflate(const uint8_t *in_data, size_t in_data_size, 1257ec681f3Smrg uint8_t *out_data, size_t out_data_size) 1267ec681f3Smrg{ 1277ec681f3Smrg#ifdef HAVE_ZSTD 1287ec681f3Smrg size_t ret = ZSTD_decompress(out_data, out_data_size, in_data, in_data_size); 1297ec681f3Smrg return !ZSTD_isError(ret); 1307ec681f3Smrg#elif defined(HAVE_ZLIB) 1317ec681f3Smrg z_stream strm; 1327ec681f3Smrg 1337ec681f3Smrg /* allocate inflate state */ 1347ec681f3Smrg strm.zalloc = Z_NULL; 1357ec681f3Smrg strm.zfree = Z_NULL; 1367ec681f3Smrg strm.opaque = Z_NULL; 1377ec681f3Smrg strm.next_in = in_data; 1387ec681f3Smrg strm.avail_in = in_data_size; 1397ec681f3Smrg strm.next_out = out_data; 1407ec681f3Smrg strm.avail_out = out_data_size; 1417ec681f3Smrg 1427ec681f3Smrg int ret = inflateInit(&strm); 1437ec681f3Smrg if (ret != Z_OK) 1447ec681f3Smrg return false; 1457ec681f3Smrg 1467ec681f3Smrg ret = inflate(&strm, Z_NO_FLUSH); 1477ec681f3Smrg assert(ret != Z_STREAM_ERROR); /* state not clobbered */ 1487ec681f3Smrg 1497ec681f3Smrg /* Unless there was an error we should have decompressed everything in one 1507ec681f3Smrg * go as we know the uncompressed file size. 1517ec681f3Smrg */ 1527ec681f3Smrg if (ret != Z_STREAM_END) { 1537ec681f3Smrg (void)inflateEnd(&strm); 1547ec681f3Smrg return false; 1557ec681f3Smrg } 1567ec681f3Smrg assert(strm.avail_out == 0); 1577ec681f3Smrg 1587ec681f3Smrg /* clean up and return */ 1597ec681f3Smrg (void)inflateEnd(&strm); 1607ec681f3Smrg return true; 1617ec681f3Smrg#else 1627ec681f3Smrg STATIC_ASSERT(false); 1637ec681f3Smrg#endif 1647ec681f3Smrg} 1657ec681f3Smrg 1667ec681f3Smrg#endif 167