summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/third_party/zlib/google/compression_utils_portable.cc
blob: 49d6bfe9ea6ed4e42788087e8f35c2bfe1e52acc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/* compression_utils_portable.cc
 *
 * Copyright 2019 The Chromium Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the Chromium source repository LICENSE file.
 */

#include "compression_utils_portable.h"

#include <stddef.h>
#include <stdlib.h>
#include <string.h>

namespace zlib_internal {

// The difference in bytes between a zlib header and a gzip header.
const size_t kGzipZlibHeaderDifferenceBytes = 16;

// Pass an integer greater than the following get a gzip header instead of a
// zlib header when calling deflateInit2() and inflateInit2().
const int kWindowBitsToGetGzipHeader = 16;

// This describes the amount of memory zlib uses to compress data. It can go
// from 1 to 9, with 8 being the default. For details, see:
// http://www.zlib.net/manual.html (search for memLevel).
const int kZlibMemoryLevel = 8;

// The expected compressed size is based on the input size factored by
// internal Zlib constants (e.g. window size, etc) plus the wrapper
// header size.
uLongf GzipExpectedCompressedSize(uLongf input_size) {
  return kGzipZlibHeaderDifferenceBytes + compressBound(input_size);
}

// The expected decompressed size is stored in the last
// 4 bytes of |input| in LE. See https://tools.ietf.org/html/rfc1952#page-5
uint32_t GetGzipUncompressedSize(const Bytef* compressed_data, size_t length) {
  uint32_t size;
  if (length < sizeof(size))
    return 0;

  memcpy(&size, &compressed_data[length - sizeof(size)], sizeof(size));
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
  return size;
#else
  return __builtin_bswap32(size);
#endif
}

// The number of window bits determines the type of wrapper to use - see
// https://cs.chromium.org/chromium/src/third_party/zlib/zlib.h?l=566
inline int ZlibStreamWrapperType(WrapperType type) {
  if (type == ZLIB)  // zlib DEFLATE stream wrapper
    return MAX_WBITS;
  if (type == GZIP)  // gzip DEFLATE stream wrapper
    return MAX_WBITS + kWindowBitsToGetGzipHeader;
  if (type == ZRAW)  // no wrapper, use raw DEFLATE
    return -MAX_WBITS;
  return 0;
}

int GzipCompressHelper(Bytef* dest,
                       uLongf* dest_length,
                       const Bytef* source,
                       uLong source_length,
                       void* (*malloc_fn)(size_t),
                       void (*free_fn)(void*)) {
  return CompressHelper(GZIP, dest, dest_length, source, source_length,
                        Z_DEFAULT_COMPRESSION, malloc_fn, free_fn);
}

// This code is taken almost verbatim from third_party/zlib/compress.c. The only
// difference is deflateInit2() is called which allows different window bits to
// be set. > 16 causes a gzip header to be emitted rather than a zlib header,
// and negative causes no header to emitted.
//
// Compression level can be a number from 1-9, with 1 being the fastest, 9 being
// the best compression. The default, which the GZIP helper uses, is 6.
int CompressHelper(WrapperType wrapper_type,
                   Bytef* dest,
                   uLongf* dest_length,
                   const Bytef* source,
                   uLong source_length,
                   int compression_level,
                   void* (*malloc_fn)(size_t),
                   void (*free_fn)(void*)) {
  if (compression_level < 0 || compression_level > 9) {
    compression_level = Z_DEFAULT_COMPRESSION;
  }

  z_stream stream;

  // FIXME(cavalcantii): z_const is not defined as 'const'.
  stream.next_in = static_cast<z_const Bytef*>(const_cast<Bytef*>(source));
  stream.avail_in = static_cast<uInt>(source_length);
  stream.next_out = dest;
  stream.avail_out = static_cast<uInt>(*dest_length);
  if (static_cast<uLong>(stream.avail_out) != *dest_length)
    return Z_BUF_ERROR;

  // Cannot convert capturing lambdas to function pointers directly, hence the
  // structure.
  struct MallocFreeFunctions {
    void* (*malloc_fn)(size_t);
    void (*free_fn)(void*);
  } malloc_free = {malloc_fn, free_fn};

  if (malloc_fn) {
    if (!free_fn)
      return Z_BUF_ERROR;

    auto zalloc = [](void* opaque, uInt items, uInt size) {
      return reinterpret_cast<MallocFreeFunctions*>(opaque)->malloc_fn(items *
                                                                       size);
    };
    auto zfree = [](void* opaque, void* address) {
      return reinterpret_cast<MallocFreeFunctions*>(opaque)->free_fn(address);
    };

    stream.zalloc = static_cast<alloc_func>(zalloc);
    stream.zfree = static_cast<free_func>(zfree);
    stream.opaque = static_cast<voidpf>(&malloc_free);
  } else {
    stream.zalloc = static_cast<alloc_func>(0);
    stream.zfree = static_cast<free_func>(0);
    stream.opaque = static_cast<voidpf>(0);
  }

  int err = deflateInit2(&stream, compression_level, Z_DEFLATED,
                         ZlibStreamWrapperType(wrapper_type), kZlibMemoryLevel,
                         Z_DEFAULT_STRATEGY);
  if (err != Z_OK)
    return err;

  // This has to exist outside of the if statement to prevent it going off the
  // stack before deflate(), which will use this object.
  gz_header gzip_header;
  if (wrapper_type == GZIP) {
    memset(&gzip_header, 0, sizeof(gzip_header));
    err = deflateSetHeader(&stream, &gzip_header);
    if (err != Z_OK)
      return err;
  }

  err = deflate(&stream, Z_FINISH);
  if (err != Z_STREAM_END) {
    deflateEnd(&stream);
    return err == Z_OK ? Z_BUF_ERROR : err;
  }
  *dest_length = stream.total_out;

  err = deflateEnd(&stream);
  return err;
}

int GzipUncompressHelper(Bytef* dest,
                         uLongf* dest_length,
                         const Bytef* source,
                         uLong source_length) {
  return UncompressHelper(GZIP, dest, dest_length, source, source_length);
}

// This code is taken almost verbatim from third_party/zlib/uncompr.c. The only
// difference is inflateInit2() is called which allows different window bits to
// be set. > 16 causes a gzip header to be emitted rather than a zlib header,
// and negative causes no header to emitted.
int UncompressHelper(WrapperType wrapper_type,
                     Bytef* dest,
                     uLongf* dest_length,
                     const Bytef* source,
                     uLong source_length) {
  z_stream stream;

  // FIXME(cavalcantii): z_const is not defined as 'const'.
  stream.next_in = static_cast<z_const Bytef*>(const_cast<Bytef*>(source));
  stream.avail_in = static_cast<uInt>(source_length);
  if (static_cast<uLong>(stream.avail_in) != source_length)
    return Z_BUF_ERROR;

  stream.next_out = dest;
  stream.avail_out = static_cast<uInt>(*dest_length);
  if (static_cast<uLong>(stream.avail_out) != *dest_length)
    return Z_BUF_ERROR;

  stream.zalloc = static_cast<alloc_func>(0);
  stream.zfree = static_cast<free_func>(0);

  int err = inflateInit2(&stream, ZlibStreamWrapperType(wrapper_type));
  if (err != Z_OK)
    return err;

  err = inflate(&stream, Z_FINISH);
  if (err != Z_STREAM_END) {
    inflateEnd(&stream);
    if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
      return Z_DATA_ERROR;
    return err;
  }
  *dest_length = stream.total_out;

  err = inflateEnd(&stream);
  return err;
}

}  // namespace zlib_internal