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