1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright © 2021 Valve Corporation 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21bf215546Sopenharmony_ci * IN THE SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci#ifdef HAVE_COMPRESSION 25bf215546Sopenharmony_ci 26bf215546Sopenharmony_ci#include <assert.h> 27bf215546Sopenharmony_ci 28bf215546Sopenharmony_ci/* Ensure that zlib uses 'const' in 'z_const' declarations. */ 29bf215546Sopenharmony_ci#ifndef ZLIB_CONST 30bf215546Sopenharmony_ci#define ZLIB_CONST 31bf215546Sopenharmony_ci#endif 32bf215546Sopenharmony_ci 33bf215546Sopenharmony_ci#ifdef HAVE_ZLIB 34bf215546Sopenharmony_ci#include "zlib.h" 35bf215546Sopenharmony_ci#endif 36bf215546Sopenharmony_ci 37bf215546Sopenharmony_ci#ifdef HAVE_ZSTD 38bf215546Sopenharmony_ci#include "zstd.h" 39bf215546Sopenharmony_ci#endif 40bf215546Sopenharmony_ci 41bf215546Sopenharmony_ci#include "util/compress.h" 42bf215546Sopenharmony_ci#include "macros.h" 43bf215546Sopenharmony_ci 44bf215546Sopenharmony_ci/* 3 is the recomended level, with 22 as the absolute maximum */ 45bf215546Sopenharmony_ci#define ZSTD_COMPRESSION_LEVEL 3 46bf215546Sopenharmony_ci 47bf215546Sopenharmony_cisize_t 48bf215546Sopenharmony_ciutil_compress_max_compressed_len(size_t in_data_size) 49bf215546Sopenharmony_ci{ 50bf215546Sopenharmony_ci#ifdef HAVE_ZSTD 51bf215546Sopenharmony_ci /* from the zstd docs (https://facebook.github.io/zstd/zstd_manual.html): 52bf215546Sopenharmony_ci * compression runs faster if `dstCapacity` >= `ZSTD_compressBound(srcSize)`. 53bf215546Sopenharmony_ci */ 54bf215546Sopenharmony_ci return ZSTD_compressBound(in_data_size); 55bf215546Sopenharmony_ci#elif defined(HAVE_ZLIB) 56bf215546Sopenharmony_ci /* From https://zlib.net/zlib_tech.html: 57bf215546Sopenharmony_ci * 58bf215546Sopenharmony_ci * "In the worst possible case, where the other block types would expand 59bf215546Sopenharmony_ci * the data, deflation falls back to stored (uncompressed) blocks. Thus 60bf215546Sopenharmony_ci * for the default settings used by deflateInit(), compress(), and 61bf215546Sopenharmony_ci * compress2(), the only expansion is an overhead of five bytes per 16 KB 62bf215546Sopenharmony_ci * block (about 0.03%), plus a one-time overhead of six bytes for the 63bf215546Sopenharmony_ci * entire stream." 64bf215546Sopenharmony_ci */ 65bf215546Sopenharmony_ci size_t num_blocks = (in_data_size + 16383) / 16384; /* round up blocks */ 66bf215546Sopenharmony_ci return in_data_size + 6 + (num_blocks * 5); 67bf215546Sopenharmony_ci#else 68bf215546Sopenharmony_ci STATIC_ASSERT(false); 69bf215546Sopenharmony_ci#endif 70bf215546Sopenharmony_ci} 71bf215546Sopenharmony_ci 72bf215546Sopenharmony_ci/* Compress data and return the size of the compressed data */ 73bf215546Sopenharmony_cisize_t 74bf215546Sopenharmony_ciutil_compress_deflate(const uint8_t *in_data, size_t in_data_size, 75bf215546Sopenharmony_ci uint8_t *out_data, size_t out_buff_size) 76bf215546Sopenharmony_ci{ 77bf215546Sopenharmony_ci#ifdef HAVE_ZSTD 78bf215546Sopenharmony_ci size_t ret = ZSTD_compress(out_data, out_buff_size, in_data, in_data_size, 79bf215546Sopenharmony_ci ZSTD_COMPRESSION_LEVEL); 80bf215546Sopenharmony_ci if (ZSTD_isError(ret)) 81bf215546Sopenharmony_ci return 0; 82bf215546Sopenharmony_ci 83bf215546Sopenharmony_ci return ret; 84bf215546Sopenharmony_ci#elif defined(HAVE_ZLIB) 85bf215546Sopenharmony_ci size_t compressed_size = 0; 86bf215546Sopenharmony_ci 87bf215546Sopenharmony_ci /* allocate deflate state */ 88bf215546Sopenharmony_ci z_stream strm; 89bf215546Sopenharmony_ci strm.zalloc = Z_NULL; 90bf215546Sopenharmony_ci strm.zfree = Z_NULL; 91bf215546Sopenharmony_ci strm.opaque = Z_NULL; 92bf215546Sopenharmony_ci strm.next_in = in_data; 93bf215546Sopenharmony_ci strm.next_out = out_data; 94bf215546Sopenharmony_ci strm.avail_in = in_data_size; 95bf215546Sopenharmony_ci strm.avail_out = out_buff_size; 96bf215546Sopenharmony_ci 97bf215546Sopenharmony_ci int ret = deflateInit(&strm, Z_BEST_COMPRESSION); 98bf215546Sopenharmony_ci if (ret != Z_OK) { 99bf215546Sopenharmony_ci (void) deflateEnd(&strm); 100bf215546Sopenharmony_ci return 0; 101bf215546Sopenharmony_ci } 102bf215546Sopenharmony_ci 103bf215546Sopenharmony_ci /* compress until end of in_data */ 104bf215546Sopenharmony_ci ret = deflate(&strm, Z_FINISH); 105bf215546Sopenharmony_ci 106bf215546Sopenharmony_ci /* stream should be complete */ 107bf215546Sopenharmony_ci assert(ret == Z_STREAM_END); 108bf215546Sopenharmony_ci if (ret == Z_STREAM_END) { 109bf215546Sopenharmony_ci compressed_size = strm.total_out; 110bf215546Sopenharmony_ci } 111bf215546Sopenharmony_ci 112bf215546Sopenharmony_ci /* clean up and return */ 113bf215546Sopenharmony_ci (void) deflateEnd(&strm); 114bf215546Sopenharmony_ci return compressed_size; 115bf215546Sopenharmony_ci#else 116bf215546Sopenharmony_ci STATIC_ASSERT(false); 117bf215546Sopenharmony_ci# endif 118bf215546Sopenharmony_ci} 119bf215546Sopenharmony_ci 120bf215546Sopenharmony_ci/** 121bf215546Sopenharmony_ci * Decompresses data, returns true if successful. 122bf215546Sopenharmony_ci */ 123bf215546Sopenharmony_cibool 124bf215546Sopenharmony_ciutil_compress_inflate(const uint8_t *in_data, size_t in_data_size, 125bf215546Sopenharmony_ci uint8_t *out_data, size_t out_data_size) 126bf215546Sopenharmony_ci{ 127bf215546Sopenharmony_ci#ifdef HAVE_ZSTD 128bf215546Sopenharmony_ci size_t ret = ZSTD_decompress(out_data, out_data_size, in_data, in_data_size); 129bf215546Sopenharmony_ci return !ZSTD_isError(ret); 130bf215546Sopenharmony_ci#elif defined(HAVE_ZLIB) 131bf215546Sopenharmony_ci z_stream strm; 132bf215546Sopenharmony_ci 133bf215546Sopenharmony_ci /* allocate inflate state */ 134bf215546Sopenharmony_ci strm.zalloc = Z_NULL; 135bf215546Sopenharmony_ci strm.zfree = Z_NULL; 136bf215546Sopenharmony_ci strm.opaque = Z_NULL; 137bf215546Sopenharmony_ci strm.next_in = in_data; 138bf215546Sopenharmony_ci strm.avail_in = in_data_size; 139bf215546Sopenharmony_ci strm.next_out = out_data; 140bf215546Sopenharmony_ci strm.avail_out = out_data_size; 141bf215546Sopenharmony_ci 142bf215546Sopenharmony_ci int ret = inflateInit(&strm); 143bf215546Sopenharmony_ci if (ret != Z_OK) 144bf215546Sopenharmony_ci return false; 145bf215546Sopenharmony_ci 146bf215546Sopenharmony_ci ret = inflate(&strm, Z_NO_FLUSH); 147bf215546Sopenharmony_ci assert(ret != Z_STREAM_ERROR); /* state not clobbered */ 148bf215546Sopenharmony_ci 149bf215546Sopenharmony_ci /* Unless there was an error we should have decompressed everything in one 150bf215546Sopenharmony_ci * go as we know the uncompressed file size. 151bf215546Sopenharmony_ci */ 152bf215546Sopenharmony_ci if (ret != Z_STREAM_END) { 153bf215546Sopenharmony_ci (void)inflateEnd(&strm); 154bf215546Sopenharmony_ci return false; 155bf215546Sopenharmony_ci } 156bf215546Sopenharmony_ci assert(strm.avail_out == 0); 157bf215546Sopenharmony_ci 158bf215546Sopenharmony_ci /* clean up and return */ 159bf215546Sopenharmony_ci (void)inflateEnd(&strm); 160bf215546Sopenharmony_ci return true; 161bf215546Sopenharmony_ci#else 162bf215546Sopenharmony_ci STATIC_ASSERT(false); 163bf215546Sopenharmony_ci#endif 164bf215546Sopenharmony_ci} 165bf215546Sopenharmony_ci 166bf215546Sopenharmony_ci#endif 167