2013-10-23 11:06:13 +00:00
|
|
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
//
|
|
|
|
// Implementation of Brotli compressor.
|
|
|
|
|
|
|
|
#include "./encode.h"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <limits>
|
|
|
|
|
|
|
|
#include "./backward_references.h"
|
|
|
|
#include "./bit_cost.h"
|
|
|
|
#include "./block_splitter.h"
|
2014-10-15 12:01:36 +00:00
|
|
|
#include "./brotli_bit_stream.h"
|
2013-10-23 11:06:13 +00:00
|
|
|
#include "./cluster.h"
|
|
|
|
#include "./context.h"
|
2014-02-17 13:25:36 +00:00
|
|
|
#include "./transform.h"
|
2013-10-23 11:06:13 +00:00
|
|
|
#include "./entropy_encode.h"
|
|
|
|
#include "./fast_log.h"
|
2013-11-15 18:02:17 +00:00
|
|
|
#include "./hash.h"
|
2013-10-23 11:06:13 +00:00
|
|
|
#include "./histogram.h"
|
2013-11-15 18:02:17 +00:00
|
|
|
#include "./literal_cost.h"
|
2013-10-23 11:06:13 +00:00
|
|
|
#include "./prefix.h"
|
|
|
|
#include "./write_bits.h"
|
|
|
|
|
|
|
|
namespace brotli {
|
|
|
|
|
2013-11-19 22:32:56 +00:00
|
|
|
static const int kWindowBits = 22;
|
|
|
|
// To make decoding faster, we allow the decoder to write 16 bytes ahead in
|
|
|
|
// its ringbuffer, therefore the encoder has to decrease max distance by this
|
|
|
|
// amount.
|
|
|
|
static const int kDecoderRingBufferWriteAheadSlack = 16;
|
|
|
|
static const int kMaxBackwardDistance =
|
|
|
|
(1 << kWindowBits) - kDecoderRingBufferWriteAheadSlack;
|
|
|
|
|
|
|
|
static const int kMetaBlockSizeBits = 21;
|
|
|
|
static const int kRingBufferBits = 23;
|
|
|
|
static const int kRingBufferMask = (1 << kRingBufferBits) - 1;
|
|
|
|
|
2013-10-23 11:06:13 +00:00
|
|
|
template<int kSize>
|
|
|
|
double Entropy(const std::vector<Histogram<kSize> >& histograms) {
|
|
|
|
double retval = 0;
|
|
|
|
for (int i = 0; i < histograms.size(); ++i) {
|
|
|
|
retval += histograms[i].EntropyBitCost();
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2013-11-15 18:02:17 +00:00
|
|
|
template<int kSize>
|
|
|
|
double TotalBitCost(const std::vector<Histogram<kSize> >& histograms) {
|
|
|
|
double retval = 0;
|
|
|
|
for (int i = 0; i < histograms.size(); ++i) {
|
|
|
|
retval += PopulationCost(histograms[i]);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2014-02-14 14:04:23 +00:00
|
|
|
int ParseAsUTF8(int* symbol, const uint8_t* input, int size) {
|
|
|
|
// ASCII
|
|
|
|
if ((input[0] & 0x80) == 0) {
|
|
|
|
*symbol = input[0];
|
|
|
|
if (*symbol > 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 2-byte UTF8
|
|
|
|
if (size > 1 &&
|
|
|
|
(input[0] & 0xe0) == 0xc0 &&
|
|
|
|
(input[1] & 0xc0) == 0x80) {
|
|
|
|
*symbol = (((input[0] & 0x1f) << 6) |
|
|
|
|
(input[1] & 0x3f));
|
|
|
|
if (*symbol > 0x7f) {
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 3-byte UFT8
|
|
|
|
if (size > 2 &&
|
|
|
|
(input[0] & 0xf0) == 0xe0 &&
|
|
|
|
(input[1] & 0xc0) == 0x80 &&
|
|
|
|
(input[2] & 0xc0) == 0x80) {
|
|
|
|
*symbol = (((input[0] & 0x0f) << 12) |
|
|
|
|
((input[1] & 0x3f) << 6) |
|
|
|
|
(input[2] & 0x3f));
|
|
|
|
if (*symbol > 0x7ff) {
|
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// 4-byte UFT8
|
|
|
|
if (size > 3 &&
|
|
|
|
(input[0] & 0xf8) == 0xf0 &&
|
|
|
|
(input[1] & 0xc0) == 0x80 &&
|
|
|
|
(input[2] & 0xc0) == 0x80 &&
|
|
|
|
(input[3] & 0xc0) == 0x80) {
|
|
|
|
*symbol = (((input[0] & 0x07) << 18) |
|
|
|
|
((input[1] & 0x3f) << 12) |
|
|
|
|
((input[2] & 0x3f) << 6) |
|
|
|
|
(input[3] & 0x3f));
|
|
|
|
if (*symbol > 0xffff && *symbol <= 0x10ffff) {
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Not UTF8, emit a special symbol above the UTF8-code space
|
|
|
|
*symbol = 0x110000 | input[0];
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns true if at least min_fraction of the data is UTF8-encoded.
|
|
|
|
bool IsMostlyUTF8(const uint8_t* data, size_t length, double min_fraction) {
|
|
|
|
size_t size_utf8 = 0;
|
|
|
|
size_t pos = 0;
|
|
|
|
while (pos < length) {
|
|
|
|
int symbol;
|
|
|
|
int bytes_read = ParseAsUTF8(&symbol, data + pos, length - pos);
|
|
|
|
pos += bytes_read;
|
|
|
|
if (symbol < 0x110000) size_utf8 += bytes_read;
|
|
|
|
}
|
|
|
|
return size_utf8 > min_fraction * length;
|
|
|
|
}
|
|
|
|
|
2013-11-15 18:02:17 +00:00
|
|
|
void EncodeMetaBlockLength(size_t meta_block_size,
|
2013-12-12 12:18:04 +00:00
|
|
|
bool is_last,
|
|
|
|
bool is_uncompressed,
|
2013-10-23 11:06:13 +00:00
|
|
|
int* storage_ix, uint8_t* storage) {
|
2013-12-12 12:18:04 +00:00
|
|
|
WriteBits(1, is_last, storage_ix, storage);
|
|
|
|
if (is_last) {
|
|
|
|
if (meta_block_size == 0) {
|
|
|
|
WriteBits(1, 1, storage_ix, storage);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
WriteBits(1, 0, storage_ix, storage);
|
|
|
|
}
|
|
|
|
--meta_block_size;
|
2013-11-15 18:02:17 +00:00
|
|
|
int num_bits = Log2Floor(meta_block_size) + 1;
|
2013-11-28 16:37:13 +00:00
|
|
|
if (num_bits < 16) {
|
|
|
|
num_bits = 16;
|
|
|
|
}
|
|
|
|
WriteBits(2, (num_bits - 13) >> 2, storage_ix, storage);
|
2013-11-15 18:02:17 +00:00
|
|
|
while (num_bits > 0) {
|
|
|
|
WriteBits(4, meta_block_size & 0xf, storage_ix, storage);
|
|
|
|
meta_block_size >>= 4;
|
|
|
|
num_bits -= 4;
|
2013-10-23 11:06:13 +00:00
|
|
|
}
|
2013-12-12 12:18:04 +00:00
|
|
|
if (!is_last) {
|
|
|
|
WriteBits(1, is_uncompressed, storage_ix, storage);
|
|
|
|
}
|
2013-10-23 11:06:13 +00:00
|
|
|
}
|
|
|
|
|
2014-02-14 14:04:23 +00:00
|
|
|
template<int kSize>
|
2014-03-20 13:32:35 +00:00
|
|
|
void BuildAndStoreEntropyCode(const Histogram<kSize>& histogram,
|
|
|
|
const int tree_limit,
|
|
|
|
const int alphabet_size,
|
|
|
|
EntropyCode<kSize>* code,
|
|
|
|
int* storage_ix, uint8_t* storage) {
|
|
|
|
memset(code->depth_, 0, sizeof(code->depth_));
|
|
|
|
memset(code->bits_, 0, sizeof(code->bits_));
|
2014-10-15 12:01:36 +00:00
|
|
|
BuildAndStoreHuffmanTree(histogram.data_, alphabet_size, 9,
|
|
|
|
code->depth_, code->bits_, storage_ix, storage);
|
2014-02-14 14:04:23 +00:00
|
|
|
}
|
|
|
|
|
2013-10-23 11:06:13 +00:00
|
|
|
template<int kSize>
|
2014-03-20 13:32:35 +00:00
|
|
|
void BuildAndStoreEntropyCodes(
|
|
|
|
const std::vector<Histogram<kSize> >& histograms,
|
|
|
|
int alphabet_size,
|
|
|
|
std::vector<EntropyCode<kSize> >* entropy_codes,
|
|
|
|
int* storage_ix, uint8_t* storage) {
|
|
|
|
entropy_codes->resize(histograms.size());
|
|
|
|
for (int i = 0; i < histograms.size(); ++i) {
|
|
|
|
BuildAndStoreEntropyCode(histograms[i], 15, alphabet_size,
|
|
|
|
&(*entropy_codes)[i],
|
|
|
|
storage_ix, storage);
|
2013-10-23 11:06:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EncodeCommand(const Command& cmd,
|
|
|
|
const EntropyCodeCommand& entropy,
|
|
|
|
int* storage_ix, uint8_t* storage) {
|
|
|
|
int code = cmd.command_prefix_;
|
2014-03-20 13:32:35 +00:00
|
|
|
WriteBits(entropy.depth_[code], entropy.bits_[code], storage_ix, storage);
|
2013-10-23 11:06:13 +00:00
|
|
|
if (code >= 128) {
|
|
|
|
code -= 128;
|
|
|
|
}
|
|
|
|
int insert_extra_bits = InsertLengthExtraBits(code);
|
|
|
|
uint64_t insert_extra_bits_val =
|
|
|
|
cmd.insert_length_ - InsertLengthOffset(code);
|
|
|
|
int copy_extra_bits = CopyLengthExtraBits(code);
|
2013-11-19 22:32:56 +00:00
|
|
|
uint64_t copy_extra_bits_val = cmd.copy_length_code_ - CopyLengthOffset(code);
|
2013-10-23 11:06:13 +00:00
|
|
|
if (insert_extra_bits > 0) {
|
|
|
|
WriteBits(insert_extra_bits, insert_extra_bits_val, storage_ix, storage);
|
|
|
|
}
|
|
|
|
if (copy_extra_bits > 0) {
|
|
|
|
WriteBits(copy_extra_bits, copy_extra_bits_val, storage_ix, storage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EncodeCopyDistance(const Command& cmd, const EntropyCodeDistance& entropy,
|
|
|
|
int* storage_ix, uint8_t* storage) {
|
|
|
|
int code = cmd.distance_prefix_;
|
|
|
|
int extra_bits = cmd.distance_extra_bits_;
|
|
|
|
uint64_t extra_bits_val = cmd.distance_extra_bits_value_;
|
2014-03-20 13:32:35 +00:00
|
|
|
WriteBits(entropy.depth_[code], entropy.bits_[code], storage_ix, storage);
|
2013-10-23 11:06:13 +00:00
|
|
|
if (extra_bits > 0) {
|
|
|
|
WriteBits(extra_bits, extra_bits_val, storage_ix, storage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-15 18:02:17 +00:00
|
|
|
void ComputeDistanceShortCodes(std::vector<Command>* cmds,
|
2014-03-20 13:32:35 +00:00
|
|
|
size_t pos,
|
|
|
|
const size_t max_backward,
|
2013-11-15 18:02:17 +00:00
|
|
|
int* dist_ringbuffer,
|
|
|
|
size_t* ringbuffer_idx) {
|
2013-10-23 11:06:13 +00:00
|
|
|
static const int kIndexOffset[16] = {
|
|
|
|
3, 2, 1, 0, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2
|
|
|
|
};
|
|
|
|
static const int kValueOffset[16] = {
|
|
|
|
0, 0, 0, 0, -1, 1, -2, 2, -3, 3, -1, 1, -2, 2, -3, 3
|
|
|
|
};
|
|
|
|
for (int i = 0; i < cmds->size(); ++i) {
|
2014-03-20 13:32:35 +00:00
|
|
|
pos += (*cmds)[i].insert_length_;
|
|
|
|
size_t max_distance = std::min(pos, max_backward);
|
2013-10-23 11:06:13 +00:00
|
|
|
int cur_dist = (*cmds)[i].copy_distance_;
|
|
|
|
int dist_code = cur_dist + 16;
|
2014-03-20 13:32:35 +00:00
|
|
|
if (cur_dist <= max_distance) {
|
|
|
|
if (cur_dist == 0) break;
|
|
|
|
int limits[16] = { 0, 0, 0, 0,
|
|
|
|
6, 6, 11, 11,
|
|
|
|
11, 11, 11, 11,
|
|
|
|
12, 12, 12, 12 };
|
|
|
|
for (int k = 0; k < 16; ++k) {
|
|
|
|
// Only accept more popular choices.
|
|
|
|
if (cur_dist < limits[k]) {
|
|
|
|
// Typically unpopular ranges, don't replace a short distance
|
|
|
|
// with them.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int comp = (dist_ringbuffer[(*ringbuffer_idx + kIndexOffset[k]) & 3] +
|
|
|
|
kValueOffset[k]);
|
|
|
|
if (cur_dist == comp) {
|
|
|
|
dist_code = k + 1;
|
|
|
|
break;
|
|
|
|
}
|
2013-10-23 11:06:13 +00:00
|
|
|
}
|
2014-03-20 13:32:35 +00:00
|
|
|
if (dist_code > 1) {
|
|
|
|
dist_ringbuffer[*ringbuffer_idx & 3] = cur_dist;
|
|
|
|
++(*ringbuffer_idx);
|
2013-10-23 11:06:13 +00:00
|
|
|
}
|
2014-03-20 13:32:35 +00:00
|
|
|
pos += (*cmds)[i].copy_length_;
|
|
|
|
} else {
|
|
|
|
int word_idx = cur_dist - max_distance - 1;
|
|
|
|
const std::string word =
|
|
|
|
GetTransformedDictionaryWord((*cmds)[i].copy_length_code_, word_idx);
|
|
|
|
pos += word.size();
|
2013-10-23 11:06:13 +00:00
|
|
|
}
|
|
|
|
(*cmds)[i].distance_code_ = dist_code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ComputeCommandPrefixes(std::vector<Command>* cmds,
|
|
|
|
int num_direct_distance_codes,
|
|
|
|
int distance_postfix_bits) {
|
|
|
|
for (int i = 0; i < cmds->size(); ++i) {
|
|
|
|
Command* cmd = &(*cmds)[i];
|
|
|
|
cmd->command_prefix_ = CommandPrefix(cmd->insert_length_,
|
2013-11-19 22:32:56 +00:00
|
|
|
cmd->copy_length_code_);
|
|
|
|
if (cmd->copy_length_code_ > 0) {
|
2013-10-23 11:06:13 +00:00
|
|
|
PrefixEncodeCopyDistance(cmd->distance_code_,
|
|
|
|
num_direct_distance_codes,
|
|
|
|
distance_postfix_bits,
|
|
|
|
&cmd->distance_prefix_,
|
|
|
|
&cmd->distance_extra_bits_,
|
|
|
|
&cmd->distance_extra_bits_value_);
|
|
|
|
}
|
|
|
|
if (cmd->command_prefix_ < 128 && cmd->distance_prefix_ == 0) {
|
|
|
|
cmd->distance_prefix_ = 0xffff;
|
|
|
|
} else {
|
|
|
|
cmd->command_prefix_ += 128;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int IndexOf(const std::vector<int>& v, int value) {
|
|
|
|
for (int i = 0; i < v.size(); ++i) {
|
|
|
|
if (v[i] == value) return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MoveToFront(std::vector<int>* v, int index) {
|
|
|
|
int value = (*v)[index];
|
|
|
|
for (int i = index; i > 0; --i) {
|
|
|
|
(*v)[i] = (*v)[i - 1];
|
|
|
|
}
|
|
|
|
(*v)[0] = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<int> MoveToFrontTransform(const std::vector<int>& v) {
|
|
|
|
if (v.empty()) return v;
|
|
|
|
std::vector<int> mtf(*max_element(v.begin(), v.end()) + 1);
|
|
|
|
for (int i = 0; i < mtf.size(); ++i) mtf[i] = i;
|
|
|
|
std::vector<int> result(v.size());
|
|
|
|
for (int i = 0; i < v.size(); ++i) {
|
|
|
|
int index = IndexOf(mtf, v[i]);
|
|
|
|
result[i] = index;
|
|
|
|
MoveToFront(&mtf, index);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finds runs of zeros in v_in and replaces them with a prefix code of the run
|
|
|
|
// length plus extra bits in *v_out and *extra_bits. Non-zero values in v_in are
|
|
|
|
// shifted by *max_length_prefix. Will not create prefix codes bigger than the
|
|
|
|
// initial value of *max_run_length_prefix. The prefix code of run length L is
|
|
|
|
// simply Log2Floor(L) and the number of extra bits is the same as the prefix
|
|
|
|
// code.
|
|
|
|
void RunLengthCodeZeros(const std::vector<int>& v_in,
|
|
|
|
int* max_run_length_prefix,
|
|
|
|
std::vector<int>* v_out,
|
|
|
|
std::vector<int>* extra_bits) {
|
|
|
|
int max_reps = 0;
|
|
|
|
for (int i = 0; i < v_in.size();) {
|
|
|
|
for (; i < v_in.size() && v_in[i] != 0; ++i) ;
|
|
|
|
int reps = 0;
|
|
|
|
for (; i < v_in.size() && v_in[i] == 0; ++i) {
|
|
|
|
++reps;
|
|
|
|
}
|
|
|
|
max_reps = std::max(reps, max_reps);
|
|
|
|
}
|
|
|
|
int max_prefix = max_reps > 0 ? Log2Floor(max_reps) : 0;
|
|
|
|
*max_run_length_prefix = std::min(max_prefix, *max_run_length_prefix);
|
|
|
|
for (int i = 0; i < v_in.size();) {
|
|
|
|
if (v_in[i] != 0) {
|
|
|
|
v_out->push_back(v_in[i] + *max_run_length_prefix);
|
|
|
|
extra_bits->push_back(0);
|
|
|
|
++i;
|
|
|
|
} else {
|
|
|
|
int reps = 1;
|
|
|
|
for (uint32_t k = i + 1; k < v_in.size() && v_in[k] == 0; ++k) {
|
|
|
|
++reps;
|
|
|
|
}
|
|
|
|
i += reps;
|
|
|
|
while (reps) {
|
|
|
|
if (reps < (2 << *max_run_length_prefix)) {
|
|
|
|
int run_length_prefix = Log2Floor(reps);
|
|
|
|
v_out->push_back(run_length_prefix);
|
|
|
|
extra_bits->push_back(reps - (1 << run_length_prefix));
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
v_out->push_back(*max_run_length_prefix);
|
|
|
|
extra_bits->push_back((1 << *max_run_length_prefix) - 1);
|
|
|
|
reps -= (2 << *max_run_length_prefix) - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Returns a maximum zero-run-length-prefix value such that run-length coding
|
|
|
|
// zeros in v with this maximum prefix value and then encoding the resulting
|
|
|
|
// histogram and entropy-coding v produces the least amount of bits.
|
|
|
|
int BestMaxZeroRunLengthPrefix(const std::vector<int>& v) {
|
|
|
|
int min_cost = std::numeric_limits<int>::max();
|
|
|
|
int best_max_prefix = 0;
|
|
|
|
for (int max_prefix = 0; max_prefix <= 16; ++max_prefix) {
|
|
|
|
std::vector<int> rle_symbols;
|
|
|
|
std::vector<int> extra_bits;
|
|
|
|
int max_run_length_prefix = max_prefix;
|
|
|
|
RunLengthCodeZeros(v, &max_run_length_prefix, &rle_symbols, &extra_bits);
|
|
|
|
if (max_run_length_prefix < max_prefix) break;
|
2014-03-27 15:38:07 +00:00
|
|
|
HistogramContextMap histogram;
|
2013-10-23 11:06:13 +00:00
|
|
|
for (int i = 0; i < rle_symbols.size(); ++i) {
|
|
|
|
histogram.Add(rle_symbols[i]);
|
|
|
|
}
|
|
|
|
int bit_cost = PopulationCost(histogram);
|
|
|
|
if (max_prefix > 0) {
|
|
|
|
bit_cost += 4;
|
|
|
|
}
|
|
|
|
for (int i = 1; i <= max_prefix; ++i) {
|
|
|
|
bit_cost += histogram.data_[i] * i; // extra bits
|
|
|
|
}
|
|
|
|
if (bit_cost < min_cost) {
|
|
|
|
min_cost = bit_cost;
|
|
|
|
best_max_prefix = max_prefix;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return best_max_prefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EncodeContextMap(const std::vector<int>& context_map,
|
|
|
|
int num_clusters,
|
|
|
|
int* storage_ix, uint8_t* storage) {
|
2014-10-15 12:01:36 +00:00
|
|
|
StoreVarLenUint8(num_clusters - 1, storage_ix, storage);
|
2013-10-23 11:06:13 +00:00
|
|
|
|
2013-11-19 22:32:56 +00:00
|
|
|
if (num_clusters == 1) {
|
2013-10-23 11:06:13 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<int> transformed_symbols = MoveToFrontTransform(context_map);
|
|
|
|
std::vector<int> rle_symbols;
|
|
|
|
std::vector<int> extra_bits;
|
|
|
|
int max_run_length_prefix = BestMaxZeroRunLengthPrefix(transformed_symbols);
|
|
|
|
RunLengthCodeZeros(transformed_symbols, &max_run_length_prefix,
|
|
|
|
&rle_symbols, &extra_bits);
|
2013-12-12 12:18:04 +00:00
|
|
|
HistogramContextMap symbol_histogram;
|
2013-10-23 11:06:13 +00:00
|
|
|
for (int i = 0; i < rle_symbols.size(); ++i) {
|
|
|
|
symbol_histogram.Add(rle_symbols[i]);
|
|
|
|
}
|
|
|
|
bool use_rle = max_run_length_prefix > 0;
|
|
|
|
WriteBits(1, use_rle, storage_ix, storage);
|
|
|
|
if (use_rle) {
|
|
|
|
WriteBits(4, max_run_length_prefix - 1, storage_ix, storage);
|
|
|
|
}
|
2014-03-20 13:32:35 +00:00
|
|
|
EntropyCodeContextMap symbol_code;
|
|
|
|
BuildAndStoreEntropyCode(symbol_histogram, 15,
|
|
|
|
num_clusters + max_run_length_prefix,
|
|
|
|
&symbol_code,
|
|
|
|
storage_ix, storage);
|
2013-10-23 11:06:13 +00:00
|
|
|
for (int i = 0; i < rle_symbols.size(); ++i) {
|
2014-03-20 13:32:35 +00:00
|
|
|
WriteBits(symbol_code.depth_[rle_symbols[i]],
|
|
|
|
symbol_code.bits_[rle_symbols[i]],
|
|
|
|
storage_ix, storage);
|
2013-10-23 11:06:13 +00:00
|
|
|
if (rle_symbols[i] > 0 && rle_symbols[i] <= max_run_length_prefix) {
|
|
|
|
WriteBits(rle_symbols[i], extra_bits[i], storage_ix, storage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
WriteBits(1, 1, storage_ix, storage); // use move-to-front
|
|
|
|
}
|
|
|
|
|
|
|
|
void MoveAndEncode(const BlockSplitCode& code,
|
|
|
|
BlockSplitIterator* it,
|
|
|
|
int* storage_ix, uint8_t* storage) {
|
|
|
|
if (it->length_ == 0) {
|
|
|
|
++it->idx_;
|
|
|
|
it->type_ = it->split_.types_[it->idx_];
|
|
|
|
it->length_ = it->split_.lengths_[it->idx_];
|
2014-10-28 10:53:52 +00:00
|
|
|
StoreBlockSwitch(code, it->idx_, storage_ix, storage);
|
2013-10-23 11:06:13 +00:00
|
|
|
}
|
|
|
|
--it->length_;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct EncodingParams {
|
|
|
|
int num_direct_distance_codes;
|
|
|
|
int distance_postfix_bits;
|
|
|
|
int literal_context_mode;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct MetaBlock {
|
|
|
|
std::vector<Command> cmds;
|
|
|
|
EncodingParams params;
|
|
|
|
BlockSplit literal_split;
|
|
|
|
BlockSplit command_split;
|
|
|
|
BlockSplit distance_split;
|
2013-11-15 18:02:17 +00:00
|
|
|
std::vector<int> literal_context_modes;
|
2013-10-23 11:06:13 +00:00
|
|
|
std::vector<int> literal_context_map;
|
|
|
|
std::vector<int> distance_context_map;
|
|
|
|
std::vector<HistogramLiteral> literal_histograms;
|
|
|
|
std::vector<HistogramCommand> command_histograms;
|
|
|
|
std::vector<HistogramDistance> distance_histograms;
|
|
|
|
};
|
|
|
|
|
|
|
|
void BuildMetaBlock(const EncodingParams& params,
|
|
|
|
const std::vector<Command>& cmds,
|
2013-11-15 18:02:17 +00:00
|
|
|
const uint8_t* ringbuffer,
|
|
|
|
const size_t pos,
|
|
|
|
const size_t mask,
|
2013-10-23 11:06:13 +00:00
|
|
|
MetaBlock* mb) {
|
|
|
|
mb->cmds = cmds;
|
|
|
|
mb->params = params;
|
2013-12-12 12:18:04 +00:00
|
|
|
if (cmds.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
2013-10-23 11:06:13 +00:00
|
|
|
ComputeCommandPrefixes(&mb->cmds,
|
|
|
|
mb->params.num_direct_distance_codes,
|
|
|
|
mb->params.distance_postfix_bits);
|
|
|
|
SplitBlock(mb->cmds,
|
2013-11-15 18:02:17 +00:00
|
|
|
&ringbuffer[pos & mask],
|
2013-10-23 11:06:13 +00:00
|
|
|
&mb->literal_split,
|
|
|
|
&mb->command_split,
|
|
|
|
&mb->distance_split);
|
|
|
|
|
2013-11-15 18:02:17 +00:00
|
|
|
mb->literal_context_modes.resize(mb->literal_split.num_types_,
|
|
|
|
mb->params.literal_context_mode);
|
|
|
|
|
|
|
|
|
2013-10-23 11:06:13 +00:00
|
|
|
int num_literal_contexts =
|
2013-11-15 18:02:17 +00:00
|
|
|
mb->literal_split.num_types_ << kLiteralContextBits;
|
2013-10-23 11:06:13 +00:00
|
|
|
int num_distance_contexts =
|
2013-11-15 18:02:17 +00:00
|
|
|
mb->distance_split.num_types_ << kDistanceContextBits;
|
2013-10-23 11:06:13 +00:00
|
|
|
std::vector<HistogramLiteral> literal_histograms(num_literal_contexts);
|
|
|
|
mb->command_histograms.resize(mb->command_split.num_types_);
|
|
|
|
std::vector<HistogramDistance> distance_histograms(num_distance_contexts);
|
|
|
|
BuildHistograms(mb->cmds,
|
|
|
|
mb->literal_split,
|
|
|
|
mb->command_split,
|
|
|
|
mb->distance_split,
|
2013-11-15 18:02:17 +00:00
|
|
|
ringbuffer,
|
2013-10-23 11:06:13 +00:00
|
|
|
pos,
|
2013-11-15 18:02:17 +00:00
|
|
|
mask,
|
|
|
|
mb->literal_context_modes,
|
2013-10-23 11:06:13 +00:00
|
|
|
&literal_histograms,
|
|
|
|
&mb->command_histograms,
|
|
|
|
&distance_histograms);
|
|
|
|
|
2013-12-12 12:18:04 +00:00
|
|
|
// Histogram ids need to fit in one byte.
|
|
|
|
static const int kMaxNumberOfHistograms = 256;
|
2013-10-23 11:06:13 +00:00
|
|
|
|
|
|
|
mb->literal_histograms = literal_histograms;
|
2013-11-15 18:02:17 +00:00
|
|
|
ClusterHistograms(literal_histograms,
|
|
|
|
1 << kLiteralContextBits,
|
|
|
|
mb->literal_split.num_types_,
|
|
|
|
kMaxNumberOfHistograms,
|
|
|
|
&mb->literal_histograms,
|
|
|
|
&mb->literal_context_map);
|
2013-10-23 11:06:13 +00:00
|
|
|
|
|
|
|
mb->distance_histograms = distance_histograms;
|
2013-11-15 18:02:17 +00:00
|
|
|
ClusterHistograms(distance_histograms,
|
|
|
|
1 << kDistanceContextBits,
|
|
|
|
mb->distance_split.num_types_,
|
|
|
|
kMaxNumberOfHistograms,
|
|
|
|
&mb->distance_histograms,
|
|
|
|
&mb->distance_context_map);
|
2013-10-23 11:06:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t MetaBlockLength(const std::vector<Command>& cmds) {
|
|
|
|
size_t length = 0;
|
|
|
|
for (int i = 0; i < cmds.size(); ++i) {
|
|
|
|
const Command& cmd = cmds[i];
|
|
|
|
length += cmd.insert_length_ + cmd.copy_length_;
|
|
|
|
}
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StoreMetaBlock(const MetaBlock& mb,
|
2013-12-12 12:18:04 +00:00
|
|
|
const bool is_last,
|
2013-11-15 18:02:17 +00:00
|
|
|
const uint8_t* ringbuffer,
|
|
|
|
const size_t mask,
|
2013-10-23 11:06:13 +00:00
|
|
|
size_t* pos,
|
|
|
|
int* storage_ix, uint8_t* storage) {
|
|
|
|
size_t length = MetaBlockLength(mb.cmds);
|
|
|
|
const size_t end_pos = *pos + length;
|
2014-03-20 13:32:35 +00:00
|
|
|
EncodeMetaBlockLength(length, is_last, false, storage_ix, storage);
|
|
|
|
|
2013-12-12 12:18:04 +00:00
|
|
|
if (length == 0) {
|
|
|
|
return;
|
|
|
|
}
|
2013-10-23 11:06:13 +00:00
|
|
|
BlockSplitCode literal_split_code;
|
|
|
|
BlockSplitCode command_split_code;
|
|
|
|
BlockSplitCode distance_split_code;
|
2014-10-28 10:53:52 +00:00
|
|
|
BuildAndStoreBlockSplitCode(mb.literal_split.types_,
|
|
|
|
mb.literal_split.lengths_,
|
|
|
|
mb.literal_split.num_types_,
|
|
|
|
9, // quality
|
|
|
|
&literal_split_code,
|
|
|
|
storage_ix, storage);
|
|
|
|
BuildAndStoreBlockSplitCode(mb.command_split.types_,
|
|
|
|
mb.command_split.lengths_,
|
|
|
|
mb.command_split.num_types_,
|
|
|
|
9, // quality
|
|
|
|
&command_split_code,
|
|
|
|
storage_ix, storage);
|
|
|
|
BuildAndStoreBlockSplitCode(mb.distance_split.types_,
|
|
|
|
mb.distance_split.lengths_,
|
|
|
|
mb.distance_split.num_types_,
|
|
|
|
9, // quality
|
|
|
|
&distance_split_code,
|
|
|
|
storage_ix, storage);
|
2013-10-23 11:06:13 +00:00
|
|
|
WriteBits(2, mb.params.distance_postfix_bits, storage_ix, storage);
|
|
|
|
WriteBits(4,
|
|
|
|
mb.params.num_direct_distance_codes >>
|
2014-03-20 13:32:35 +00:00
|
|
|
mb.params.distance_postfix_bits,
|
|
|
|
storage_ix, storage);
|
2013-10-23 11:06:13 +00:00
|
|
|
int num_distance_codes =
|
|
|
|
kNumDistanceShortCodes + mb.params.num_direct_distance_codes +
|
|
|
|
(48 << mb.params.distance_postfix_bits);
|
2013-11-15 18:02:17 +00:00
|
|
|
for (int i = 0; i < mb.literal_split.num_types_; ++i) {
|
|
|
|
WriteBits(2, mb.literal_context_modes[i], storage_ix, storage);
|
|
|
|
}
|
2014-03-20 13:32:35 +00:00
|
|
|
EncodeContextMap(mb.literal_context_map, mb.literal_histograms.size(),
|
|
|
|
storage_ix, storage);
|
|
|
|
EncodeContextMap(mb.distance_context_map, mb.distance_histograms.size(),
|
|
|
|
storage_ix, storage);
|
2013-10-23 11:06:13 +00:00
|
|
|
std::vector<EntropyCodeLiteral> literal_codes;
|
|
|
|
std::vector<EntropyCodeCommand> command_codes;
|
|
|
|
std::vector<EntropyCodeDistance> distance_codes;
|
2014-03-20 13:32:35 +00:00
|
|
|
BuildAndStoreEntropyCodes(mb.literal_histograms, 256, &literal_codes,
|
|
|
|
storage_ix, storage);
|
|
|
|
BuildAndStoreEntropyCodes(mb.command_histograms, kNumCommandPrefixes,
|
|
|
|
&command_codes, storage_ix, storage);
|
|
|
|
BuildAndStoreEntropyCodes(mb.distance_histograms, num_distance_codes,
|
|
|
|
&distance_codes, storage_ix, storage);
|
2013-10-23 11:06:13 +00:00
|
|
|
BlockSplitIterator literal_it(mb.literal_split);
|
|
|
|
BlockSplitIterator command_it(mb.command_split);
|
|
|
|
BlockSplitIterator distance_it(mb.distance_split);
|
|
|
|
for (int i = 0; i < mb.cmds.size(); ++i) {
|
|
|
|
const Command& cmd = mb.cmds[i];
|
|
|
|
MoveAndEncode(command_split_code, &command_it, storage_ix, storage);
|
|
|
|
EncodeCommand(cmd, command_codes[command_it.type_], storage_ix, storage);
|
|
|
|
for (int j = 0; j < cmd.insert_length_; ++j) {
|
|
|
|
MoveAndEncode(literal_split_code, &literal_it, storage_ix, storage);
|
|
|
|
int histogram_idx = literal_it.type_;
|
2013-11-15 18:02:17 +00:00
|
|
|
uint8_t prev_byte = *pos > 0 ? ringbuffer[(*pos - 1) & mask] : 0;
|
|
|
|
uint8_t prev_byte2 = *pos > 1 ? ringbuffer[(*pos - 2) & mask] : 0;
|
|
|
|
int context = ((literal_it.type_ << kLiteralContextBits) +
|
|
|
|
Context(prev_byte, prev_byte2,
|
|
|
|
mb.literal_context_modes[literal_it.type_]));
|
|
|
|
histogram_idx = mb.literal_context_map[context];
|
2014-03-20 13:32:35 +00:00
|
|
|
int literal = ringbuffer[*pos & mask];
|
|
|
|
WriteBits(literal_codes[histogram_idx].depth_[literal],
|
|
|
|
literal_codes[histogram_idx].bits_[literal],
|
|
|
|
storage_ix, storage);
|
2013-11-15 18:02:17 +00:00
|
|
|
++(*pos);
|
2013-10-23 11:06:13 +00:00
|
|
|
}
|
|
|
|
if (*pos < end_pos && cmd.distance_prefix_ != 0xffff) {
|
|
|
|
MoveAndEncode(distance_split_code, &distance_it, storage_ix, storage);
|
2013-11-15 18:02:17 +00:00
|
|
|
int context = (distance_it.type_ << 2) +
|
2013-11-19 22:32:56 +00:00
|
|
|
((cmd.copy_length_code_ > 4) ? 3 : cmd.copy_length_code_ - 2);
|
|
|
|
int histogram_index = mb.distance_context_map[context];
|
2013-10-23 11:06:13 +00:00
|
|
|
EncodeCopyDistance(cmd, distance_codes[histogram_index],
|
|
|
|
storage_ix, storage);
|
|
|
|
}
|
|
|
|
*pos += cmd.copy_length_;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-20 13:32:35 +00:00
|
|
|
BrotliCompressor::BrotliCompressor(BrotliParams params)
|
|
|
|
: params_(params),
|
|
|
|
window_bits_(kWindowBits),
|
|
|
|
hashers_(new Hashers()),
|
2013-11-15 18:02:17 +00:00
|
|
|
dist_ringbuffer_idx_(0),
|
|
|
|
input_pos_(0),
|
|
|
|
ringbuffer_(kRingBufferBits, kMetaBlockSizeBits),
|
|
|
|
literal_cost_(1 << kRingBufferBits),
|
|
|
|
storage_ix_(0),
|
|
|
|
storage_(new uint8_t[2 << kMetaBlockSizeBits]) {
|
2013-11-28 16:37:13 +00:00
|
|
|
dist_ringbuffer_[0] = 16;
|
|
|
|
dist_ringbuffer_[1] = 15;
|
|
|
|
dist_ringbuffer_[2] = 11;
|
|
|
|
dist_ringbuffer_[3] = 4;
|
2013-11-19 22:32:56 +00:00
|
|
|
storage_[0] = 0;
|
2014-03-20 13:32:35 +00:00
|
|
|
switch (params.mode) {
|
|
|
|
case BrotliParams::MODE_TEXT: hash_type_ = Hashers::HASH_15_8_4; break;
|
|
|
|
case BrotliParams::MODE_FONT: hash_type_ = Hashers::HASH_15_8_2; break;
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
hashers_->Init(hash_type_);
|
|
|
|
if (params.mode == BrotliParams::MODE_TEXT) {
|
|
|
|
StoreDictionaryWordHashes();
|
|
|
|
}
|
2013-11-19 22:32:56 +00:00
|
|
|
}
|
2013-11-15 18:02:17 +00:00
|
|
|
|
|
|
|
BrotliCompressor::~BrotliCompressor() {
|
|
|
|
delete[] storage_;
|
|
|
|
}
|
|
|
|
|
2014-03-20 13:32:35 +00:00
|
|
|
StaticDictionary *BrotliCompressor::static_dictionary_ = NULL;
|
|
|
|
|
2014-02-17 13:25:36 +00:00
|
|
|
void BrotliCompressor::StoreDictionaryWordHashes() {
|
2014-03-20 13:32:35 +00:00
|
|
|
const int num_transforms = kNumTransforms;
|
|
|
|
if (static_dictionary_ == NULL) {
|
|
|
|
static_dictionary_ = new StaticDictionary;
|
|
|
|
for (int t = num_transforms - 1; t >= 0; --t) {
|
2014-03-25 15:48:25 +00:00
|
|
|
for (int i = kMaxDictionaryWordLength;
|
|
|
|
i >= kMinDictionaryWordLength; --i) {
|
2014-03-20 13:32:35 +00:00
|
|
|
const int num_words = 1 << kBrotliDictionarySizeBitsByLength[i];
|
|
|
|
for (int j = num_words - 1; j >= 0; --j) {
|
|
|
|
int word_id = t * num_words + j;
|
|
|
|
std::string word = GetTransformedDictionaryWord(i, word_id);
|
2014-03-25 15:48:25 +00:00
|
|
|
if (word.size() >= 4) {
|
2014-03-20 13:32:35 +00:00
|
|
|
static_dictionary_->Insert(word, i, word_id);
|
|
|
|
}
|
2014-02-17 13:25:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-03-20 13:32:35 +00:00
|
|
|
hashers_->SetStaticDictionary(static_dictionary_);
|
2014-02-17 13:25:36 +00:00
|
|
|
}
|
|
|
|
|
2013-11-15 18:02:17 +00:00
|
|
|
void BrotliCompressor::WriteStreamHeader() {
|
|
|
|
// Encode window size.
|
2013-11-19 22:32:56 +00:00
|
|
|
if (window_bits_ == 16) {
|
|
|
|
WriteBits(1, 0, &storage_ix_, storage_);
|
|
|
|
} else {
|
|
|
|
WriteBits(1, 1, &storage_ix_, storage_);
|
|
|
|
WriteBits(3, window_bits_ - 17, &storage_ix_, storage_);
|
|
|
|
}
|
2013-11-15 18:02:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BrotliCompressor::WriteMetaBlock(const size_t input_size,
|
|
|
|
const uint8_t* input_buffer,
|
2013-12-12 12:18:04 +00:00
|
|
|
const bool is_last,
|
2013-11-15 18:02:17 +00:00
|
|
|
size_t* encoded_size,
|
|
|
|
uint8_t* encoded_buffer) {
|
2014-02-14 14:04:23 +00:00
|
|
|
static const double kMinUTF8Ratio = 0.75;
|
|
|
|
bool utf8_mode = false;
|
2013-11-15 18:02:17 +00:00
|
|
|
std::vector<Command> commands;
|
2013-12-12 12:18:04 +00:00
|
|
|
if (input_size > 0) {
|
|
|
|
ringbuffer_.Write(input_buffer, input_size);
|
2014-02-14 14:04:23 +00:00
|
|
|
utf8_mode = IsMostlyUTF8(
|
|
|
|
&ringbuffer_.start()[input_pos_ & kRingBufferMask],
|
|
|
|
input_size, kMinUTF8Ratio);
|
|
|
|
if (utf8_mode) {
|
|
|
|
EstimateBitCostsForLiteralsUTF8(input_pos_, input_size,
|
2014-03-20 13:32:35 +00:00
|
|
|
kRingBufferMask, kRingBufferMask,
|
|
|
|
ringbuffer_.start(), &literal_cost_[0]);
|
2014-02-14 14:04:23 +00:00
|
|
|
} else {
|
|
|
|
EstimateBitCostsForLiterals(input_pos_, input_size,
|
2014-03-20 13:32:35 +00:00
|
|
|
kRingBufferMask, kRingBufferMask,
|
|
|
|
ringbuffer_.start(), &literal_cost_[0]);
|
|
|
|
}
|
|
|
|
CreateBackwardReferences(
|
|
|
|
input_size, input_pos_,
|
|
|
|
ringbuffer_.start(),
|
|
|
|
&literal_cost_[0],
|
|
|
|
kRingBufferMask, kMaxBackwardDistance,
|
|
|
|
hashers_.get(),
|
|
|
|
hash_type_,
|
|
|
|
&commands);
|
|
|
|
ComputeDistanceShortCodes(&commands, input_pos_, kMaxBackwardDistance,
|
|
|
|
dist_ringbuffer_,
|
2013-12-12 12:18:04 +00:00
|
|
|
&dist_ringbuffer_idx_);
|
|
|
|
}
|
2013-11-15 18:02:17 +00:00
|
|
|
EncodingParams params;
|
2014-03-20 13:32:35 +00:00
|
|
|
params.num_direct_distance_codes =
|
|
|
|
params_.mode == BrotliParams::MODE_FONT ? 12 : 0;
|
|
|
|
params.distance_postfix_bits =
|
|
|
|
params_.mode == BrotliParams::MODE_FONT ? 1 : 0;
|
2013-11-15 18:02:17 +00:00
|
|
|
params.literal_context_mode = CONTEXT_SIGNED;
|
2013-12-12 12:18:04 +00:00
|
|
|
const int storage_ix0 = storage_ix_;
|
2013-11-15 18:02:17 +00:00
|
|
|
MetaBlock mb;
|
|
|
|
BuildMetaBlock(params, commands, ringbuffer_.start(), input_pos_,
|
|
|
|
kRingBufferMask, &mb);
|
2013-12-12 12:18:04 +00:00
|
|
|
StoreMetaBlock(mb, is_last, ringbuffer_.start(), kRingBufferMask,
|
2013-11-15 18:02:17 +00:00
|
|
|
&input_pos_, &storage_ix_, storage_);
|
2013-12-12 12:18:04 +00:00
|
|
|
size_t output_size = is_last ? ((storage_ix_ + 7) >> 3) : (storage_ix_ >> 3);
|
2014-03-20 13:32:35 +00:00
|
|
|
output_size -= (storage_ix0 >> 3);
|
2013-12-12 12:18:04 +00:00
|
|
|
if (input_size + 4 < output_size) {
|
|
|
|
storage_ix_ = storage_ix0;
|
|
|
|
storage_[storage_ix_ >> 3] &= (1 << (storage_ix_ & 7)) - 1;
|
|
|
|
EncodeMetaBlockLength(input_size, false, true, &storage_ix_, storage_);
|
|
|
|
size_t hdr_size = (storage_ix_ + 7) >> 3;
|
|
|
|
memcpy(encoded_buffer, storage_, hdr_size);
|
|
|
|
memcpy(encoded_buffer + hdr_size, input_buffer, input_size);
|
|
|
|
*encoded_size = hdr_size + input_size;
|
|
|
|
if (is_last) {
|
|
|
|
encoded_buffer[*encoded_size] = 0x3; // ISLAST, ISEMPTY
|
|
|
|
++(*encoded_size);
|
|
|
|
}
|
|
|
|
storage_ix_ = 0;
|
|
|
|
storage_[0] = 0;
|
|
|
|
} else {
|
|
|
|
memcpy(encoded_buffer, storage_, output_size);
|
|
|
|
*encoded_size = output_size;
|
|
|
|
if (is_last) {
|
|
|
|
storage_ix_ = 0;
|
|
|
|
storage_[0] = 0;
|
|
|
|
} else {
|
|
|
|
storage_ix_ -= output_size << 3;
|
|
|
|
storage_[storage_ix_ >> 3] = storage_[output_size];
|
|
|
|
}
|
|
|
|
}
|
2013-11-15 18:02:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BrotliCompressor::FinishStream(
|
|
|
|
size_t* encoded_size, uint8_t* encoded_buffer) {
|
2013-12-12 12:18:04 +00:00
|
|
|
WriteMetaBlock(0, NULL, true, encoded_size, encoded_buffer);
|
2013-11-15 18:02:17 +00:00
|
|
|
}
|
|
|
|
|
2014-03-20 13:32:35 +00:00
|
|
|
int BrotliCompressBuffer(BrotliParams params,
|
|
|
|
size_t input_size,
|
2013-10-23 11:06:13 +00:00
|
|
|
const uint8_t* input_buffer,
|
|
|
|
size_t* encoded_size,
|
|
|
|
uint8_t* encoded_buffer) {
|
|
|
|
if (input_size == 0) {
|
2013-11-28 16:37:13 +00:00
|
|
|
encoded_buffer[0] = 6;
|
|
|
|
*encoded_size = 1;
|
2013-10-23 11:06:13 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-03-20 13:32:35 +00:00
|
|
|
BrotliCompressor compressor(params);
|
2013-11-15 18:02:17 +00:00
|
|
|
compressor.WriteStreamHeader();
|
2013-10-23 11:06:13 +00:00
|
|
|
|
2013-11-15 18:02:17 +00:00
|
|
|
const int max_block_size = 1 << kMetaBlockSizeBits;
|
|
|
|
size_t max_output_size = *encoded_size;
|
|
|
|
const uint8_t* input_end = input_buffer + input_size;
|
|
|
|
*encoded_size = 0;
|
2013-10-23 11:06:13 +00:00
|
|
|
|
2013-11-15 18:02:17 +00:00
|
|
|
while (input_buffer < input_end) {
|
|
|
|
int block_size = max_block_size;
|
2013-12-12 12:18:04 +00:00
|
|
|
bool is_last = false;
|
2013-11-15 18:02:17 +00:00
|
|
|
if (block_size >= input_end - input_buffer) {
|
|
|
|
block_size = input_end - input_buffer;
|
2013-12-12 12:18:04 +00:00
|
|
|
is_last = true;
|
2013-11-15 18:02:17 +00:00
|
|
|
}
|
|
|
|
size_t output_size = max_output_size;
|
2013-12-12 12:18:04 +00:00
|
|
|
compressor.WriteMetaBlock(block_size, input_buffer, is_last,
|
2013-11-15 18:02:17 +00:00
|
|
|
&output_size, &encoded_buffer[*encoded_size]);
|
|
|
|
input_buffer += block_size;
|
|
|
|
*encoded_size += output_size;
|
|
|
|
max_output_size -= output_size;
|
2013-10-23 11:06:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace brotli
|