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
|
#include <mbgl/util/compression.hpp>
#if defined(__QT__) && defined(_WINDOWS)
#include <QtZlib/zlib.h>
#else
#include <zlib.h>
#endif
#include <cstdio>
#include <cstring>
#include <stdexcept>
// Check zlib library version.
const static bool zlibVersionCheck __attribute__((unused)) = []() {
const char *const version = zlibVersion();
if (version[0] != ZLIB_VERSION[0]) {
char message[96];
snprintf(message, 96, "zlib version mismatch: headers report %s, but library reports %s",
ZLIB_VERSION, version);
throw std::runtime_error(message);
}
return true;
}();
namespace mbgl {
namespace util {
// Needed when using a zlib compiled with -DZ_PREFIX
// because it will mess with this function name and
// cause a link error.
#undef compress
std::string compress(const std::string &raw) {
z_stream deflate_stream;
memset(&deflate_stream, 0, sizeof(deflate_stream));
// TODO: reuse z_streams
if (deflateInit(&deflate_stream, Z_DEFAULT_COMPRESSION) != Z_OK) {
throw std::runtime_error("failed to initialize deflate");
}
deflate_stream.next_in = (Bytef *)raw.data();
deflate_stream.avail_in = uInt(raw.size());
std::string result;
char out[16384];
int code;
do {
deflate_stream.next_out = reinterpret_cast<Bytef *>(out);
deflate_stream.avail_out = sizeof(out);
code = deflate(&deflate_stream, Z_FINISH);
if (result.size() < deflate_stream.total_out) {
// append the block to the output string
result.append(out, deflate_stream.total_out - result.size());
}
} while (code == Z_OK);
deflateEnd(&deflate_stream);
if (code != Z_STREAM_END) {
throw std::runtime_error(deflate_stream.msg);
}
return result;
}
std::string decompress(const std::string &raw) {
z_stream inflate_stream;
memset(&inflate_stream, 0, sizeof(inflate_stream));
// TODO: reuse z_streams
// MAX_WBITS + allows decoding gzip in addition to zlib
if (inflateInit2(&inflate_stream, MAX_WBITS + 32) != Z_OK) {
throw std::runtime_error("failed to initialize inflate");
}
inflate_stream.next_in = (Bytef *)raw.data();
inflate_stream.avail_in = uInt(raw.size());
std::string result;
char out[15384];
int code;
do {
inflate_stream.next_out = reinterpret_cast<Bytef *>(out);
inflate_stream.avail_out = sizeof(out);
code = inflate(&inflate_stream, 0);
// result.append(out, sizeof(out) - inflate_stream.avail_out);
if (result.size() < inflate_stream.total_out) {
result.append(out, inflate_stream.total_out - result.size());
}
} while (code == Z_OK);
inflateEnd(&inflate_stream);
if (code != Z_STREAM_END) {
throw std::runtime_error(inflate_stream.msg ? inflate_stream.msg : "decompression error");
}
return result;
}
bool isCompressible(const std::string& raw) {
// WebP
if (raw.size() >= 12 && static_cast<uint8_t>(raw[0]) == 'R' &&
static_cast<uint8_t>(raw[1]) == 'I' && static_cast<uint8_t>(raw[2]) == 'F' &&
static_cast<uint8_t>(raw[3]) == 'F' && static_cast<uint8_t>(raw[8]) == 'W' &&
static_cast<uint8_t>(raw[9]) == 'E' && static_cast<uint8_t>(raw[10]) == 'B' &&
static_cast<uint8_t>(raw[11]) == 'P') {
// Note: the WebP container format allows uncompressed data as well, but we just assume that
// all WebP files are already compressed.
return false;
}
// PNG
if (raw.size() >= 8 && static_cast<uint8_t>(raw[0]) == 0x89 &&
static_cast<uint8_t>(raw[1]) == 'P' && static_cast<uint8_t>(raw[2]) == 'N' &&
static_cast<uint8_t>(raw[3]) == 'G' && static_cast<uint8_t>(raw[4]) == '\r' &&
static_cast<uint8_t>(raw[5]) == '\n' && static_cast<uint8_t>(raw[6]) == 0x1a &&
static_cast<uint8_t>(raw[7]) == '\n') {
// Note: this assumes the PNG file itself is compressed. However, it is possible to create
// PNG files with uncompressed data in it (zlib compression 0), but they are exceedingly
// rare, so we don't care about them.
return false;
}
// JPEG
if (raw.size() >= 3 && static_cast<uint8_t>(raw[0]) == 0xff &&
static_cast<uint8_t>(raw[1]) == 0xd8 && static_cast<uint8_t>(raw[2]) == 0xff) {
return false;
}
return true;
}
} // namespace util
} // namespace mbgl
|