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