1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/metrics/compression_utils.h"
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "base/sys_byteorder.h"
12 #include "third_party/zlib/zlib.h"
16 // The difference in bytes between a zlib header and a gzip header.
17 const size_t kGzipZlibHeaderDifferenceBytes
= 16;
19 // Pass an integer greater than the following get a gzip header instead of a
20 // zlib header when calling deflateInit2() and inflateInit2().
21 const int kWindowBitsToGetGzipHeader
= 16;
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).
26 const int kZlibMemoryLevel
= 8;
28 // This code is taken almost verbatim from third_party/zlib/compress.c. The only
29 // difference is deflateInit2() is called which sets the window bits to be > 16.
30 // That causes a gzip header to be emitted rather than a zlib header.
31 int GzipCompressHelper(Bytef
* dest
,
34 uLong source_length
) {
37 stream
.next_in
= bit_cast
<Bytef
*>(source
);
38 stream
.avail_in
= static_cast<uInt
>(source_length
);
39 stream
.next_out
= dest
;
40 stream
.avail_out
= static_cast<uInt
>(*dest_length
);
41 if (static_cast<uLong
>(stream
.avail_out
) != *dest_length
)
44 stream
.zalloc
= static_cast<alloc_func
>(0);
45 stream
.zfree
= static_cast<free_func
>(0);
46 stream
.opaque
= static_cast<voidpf
>(0);
48 gz_header gzip_header
;
49 memset(&gzip_header
, 0, sizeof(gzip_header
));
50 int err
= deflateInit2(&stream
,
51 Z_DEFAULT_COMPRESSION
,
53 MAX_WBITS
+ kWindowBitsToGetGzipHeader
,
59 err
= deflateSetHeader(&stream
, &gzip_header
);
63 err
= deflate(&stream
, Z_FINISH
);
64 if (err
!= Z_STREAM_END
) {
66 return err
== Z_OK
? Z_BUF_ERROR
: err
;
68 *dest_length
= stream
.total_out
;
70 err
= deflateEnd(&stream
);
74 // This code is taken almost verbatim from third_party/zlib/uncompr.c. The only
75 // difference is inflateInit2() is called which sets the window bits to be > 16.
76 // That causes a gzip header to be parsed rather than a zlib header.
77 int GzipUncompressHelper(Bytef
* dest
,
80 uLong source_length
) {
83 stream
.next_in
= bit_cast
<Bytef
*>(source
);
84 stream
.avail_in
= static_cast<uInt
>(source_length
);
85 if (static_cast<uLong
>(stream
.avail_in
) != source_length
)
88 stream
.next_out
= dest
;
89 stream
.avail_out
= static_cast<uInt
>(*dest_length
);
90 if (static_cast<uLong
>(stream
.avail_out
) != *dest_length
)
93 stream
.zalloc
= static_cast<alloc_func
>(0);
94 stream
.zfree
= static_cast<free_func
>(0);
96 int err
= inflateInit2(&stream
, MAX_WBITS
+ kWindowBitsToGetGzipHeader
);
100 err
= inflate(&stream
, Z_FINISH
);
101 if (err
!= Z_STREAM_END
) {
103 if (err
== Z_NEED_DICT
|| (err
== Z_BUF_ERROR
&& stream
.avail_in
== 0))
107 *dest_length
= stream
.total_out
;
109 err
= inflateEnd(&stream
);
113 // Returns the uncompressed size from GZIP-compressed |compressed_data|.
114 uint32
GetUncompressedSize(const std::string
& compressed_data
) {
115 // The uncompressed size is stored in the last 4 bytes of |input| in LE.
117 if (compressed_data
.length() < sizeof(size
))
119 memcpy(&size
, &compressed_data
[compressed_data
.length() - sizeof(size
)],
121 return base::ByteSwapToLE32(size
);
128 bool GzipCompress(const std::string
& input
, std::string
* output
) {
129 const uLongf input_size
= static_cast<uLongf
>(input
.size());
130 std::vector
<Bytef
> compressed_data(kGzipZlibHeaderDifferenceBytes
+
131 compressBound(input_size
));
133 uLongf compressed_size
= static_cast<uLongf
>(compressed_data
.size());
134 if (GzipCompressHelper(&compressed_data
.front(),
136 bit_cast
<const Bytef
*>(input
.data()),
137 input_size
) != Z_OK
) {
141 compressed_data
.resize(compressed_size
);
142 output
->assign(compressed_data
.begin(), compressed_data
.end());
143 DCHECK_EQ(input
.size(), GetUncompressedSize(*output
));
147 bool GzipUncompress(const std::string
& input
, std::string
* output
) {
148 output
->resize(GetUncompressedSize(input
));
149 uLongf uncompressed_size
= static_cast<uLongf
>(output
->length());
150 return GzipUncompressHelper(bit_cast
<Bytef
*>(output
->data()),
152 bit_cast
<const Bytef
*>(input
.data()),
153 static_cast<uLongf
>(input
.length())) == Z_OK
;
156 } // namespace metrics