1/* compression_utils_portable.cc
2 *
3 * Copyright 2019 The Chromium Authors
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the Chromium source repository LICENSE file.
6 */
7
8#include "compression_utils_portable.h"
9
10#include <stddef.h>
11#include <stdlib.h>
12#include <string.h>
13
14namespace zlib_internal {
15
16// The difference in bytes between a zlib header and a gzip header.
17const size_t kGzipZlibHeaderDifferenceBytes = 16;
18
19// Pass an integer greater than the following get a gzip header instead of a
20// zlib header when calling deflateInit2() and inflateInit2().
21const int kWindowBitsToGetGzipHeader = 16;
22
23// This describes the amount of memory zlib uses to compress data. It can go
24// from 1 to 9, with 8 being the default. For details, see:
25// http://www.zlib.net/manual.html (search for memLevel).
26const int kZlibMemoryLevel = 8;
27
28// The expected compressed size is based on the input size factored by
29// internal Zlib constants (e.g. window size, etc) plus the wrapper
30// header size.
31uLongf GzipExpectedCompressedSize(uLongf input_size) {
32  return kGzipZlibHeaderDifferenceBytes + compressBound(input_size);
33}
34
35// The expected decompressed size is stored in the last
36// 4 bytes of |input| in LE. See https://tools.ietf.org/html/rfc1952#page-5
37uint32_t GetGzipUncompressedSize(const Bytef* compressed_data, size_t length) {
38  uint32_t size;
39  if (length < sizeof(size))
40    return 0;
41
42  memcpy(&size, &compressed_data[length - sizeof(size)], sizeof(size));
43#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
44  return size;
45#else
46  return __builtin_bswap32(size);
47#endif
48}
49
50// The number of window bits determines the type of wrapper to use - see
51// https://cs.chromium.org/chromium/src/third_party/zlib/zlib.h?l=566
52inline int ZlibStreamWrapperType(WrapperType type) {
53  if (type == ZLIB)  // zlib DEFLATE stream wrapper
54    return MAX_WBITS;
55  if (type == GZIP)  // gzip DEFLATE stream wrapper
56    return MAX_WBITS + kWindowBitsToGetGzipHeader;
57  if (type == ZRAW)  // no wrapper, use raw DEFLATE
58    return -MAX_WBITS;
59  return 0;
60}
61
62int GzipCompressHelper(Bytef* dest,
63                       uLongf* dest_length,
64                       const Bytef* source,
65                       uLong source_length,
66                       void* (*malloc_fn)(size_t),
67                       void (*free_fn)(void*)) {
68  return CompressHelper(GZIP, dest, dest_length, source, source_length,
69                        Z_DEFAULT_COMPRESSION, malloc_fn, free_fn);
70}
71
72// This code is taken almost verbatim from third_party/zlib/compress.c. The only
73// difference is deflateInit2() is called which allows different window bits to
74// be set. > 16 causes a gzip header to be emitted rather than a zlib header,
75// and negative causes no header to emitted.
76//
77// Compression level can be a number from 1-9, with 1 being the fastest, 9 being
78// the best compression. The default, which the GZIP helper uses, is 6.
79int CompressHelper(WrapperType wrapper_type,
80                   Bytef* dest,
81                   uLongf* dest_length,
82                   const Bytef* source,
83                   uLong source_length,
84                   int compression_level,
85                   void* (*malloc_fn)(size_t),
86                   void (*free_fn)(void*)) {
87  if (compression_level < 0 || compression_level > 9) {
88    compression_level = Z_DEFAULT_COMPRESSION;
89  }
90
91  z_stream stream;
92
93  // FIXME(cavalcantii): z_const is not defined as 'const'.
94  stream.next_in = static_cast<z_const Bytef*>(const_cast<Bytef*>(source));
95  stream.avail_in = static_cast<uInt>(source_length);
96  stream.next_out = dest;
97  stream.avail_out = static_cast<uInt>(*dest_length);
98  if (static_cast<uLong>(stream.avail_out) != *dest_length)
99    return Z_BUF_ERROR;
100
101  // Cannot convert capturing lambdas to function pointers directly, hence the
102  // structure.
103  struct MallocFreeFunctions {
104    void* (*malloc_fn)(size_t);
105    void (*free_fn)(void*);
106  } malloc_free = {malloc_fn, free_fn};
107
108  if (malloc_fn) {
109    if (!free_fn)
110      return Z_BUF_ERROR;
111
112    auto zalloc = [](void* opaque, uInt items, uInt size) {
113      return reinterpret_cast<MallocFreeFunctions*>(opaque)->malloc_fn(items *
114                                                                       size);
115    };
116    auto zfree = [](void* opaque, void* address) {
117      return reinterpret_cast<MallocFreeFunctions*>(opaque)->free_fn(address);
118    };
119
120    stream.zalloc = static_cast<alloc_func>(zalloc);
121    stream.zfree = static_cast<free_func>(zfree);
122    stream.opaque = static_cast<voidpf>(&malloc_free);
123  } else {
124    stream.zalloc = static_cast<alloc_func>(0);
125    stream.zfree = static_cast<free_func>(0);
126    stream.opaque = static_cast<voidpf>(0);
127  }
128
129  int err = deflateInit2(&stream, compression_level, Z_DEFLATED,
130                         ZlibStreamWrapperType(wrapper_type), kZlibMemoryLevel,
131                         Z_DEFAULT_STRATEGY);
132  if (err != Z_OK)
133    return err;
134
135  // This has to exist outside of the if statement to prevent it going off the
136  // stack before deflate(), which will use this object.
137  gz_header gzip_header;
138  if (wrapper_type == GZIP) {
139    memset(&gzip_header, 0, sizeof(gzip_header));
140    err = deflateSetHeader(&stream, &gzip_header);
141    if (err != Z_OK)
142      return err;
143  }
144
145  err = deflate(&stream, Z_FINISH);
146  if (err != Z_STREAM_END) {
147    deflateEnd(&stream);
148    return err == Z_OK ? Z_BUF_ERROR : err;
149  }
150  *dest_length = stream.total_out;
151
152  err = deflateEnd(&stream);
153  return err;
154}
155
156int GzipUncompressHelper(Bytef* dest,
157                         uLongf* dest_length,
158                         const Bytef* source,
159                         uLong source_length) {
160  return UncompressHelper(GZIP, dest, dest_length, source, source_length);
161}
162
163// This code is taken almost verbatim from third_party/zlib/uncompr.c. The only
164// difference is inflateInit2() is called which allows different window bits to
165// be set. > 16 causes a gzip header to be emitted rather than a zlib header,
166// and negative causes no header to emitted.
167int UncompressHelper(WrapperType wrapper_type,
168                     Bytef* dest,
169                     uLongf* dest_length,
170                     const Bytef* source,
171                     uLong source_length) {
172  z_stream stream;
173
174  // FIXME(cavalcantii): z_const is not defined as 'const'.
175  stream.next_in = static_cast<z_const Bytef*>(const_cast<Bytef*>(source));
176  stream.avail_in = static_cast<uInt>(source_length);
177  if (static_cast<uLong>(stream.avail_in) != source_length)
178    return Z_BUF_ERROR;
179
180  stream.next_out = dest;
181  stream.avail_out = static_cast<uInt>(*dest_length);
182  if (static_cast<uLong>(stream.avail_out) != *dest_length)
183    return Z_BUF_ERROR;
184
185  stream.zalloc = static_cast<alloc_func>(0);
186  stream.zfree = static_cast<free_func>(0);
187
188  int err = inflateInit2(&stream, ZlibStreamWrapperType(wrapper_type));
189  if (err != Z_OK)
190    return err;
191
192  err = inflate(&stream, Z_FINISH);
193  if (err != Z_STREAM_END) {
194    inflateEnd(&stream);
195    if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
196      return Z_DATA_ERROR;
197    return err;
198  }
199  *dest_length = stream.total_out;
200
201  err = inflateEnd(&stream);
202  return err;
203}
204
205}  // namespace zlib_internal
206