2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/util/brotli.h"
19 #include "hphp/util/alloc.h"
20 #include "hphp/util/exception.h"
21 #include "hphp/util/logger.h"
23 #include <brotli/encode.h>
26 ///////////////////////////////////////////////////////////////////////////////
28 bool g_brotliUseLocalArena
= false;
31 void *brotlialloc(void * /*opaque*/, size_t size
) {
32 if (g_brotliUseLocalArena
) {
33 return local_malloc(size
);
39 void brotlifree(void * /*opaque*/, void *address
) {
40 if (g_brotliUseLocalArena
) {
48 BrotliCompressor::BrotliCompressor(BrotliEncoderMode mode
, uint32_t quality
, uint32_t lgWin
) {
49 if (quality
< BROTLI_MIN_QUALITY
|| quality
> BROTLI_MAX_QUALITY
) {
50 Logger::Info("brotli compression quality (%d) must be within %d..%d, clamping...", quality
, BROTLI_MIN_QUALITY
, BROTLI_MAX_QUALITY
);
51 if (quality
< BROTLI_MIN_QUALITY
) {
52 quality
= BROTLI_MIN_QUALITY
;
54 quality
= BROTLI_MAX_QUALITY
;
58 if (lgWin
< BROTLI_MIN_WINDOW_BITS
|| lgWin
> BROTLI_MAX_WINDOW_BITS
) {
59 Logger::Info("brotli window size (%d) must be within %d..%d, clamping...", lgWin
, BROTLI_MIN_WINDOW_BITS
, BROTLI_MAX_WINDOW_BITS
);
60 if (lgWin
< BROTLI_MIN_WINDOW_BITS
) {
61 lgWin
= BROTLI_MIN_WINDOW_BITS
;
63 lgWin
= BROTLI_MAX_WINDOW_BITS
;
67 m_encState
.reset(BrotliEncoderCreateInstance(brotlialloc
, brotlifree
, nullptr));
69 throw Exception("Failed to create brotli encoder instance");
72 if (BrotliEncoderSetParameter(m_encState
.get(), BROTLI_PARAM_MODE
, mode
) != BROTLI_TRUE
) {
73 throw Exception("Failed to set brotli mode(%d)", static_cast<uint32_t>(mode
));
76 if (BrotliEncoderSetParameter(m_encState
.get(), BROTLI_PARAM_QUALITY
, quality
) != BROTLI_TRUE
) {
77 throw Exception("Failed to set quality(%d)", quality
);
80 if (BrotliEncoderSetParameter(m_encState
.get(), BROTLI_PARAM_LGWIN
, lgWin
) != BROTLI_TRUE
) {
81 throw Exception("Failed to set lgWin(%d)", lgWin
);
85 StringHolder
BrotliCompressor::compress(const void* data
,
88 // Brotli does not have a utility to compute max size of the buffer
89 // for the stream-based API. Below link discusses some numbers
90 // and formula where 16MB block would use only 6 extra bytes.
91 // For all practical usage we should be fine with 20 bytes.
92 // https://github.com/google/brotli/issues/274
93 // We should also allow 6 extra bytes for an empty meta-block at
94 // the end of each chunk to force "flush".
95 size_t availableBytes
= len
+ 30;
97 StringHolder available
;
99 if (g_brotliUseLocalArena
) {
100 availablePtr
= local_malloc(availableBytes
);
101 available
= StringHolder(static_cast<char*>(availablePtr
),
103 FreeType::LocalFree
);
105 availablePtr
= malloc(availableBytes
);
106 available
= StringHolder(static_cast<char*>(availablePtr
),
111 auto buf
= static_cast<uint8_t*>(availablePtr
);
112 size_t inLength
= len
;
113 auto inBuf
= static_cast<const uint8_t *>(data
);
114 size_t remainingOut
= availableBytes
;
116 // This ought to complete in one call
117 if (BrotliEncoderCompressStream(
119 last
? BROTLI_OPERATION_FINISH
: BROTLI_OPERATION_FLUSH
,
124 nullptr) != BROTLI_TRUE
) {
125 Logger::Error("Compression encountered an error");
129 // This means the encoder didn't successfully encode + flush all data.
131 BrotliEncoderHasMoreOutput(m_encState
.get())) {
132 Logger::Error("Compression didn't flush all data");
136 len
= availableBytes
- remainingOut
;
137 available
.shrinkTo(len
);
142 ///////////////////////////////////////////////////////////////////////////////