brotli 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/brotli/brotli.cc +114 -24
- data/ext/brotli/brotli.h +0 -1
- data/ext/brotli/extconf.rb +30 -23
- data/lib/brotli/version.rb +1 -1
- data/vendor/brotli/LICENSE +1 -1
- data/vendor/brotli/dec/Makefile +1 -1
- data/vendor/brotli/dec/bit_reader.c +3 -3
- data/vendor/brotli/dec/bit_reader.h +25 -27
- data/vendor/brotli/dec/context.h +4 -4
- data/vendor/brotli/dec/decode.c +410 -486
- data/vendor/brotli/dec/decode.h +101 -105
- data/vendor/brotli/dec/dictionary.c +1 -1
- data/vendor/brotli/dec/dictionary.h +7 -8
- data/vendor/brotli/dec/huffman.c +103 -105
- data/vendor/brotli/dec/huffman.h +18 -18
- data/vendor/brotli/dec/port.h +52 -40
- data/vendor/brotli/dec/prefix.h +2 -0
- data/vendor/brotli/dec/state.c +13 -19
- data/vendor/brotli/dec/state.h +25 -39
- data/vendor/brotli/dec/transform.h +38 -44
- data/vendor/brotli/dec/types.h +2 -2
- data/vendor/brotli/enc/Makefile +1 -1
- data/vendor/brotli/enc/backward_references.cc +455 -359
- data/vendor/brotli/enc/backward_references.h +79 -3
- data/vendor/brotli/enc/bit_cost.h +54 -32
- data/vendor/brotli/enc/block_splitter.cc +285 -193
- data/vendor/brotli/enc/block_splitter.h +4 -12
- data/vendor/brotli/enc/brotli_bit_stream.cc +623 -324
- data/vendor/brotli/enc/brotli_bit_stream.h +76 -37
- data/vendor/brotli/enc/cluster.h +161 -120
- data/vendor/brotli/enc/command.h +60 -37
- data/vendor/brotli/enc/compress_fragment.cc +701 -0
- data/vendor/brotli/enc/compress_fragment.h +47 -0
- data/vendor/brotli/enc/compress_fragment_two_pass.cc +524 -0
- data/vendor/brotli/enc/compress_fragment_two_pass.h +40 -0
- data/vendor/brotli/enc/compressor.h +15 -0
- data/vendor/brotli/enc/context.h +1 -1
- data/vendor/brotli/enc/dictionary.h +2 -2
- data/vendor/brotli/enc/encode.cc +819 -286
- data/vendor/brotli/enc/encode.h +38 -15
- data/vendor/brotli/enc/encode_parallel.cc +40 -42
- data/vendor/brotli/enc/entropy_encode.cc +144 -147
- data/vendor/brotli/enc/entropy_encode.h +32 -8
- data/vendor/brotli/enc/entropy_encode_static.h +572 -0
- data/vendor/brotli/enc/fast_log.h +7 -40
- data/vendor/brotli/enc/find_match_length.h +9 -9
- data/vendor/brotli/enc/hash.h +462 -154
- data/vendor/brotli/enc/histogram.cc +6 -6
- data/vendor/brotli/enc/histogram.h +13 -13
- data/vendor/brotli/enc/literal_cost.cc +45 -45
- data/vendor/brotli/enc/metablock.cc +92 -89
- data/vendor/brotli/enc/metablock.h +12 -12
- data/vendor/brotli/enc/port.h +7 -16
- data/vendor/brotli/enc/prefix.h +23 -22
- data/vendor/brotli/enc/ringbuffer.h +75 -29
- data/vendor/brotli/enc/static_dict.cc +56 -48
- data/vendor/brotli/enc/static_dict.h +5 -5
- data/vendor/brotli/enc/streams.cc +1 -1
- data/vendor/brotli/enc/streams.h +5 -5
- data/vendor/brotli/enc/transform.h +40 -35
- data/vendor/brotli/enc/types.h +2 -0
- data/vendor/brotli/enc/utf8_util.cc +3 -2
- data/vendor/brotli/enc/write_bits.h +6 -6
- metadata +9 -5
- data/vendor/brotli/dec/streams.c +0 -102
- data/vendor/brotli/dec/streams.h +0 -95
@@ -18,12 +18,13 @@
|
|
18
18
|
|
19
19
|
namespace brotli {
|
20
20
|
|
21
|
-
|
21
|
+
// The maximum length for which the zopflification uses distinct distances.
|
22
|
+
static const uint16_t kMaxZopfliLen = 325;
|
22
23
|
|
23
24
|
// Histogram based cost model for zopflification.
|
24
25
|
class ZopfliCostModel {
|
25
26
|
public:
|
26
|
-
ZopfliCostModel() : min_cost_cmd_(kInfinity) {}
|
27
|
+
ZopfliCostModel(void) : min_cost_cmd_(kInfinity) {}
|
27
28
|
|
28
29
|
void SetFromCommands(size_t num_bytes,
|
29
30
|
size_t position,
|
@@ -31,34 +32,34 @@ class ZopfliCostModel {
|
|
31
32
|
size_t ringbuffer_mask,
|
32
33
|
const Command* commands,
|
33
34
|
size_t num_commands,
|
34
|
-
|
35
|
-
std::vector<
|
36
|
-
std::vector<
|
37
|
-
std::vector<
|
35
|
+
size_t last_insert_len) {
|
36
|
+
std::vector<uint32_t> histogram_literal(256, 0);
|
37
|
+
std::vector<uint32_t> histogram_cmd(kNumCommandPrefixes, 0);
|
38
|
+
std::vector<uint32_t> histogram_dist(kNumDistancePrefixes, 0);
|
38
39
|
|
39
40
|
size_t pos = position - last_insert_len;
|
40
41
|
for (size_t i = 0; i < num_commands; i++) {
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
42
|
+
size_t inslength = commands[i].insert_len_;
|
43
|
+
size_t copylength = commands[i].copy_len();
|
44
|
+
size_t distcode = commands[i].dist_prefix_;
|
45
|
+
size_t cmdcode = commands[i].cmd_prefix_;
|
45
46
|
|
46
47
|
histogram_cmd[cmdcode]++;
|
47
48
|
if (cmdcode >= 128) histogram_dist[distcode]++;
|
48
49
|
|
49
|
-
for (
|
50
|
+
for (size_t j = 0; j < inslength; j++) {
|
50
51
|
histogram_literal[ringbuffer[(pos + j) & ringbuffer_mask]]++;
|
51
52
|
}
|
52
53
|
|
53
54
|
pos += inslength + copylength;
|
54
55
|
}
|
55
56
|
|
56
|
-
std::vector<
|
57
|
+
std::vector<float> cost_literal;
|
57
58
|
Set(histogram_literal, &cost_literal);
|
58
59
|
Set(histogram_cmd, &cost_cmd_);
|
59
60
|
Set(histogram_dist, &cost_dist_);
|
60
61
|
|
61
|
-
for (
|
62
|
+
for (uint32_t i = 0; i < kNumCommandPrefixes; ++i) {
|
62
63
|
min_cost_cmd_ = std::min(min_cost_cmd_, cost_cmd_[i]);
|
63
64
|
}
|
64
65
|
|
@@ -74,27 +75,26 @@ class ZopfliCostModel {
|
|
74
75
|
size_t position,
|
75
76
|
const uint8_t* ringbuffer,
|
76
77
|
size_t ringbuffer_mask) {
|
77
|
-
|
78
|
+
literal_costs_.resize(num_bytes + 2);
|
78
79
|
EstimateBitCostsForLiterals(position, num_bytes, ringbuffer_mask,
|
79
|
-
ringbuffer, &
|
80
|
-
literal_costs_.resize(num_bytes + 1);
|
80
|
+
ringbuffer, &literal_costs_[1]);
|
81
81
|
literal_costs_[0] = 0.0;
|
82
82
|
for (size_t i = 0; i < num_bytes; ++i) {
|
83
|
-
literal_costs_[i + 1]
|
83
|
+
literal_costs_[i + 1] += literal_costs_[i];
|
84
84
|
}
|
85
85
|
cost_cmd_.resize(kNumCommandPrefixes);
|
86
86
|
cost_dist_.resize(kNumDistancePrefixes);
|
87
|
-
for (
|
88
|
-
cost_cmd_[i] = FastLog2(11 + i);
|
87
|
+
for (uint32_t i = 0; i < kNumCommandPrefixes; ++i) {
|
88
|
+
cost_cmd_[i] = static_cast<float>(FastLog2(11 + i));
|
89
89
|
}
|
90
|
-
for (
|
91
|
-
cost_dist_[i] = FastLog2(20 + i);
|
90
|
+
for (uint32_t i = 0; i < kNumDistancePrefixes; ++i) {
|
91
|
+
cost_dist_[i] = static_cast<float>(FastLog2(20 + i));
|
92
92
|
}
|
93
|
-
min_cost_cmd_ = FastLog2(11);
|
93
|
+
min_cost_cmd_ = static_cast<float>(FastLog2(11));
|
94
94
|
}
|
95
95
|
|
96
|
-
|
97
|
-
|
96
|
+
float GetCommandCost(
|
97
|
+
size_t dist_code, size_t length_code, size_t insert_length) const {
|
98
98
|
uint16_t inscode = GetInsertLengthCode(insert_length);
|
99
99
|
uint16_t copycode = GetCopyLengthCode(length_code);
|
100
100
|
uint16_t cmdcode = CombineLengthCodes(inscode, copycode, dist_code == 0);
|
@@ -103,28 +103,29 @@ class ZopfliCostModel {
|
|
103
103
|
PrefixEncodeCopyDistance(dist_code, 0, 0, &dist_symbol, &distextra);
|
104
104
|
uint32_t distnumextra = distextra >> 24;
|
105
105
|
|
106
|
-
|
106
|
+
float result = static_cast<float>(
|
107
|
+
GetInsertExtra(inscode) + GetCopyExtra(copycode) + distnumextra);
|
107
108
|
result += cost_cmd_[cmdcode];
|
108
109
|
if (cmdcode >= 128) result += cost_dist_[dist_symbol];
|
109
110
|
return result;
|
110
111
|
}
|
111
112
|
|
112
|
-
|
113
|
+
float GetLiteralCosts(size_t from, size_t to) const {
|
113
114
|
return literal_costs_[to] - literal_costs_[from];
|
114
115
|
}
|
115
116
|
|
116
|
-
|
117
|
+
float GetMinCostCmd(void) const {
|
117
118
|
return min_cost_cmd_;
|
118
119
|
}
|
119
120
|
|
120
121
|
private:
|
121
|
-
void Set(const std::vector<
|
122
|
+
void Set(const std::vector<uint32_t>& histogram, std::vector<float>* cost) {
|
122
123
|
cost->resize(histogram.size());
|
123
|
-
|
124
|
+
size_t sum = 0;
|
124
125
|
for (size_t i = 0; i < histogram.size(); i++) {
|
125
126
|
sum += histogram[i];
|
126
127
|
}
|
127
|
-
|
128
|
+
float log2sum = static_cast<float>(FastLog2(sum));
|
128
129
|
for (size_t i = 0; i < histogram.size(); i++) {
|
129
130
|
if (histogram[i] == 0) {
|
130
131
|
(*cost)[i] = log2sum + 2;
|
@@ -132,54 +133,42 @@ class ZopfliCostModel {
|
|
132
133
|
}
|
133
134
|
|
134
135
|
// Shannon bits for this symbol.
|
135
|
-
(*cost)[i] = log2sum - FastLog2(histogram[i]);
|
136
|
+
(*cost)[i] = log2sum - static_cast<float>(FastLog2(histogram[i]));
|
136
137
|
|
137
138
|
// Cannot be coded with less than 1 bit
|
138
139
|
if ((*cost)[i] < 1) (*cost)[i] = 1;
|
139
140
|
}
|
140
141
|
}
|
141
142
|
|
142
|
-
std::vector<
|
143
|
-
std::vector<
|
143
|
+
std::vector<float> cost_cmd_; // The insert and copy length symbols.
|
144
|
+
std::vector<float> cost_dist_;
|
144
145
|
// Cumulative costs of literals per position in the stream.
|
145
|
-
std::vector<
|
146
|
-
|
146
|
+
std::vector<float> literal_costs_;
|
147
|
+
float min_cost_cmd_;
|
147
148
|
};
|
148
149
|
|
149
|
-
inline
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
int* result_dist_cache) {
|
154
|
-
if (distance <= max_distance && distance_code > 0) {
|
155
|
-
result_dist_cache[0] = distance;
|
156
|
-
memcpy(&result_dist_cache[1], dist_cache, 3 * sizeof(dist_cache[0]));
|
157
|
-
} else {
|
158
|
-
memcpy(result_dist_cache, dist_cache, 4 * sizeof(dist_cache[0]));
|
159
|
-
}
|
160
|
-
}
|
161
|
-
|
162
|
-
inline int ComputeDistanceCode(int distance,
|
163
|
-
int max_distance,
|
164
|
-
int quality,
|
165
|
-
const int* dist_cache) {
|
150
|
+
inline size_t ComputeDistanceCode(size_t distance,
|
151
|
+
size_t max_distance,
|
152
|
+
int quality,
|
153
|
+
const int* dist_cache) {
|
166
154
|
if (distance <= max_distance) {
|
167
|
-
if (distance == dist_cache[0]) {
|
155
|
+
if (distance == static_cast<size_t>(dist_cache[0])) {
|
168
156
|
return 0;
|
169
|
-
} else if (distance == dist_cache[1]) {
|
157
|
+
} else if (distance == static_cast<size_t>(dist_cache[1])) {
|
170
158
|
return 1;
|
171
|
-
} else if (distance == dist_cache[2]) {
|
159
|
+
} else if (distance == static_cast<size_t>(dist_cache[2])) {
|
172
160
|
return 2;
|
173
|
-
} else if (distance == dist_cache[3]) {
|
161
|
+
} else if (distance == static_cast<size_t>(dist_cache[3])) {
|
174
162
|
return 3;
|
175
163
|
} else if (quality > 3 && distance >= 6) {
|
176
|
-
for (
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
164
|
+
for (size_t k = 4; k < kNumDistanceShortCodes; ++k) {
|
165
|
+
size_t idx = kDistanceCacheIndex[k];
|
166
|
+
size_t candidate =
|
167
|
+
static_cast<size_t>(dist_cache[idx] + kDistanceCacheOffset[k]);
|
168
|
+
static const size_t kLimits[16] = { 0, 0, 0, 0,
|
169
|
+
6, 6, 11, 11,
|
170
|
+
11, 11, 11, 11,
|
171
|
+
12, 12, 12, 12 };
|
183
172
|
if (distance == candidate && distance >= kLimits[k]) {
|
184
173
|
return k;
|
185
174
|
}
|
@@ -189,99 +178,78 @@ inline int ComputeDistanceCode(int distance,
|
|
189
178
|
return distance + 15;
|
190
179
|
}
|
191
180
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
distance_code(0),
|
196
|
-
length_code(0),
|
197
|
-
insert_length(0),
|
198
|
-
cost(kInfinity) {}
|
199
|
-
|
200
|
-
// best length to get up to this byte (not including this byte itself)
|
201
|
-
int length;
|
202
|
-
// distance associated with the length
|
203
|
-
int distance;
|
204
|
-
int distance_code;
|
205
|
-
int distance_cache[4];
|
206
|
-
// length code associated with the length - usually the same as length,
|
207
|
-
// except in case of length-changing dictionary transformation.
|
208
|
-
int length_code;
|
209
|
-
// number of literal inserts before this copy
|
210
|
-
int insert_length;
|
211
|
-
// smallest cost to get to this byte from the beginning, as found so far
|
212
|
-
double cost;
|
213
|
-
};
|
214
|
-
|
181
|
+
// REQUIRES: len >= 2, start_pos <= pos
|
182
|
+
// REQUIRES: cost < kInfinity, nodes[start_pos].cost < kInfinity
|
183
|
+
// Maintains the "ZopfliNode array invariant".
|
215
184
|
inline void UpdateZopfliNode(ZopfliNode* nodes, size_t pos, size_t start_pos,
|
216
|
-
|
217
|
-
|
218
|
-
double cost) {
|
185
|
+
size_t len, size_t len_code, size_t dist,
|
186
|
+
size_t short_code, float cost) {
|
219
187
|
ZopfliNode& next = nodes[pos + len];
|
220
|
-
next.length = len;
|
221
|
-
next.
|
222
|
-
next.
|
223
|
-
next.distance_code = dist_code;
|
224
|
-
next.insert_length = static_cast<int>(pos - start_pos);
|
188
|
+
next.length = static_cast<uint32_t>(len | ((len + 9u - len_code) << 24));
|
189
|
+
next.distance = static_cast<uint32_t>(dist | (short_code << 25));
|
190
|
+
next.insert_length = static_cast<uint32_t>(pos - start_pos);
|
225
191
|
next.cost = cost;
|
226
|
-
SetDistanceCache(dist, dist_code, max_dist, dist_cache,
|
227
|
-
&next.distance_cache[0]);
|
228
192
|
}
|
229
193
|
|
230
194
|
// Maintains the smallest 2^k cost difference together with their positions
|
231
195
|
class StartPosQueue {
|
232
196
|
public:
|
197
|
+
struct PosData {
|
198
|
+
size_t pos;
|
199
|
+
int distance_cache[4];
|
200
|
+
float costdiff;
|
201
|
+
};
|
202
|
+
|
233
203
|
explicit StartPosQueue(int bits)
|
234
|
-
: mask_((
|
204
|
+
: mask_((1u << bits) - 1), q_(1 << bits), idx_(0) {}
|
235
205
|
|
236
|
-
void Clear() {
|
206
|
+
void Clear(void) {
|
237
207
|
idx_ = 0;
|
238
208
|
}
|
239
209
|
|
240
|
-
void Push(
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
if (q_[i & mask_].second > q_[(i - 1) & mask_].second) {
|
251
|
-
std::swap(q_[i & mask_], q_[(i - 1) & mask_]);
|
210
|
+
void Push(const StartPosQueue::PosData& posdata) {
|
211
|
+
size_t offset = ~idx_ & mask_;
|
212
|
+
++idx_;
|
213
|
+
size_t len = size();
|
214
|
+
q_[offset] = posdata;
|
215
|
+
/* Restore the sorted order. In the list of |len| items at most |len - 1|
|
216
|
+
adjacent element comparisons / swaps are required. */
|
217
|
+
for (size_t i = 1; i < len; ++i) {
|
218
|
+
if (q_[offset & mask_].costdiff > q_[(offset + 1) & mask_].costdiff) {
|
219
|
+
std::swap(q_[offset & mask_], q_[(offset + 1) & mask_]);
|
252
220
|
}
|
221
|
+
++offset;
|
253
222
|
}
|
254
|
-
++idx_;
|
255
223
|
}
|
256
224
|
|
257
|
-
|
225
|
+
size_t size(void) const { return std::min(idx_, mask_ + 1); }
|
258
226
|
|
259
|
-
|
260
|
-
return q_[(
|
227
|
+
const StartPosQueue::PosData& GetStartPosData(size_t k) const {
|
228
|
+
return q_[(k - idx_) & mask_];
|
261
229
|
}
|
262
230
|
|
263
231
|
private:
|
264
|
-
const
|
265
|
-
std::vector<
|
266
|
-
|
232
|
+
const size_t mask_;
|
233
|
+
std::vector<PosData> q_;
|
234
|
+
size_t idx_;
|
267
235
|
};
|
268
236
|
|
269
237
|
// Returns the minimum possible copy length that can improve the cost of any
|
270
238
|
// future position.
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
239
|
+
static size_t ComputeMinimumCopyLength(const StartPosQueue& queue,
|
240
|
+
const ZopfliNode* nodes,
|
241
|
+
const ZopfliCostModel& model,
|
242
|
+
const size_t num_bytes,
|
243
|
+
const size_t pos) {
|
276
244
|
// Compute the minimum possible cost of reaching any future position.
|
277
|
-
const size_t start0 = queue.
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
while (pos + len
|
245
|
+
const size_t start0 = queue.GetStartPosData(0).pos;
|
246
|
+
float min_cost = (nodes[start0].cost +
|
247
|
+
model.GetLiteralCosts(start0, pos) +
|
248
|
+
model.GetMinCostCmd());
|
249
|
+
size_t len = 2;
|
250
|
+
size_t next_len_bucket = 4;
|
251
|
+
size_t next_len_offset = 10;
|
252
|
+
while (pos + len <= num_bytes && nodes[pos + len].cost <= min_cost) {
|
285
253
|
// We already reached (pos + len) with no more cost than the minimum
|
286
254
|
// possible cost of reaching anything from this pos, so there is no point in
|
287
255
|
// looking for lengths <= len.
|
@@ -289,7 +257,7 @@ int ComputeMinimumCopyLength(const StartPosQueue& queue,
|
|
289
257
|
if (len == next_len_offset) {
|
290
258
|
// We reached the next copy length code bucket, so we add one more
|
291
259
|
// extra bit to the minimum cost.
|
292
|
-
min_cost += 1.0;
|
260
|
+
min_cost += static_cast<float>(1.0);
|
293
261
|
next_len_offset += next_len_bucket;
|
294
262
|
next_len_bucket *= 2;
|
295
263
|
}
|
@@ -297,193 +265,309 @@ int ComputeMinimumCopyLength(const StartPosQueue& queue,
|
|
297
265
|
return len;
|
298
266
|
}
|
299
267
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
268
|
+
// Fills in dist_cache[0..3] with the last four distances (as defined by
|
269
|
+
// Section 4. of the Spec) that would be used at (block_start + pos) if we
|
270
|
+
// used the shortest path of commands from block_start, computed from
|
271
|
+
// nodes[0..pos]. The last four distances at block_start are in
|
272
|
+
// starting_dist_cach[0..3].
|
273
|
+
// REQUIRES: nodes[pos].cost < kInfinity
|
274
|
+
// REQUIRES: nodes[0..pos] satisfies that "ZopfliNode array invariant".
|
275
|
+
static void ComputeDistanceCache(const size_t block_start,
|
276
|
+
const size_t pos,
|
277
|
+
const size_t max_backward,
|
278
|
+
const int* starting_dist_cache,
|
279
|
+
const ZopfliNode* nodes,
|
280
|
+
int* dist_cache) {
|
281
|
+
int idx = 0;
|
282
|
+
size_t p = pos;
|
283
|
+
// Because of prerequisite, does at most (pos + 1) / 2 iterations.
|
284
|
+
while (idx < 4 && p > 0) {
|
285
|
+
const size_t clen = nodes[p].copy_length();
|
286
|
+
const size_t ilen = nodes[p].insert_length;
|
287
|
+
const size_t dist = nodes[p].copy_distance();
|
288
|
+
// Since block_start + p is the end position of the command, the copy part
|
289
|
+
// starts from block_start + p - clen. Distances that are greater than this
|
290
|
+
// or greater than max_backward are static dictionary references, and do
|
291
|
+
// not update the last distances. Also distance code 0 (last distance)
|
292
|
+
// does not update the last distances.
|
293
|
+
if (dist + clen <= block_start + p && dist <= max_backward &&
|
294
|
+
nodes[p].distance_code() > 0) {
|
295
|
+
dist_cache[idx++] = static_cast<int>(dist);
|
296
|
+
}
|
297
|
+
// Because of prerequisite, p >= clen + ilen >= 2.
|
298
|
+
p -= clen + ilen;
|
299
|
+
}
|
300
|
+
for (; idx < 4; ++idx) {
|
301
|
+
dist_cache[idx] = *starting_dist_cache++;
|
302
|
+
}
|
303
|
+
}
|
322
304
|
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
const int idx = kDistanceCacheIndex[j];
|
348
|
-
const int backward = dist_cache2[idx] + kDistanceCacheOffset[j];
|
349
|
-
size_t prev_ix = cur_ix - backward;
|
350
|
-
if (prev_ix >= cur_ix) {
|
351
|
-
continue;
|
352
|
-
}
|
353
|
-
if (PREDICT_FALSE(backward > max_distance)) {
|
354
|
-
continue;
|
355
|
-
}
|
356
|
-
prev_ix &= ringbuffer_mask;
|
305
|
+
static void UpdateNodes(const size_t num_bytes,
|
306
|
+
const size_t block_start,
|
307
|
+
const size_t pos,
|
308
|
+
const uint8_t* ringbuffer,
|
309
|
+
const size_t ringbuffer_mask,
|
310
|
+
const size_t max_backward_limit,
|
311
|
+
const int* starting_dist_cache,
|
312
|
+
const size_t num_matches,
|
313
|
+
const BackwardMatch* matches,
|
314
|
+
const ZopfliCostModel* model,
|
315
|
+
StartPosQueue* queue,
|
316
|
+
ZopfliNode* nodes) {
|
317
|
+
size_t cur_ix = block_start + pos;
|
318
|
+
size_t cur_ix_masked = cur_ix & ringbuffer_mask;
|
319
|
+
size_t max_distance = std::min(cur_ix, max_backward_limit);
|
320
|
+
|
321
|
+
if (nodes[pos].cost <= model->GetLiteralCosts(0, pos)) {
|
322
|
+
StartPosQueue::PosData posdata;
|
323
|
+
posdata.pos = pos;
|
324
|
+
posdata.costdiff = nodes[pos].cost - model->GetLiteralCosts(0, pos);
|
325
|
+
ComputeDistanceCache(block_start, pos, max_backward_limit,
|
326
|
+
starting_dist_cache, nodes, posdata.distance_cache);
|
327
|
+
queue->Push(posdata);
|
328
|
+
}
|
357
329
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
330
|
+
const size_t min_len = ComputeMinimumCopyLength(
|
331
|
+
*queue, nodes, *model, num_bytes, pos);
|
332
|
+
|
333
|
+
// Go over the command starting positions in order of increasing cost
|
334
|
+
// difference.
|
335
|
+
for (size_t k = 0; k < 5 && k < queue->size(); ++k) {
|
336
|
+
const StartPosQueue::PosData& posdata = queue->GetStartPosData(k);
|
337
|
+
const size_t start = posdata.pos;
|
338
|
+
const float start_costdiff = posdata.costdiff;
|
339
|
+
|
340
|
+
// Look for last distance matches using the distance cache from this
|
341
|
+
// starting position.
|
342
|
+
size_t best_len = min_len - 1;
|
343
|
+
for (size_t j = 0; j < kNumDistanceShortCodes; ++j) {
|
344
|
+
const size_t idx = kDistanceCacheIndex[j];
|
345
|
+
const size_t backward = static_cast<size_t>(posdata.distance_cache[idx] +
|
346
|
+
kDistanceCacheOffset[j]);
|
347
|
+
size_t prev_ix = cur_ix - backward;
|
348
|
+
if (prev_ix >= cur_ix) {
|
349
|
+
continue;
|
350
|
+
}
|
351
|
+
if (PREDICT_FALSE(backward > max_distance)) {
|
352
|
+
continue;
|
378
353
|
}
|
354
|
+
prev_ix &= ringbuffer_mask;
|
379
355
|
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
// the maximum length is long enough, try only one maximum length.
|
397
|
-
int max_len = match.length();
|
398
|
-
if (len < max_len && (is_dictionary_match || max_len > kMaxZopfliLen)) {
|
399
|
-
len = max_len;
|
400
|
-
}
|
401
|
-
for (; len <= max_len; ++len) {
|
402
|
-
int len_code = is_dictionary_match ? match.length_code() : len;
|
403
|
-
const int inslen = static_cast<int>(i - start);
|
404
|
-
double cmd_cost = model.GetCommandCost(dist_code, len_code, inslen);
|
405
|
-
double cost = start_costdiff + cmd_cost + model.GetLiteralCosts(0, i);
|
406
|
-
if (cost < nodes[i + len].cost) {
|
407
|
-
UpdateZopfliNode(&nodes[0], i, start, len, len_code, dist,
|
408
|
-
dist_code, max_distance, dist_cache2, cost);
|
409
|
-
}
|
356
|
+
if (cur_ix_masked + best_len > ringbuffer_mask ||
|
357
|
+
prev_ix + best_len > ringbuffer_mask ||
|
358
|
+
ringbuffer[cur_ix_masked + best_len] !=
|
359
|
+
ringbuffer[prev_ix + best_len]) {
|
360
|
+
continue;
|
361
|
+
}
|
362
|
+
const size_t len =
|
363
|
+
FindMatchLengthWithLimit(&ringbuffer[prev_ix],
|
364
|
+
&ringbuffer[cur_ix_masked],
|
365
|
+
num_bytes - pos);
|
366
|
+
for (size_t l = best_len + 1; l <= len; ++l) {
|
367
|
+
const size_t inslen = pos - start;
|
368
|
+
float cmd_cost = model->GetCommandCost(j, l, inslen);
|
369
|
+
float cost = start_costdiff + cmd_cost + model->GetLiteralCosts(0, pos);
|
370
|
+
if (cost < nodes[pos + l].cost) {
|
371
|
+
UpdateZopfliNode(&nodes[0], pos, start, l, l, backward, j + 1, cost);
|
410
372
|
}
|
373
|
+
best_len = l;
|
411
374
|
}
|
412
375
|
}
|
413
376
|
|
414
|
-
|
415
|
-
|
416
|
-
//
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
377
|
+
// At higher iterations look only for new last distance matches, since
|
378
|
+
// looking only for new command start positions with the same distances
|
379
|
+
// does not help much.
|
380
|
+
if (k >= 2) continue;
|
381
|
+
|
382
|
+
// Loop through all possible copy lengths at this position.
|
383
|
+
size_t len = min_len;
|
384
|
+
for (size_t j = 0; j < num_matches; ++j) {
|
385
|
+
BackwardMatch match = matches[j];
|
386
|
+
size_t dist = match.distance;
|
387
|
+
bool is_dictionary_match = dist > max_distance;
|
388
|
+
// We already tried all possible last distance matches, so we can use
|
389
|
+
// normal distance code here.
|
390
|
+
size_t dist_code = dist + 15;
|
391
|
+
// Try all copy lengths up until the maximum copy length corresponding
|
392
|
+
// to this distance. If the distance refers to the static dictionary, or
|
393
|
+
// the maximum length is long enough, try only one maximum length.
|
394
|
+
size_t max_len = match.length();
|
395
|
+
if (len < max_len && (is_dictionary_match || max_len > kMaxZopfliLen)) {
|
396
|
+
len = max_len;
|
397
|
+
}
|
398
|
+
for (; len <= max_len; ++len) {
|
399
|
+
size_t len_code = is_dictionary_match ? match.length_code() : len;
|
400
|
+
const size_t inslen = pos - start;
|
401
|
+
float cmd_cost = model->GetCommandCost(dist_code, len_code, inslen);
|
402
|
+
float cost = start_costdiff + cmd_cost + model->GetLiteralCosts(0, pos);
|
403
|
+
if (cost < nodes[pos + len].cost) {
|
404
|
+
UpdateZopfliNode(&nodes[0], pos, start, len, len_code, dist, 0, cost);
|
405
|
+
}
|
406
|
+
}
|
422
407
|
}
|
423
408
|
}
|
409
|
+
}
|
424
410
|
|
425
|
-
|
411
|
+
static void ComputeShortestPathFromNodes(size_t num_bytes,
|
412
|
+
const ZopfliNode* nodes,
|
413
|
+
std::vector<uint32_t>* path) {
|
414
|
+
std::vector<uint32_t> backwards(num_bytes / 2 + 1);
|
426
415
|
size_t index = num_bytes;
|
427
416
|
while (nodes[index].cost == kInfinity) --index;
|
428
|
-
|
429
|
-
|
430
|
-
|
417
|
+
size_t num_commands = 0;
|
418
|
+
while (index != 0) {
|
419
|
+
size_t len = nodes[index].command_length();
|
420
|
+
backwards[num_commands++] = static_cast<uint32_t>(len);
|
431
421
|
index -= len;
|
432
422
|
}
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
path.push_back(backwards[i - 1]);
|
423
|
+
path->resize(num_commands);
|
424
|
+
for (size_t i = num_commands, j = 0; i > 0; --i, ++j) {
|
425
|
+
(*path)[j] = backwards[i - 1];
|
437
426
|
}
|
427
|
+
}
|
438
428
|
|
429
|
+
void ZopfliCreateCommands(const size_t num_bytes,
|
430
|
+
const size_t block_start,
|
431
|
+
const size_t max_backward_limit,
|
432
|
+
const std::vector<uint32_t>& path,
|
433
|
+
const ZopfliNode* nodes,
|
434
|
+
int* dist_cache,
|
435
|
+
size_t* last_insert_len,
|
436
|
+
Command* commands,
|
437
|
+
size_t* num_literals) {
|
439
438
|
size_t pos = 0;
|
440
439
|
for (size_t i = 0; i < path.size(); i++) {
|
441
440
|
const ZopfliNode& next = nodes[pos + path[i]];
|
442
|
-
|
443
|
-
|
441
|
+
size_t copy_length = next.copy_length();
|
442
|
+
size_t insert_length = next.insert_length;
|
444
443
|
pos += insert_length;
|
445
444
|
if (i == 0) {
|
446
445
|
insert_length += *last_insert_len;
|
447
446
|
*last_insert_len = 0;
|
448
447
|
}
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
static_cast<int>(std::min(position + pos, max_backward_limit));
|
448
|
+
size_t distance = next.copy_distance();
|
449
|
+
size_t len_code = next.length_code();
|
450
|
+
size_t max_distance = std::min(block_start + pos, max_backward_limit);
|
453
451
|
bool is_dictionary = (distance > max_distance);
|
454
|
-
|
452
|
+
size_t dist_code = next.distance_code();
|
455
453
|
|
456
454
|
Command cmd(insert_length, copy_length, len_code, dist_code);
|
457
|
-
|
455
|
+
commands[i] = cmd;
|
458
456
|
|
459
457
|
if (!is_dictionary && dist_code > 0) {
|
460
458
|
dist_cache[3] = dist_cache[2];
|
461
459
|
dist_cache[2] = dist_cache[1];
|
462
460
|
dist_cache[1] = dist_cache[0];
|
463
|
-
dist_cache[0] = distance;
|
461
|
+
dist_cache[0] = static_cast<int>(distance);
|
464
462
|
}
|
465
463
|
|
466
464
|
*num_literals += insert_length;
|
467
|
-
insert_length = 0;
|
468
465
|
pos += copy_length;
|
469
466
|
}
|
470
|
-
*last_insert_len +=
|
471
|
-
|
467
|
+
*last_insert_len += num_bytes - pos;
|
468
|
+
}
|
469
|
+
|
470
|
+
static void ZopfliIterate(size_t num_bytes,
|
471
|
+
size_t position,
|
472
|
+
const uint8_t* ringbuffer,
|
473
|
+
size_t ringbuffer_mask,
|
474
|
+
const size_t max_backward_limit,
|
475
|
+
const int* dist_cache,
|
476
|
+
const ZopfliCostModel& model,
|
477
|
+
const std::vector<uint32_t>& num_matches,
|
478
|
+
const std::vector<BackwardMatch>& matches,
|
479
|
+
ZopfliNode* nodes,
|
480
|
+
std::vector<uint32_t>* path) {
|
481
|
+
nodes[0].length = 0;
|
482
|
+
nodes[0].cost = 0;
|
483
|
+
StartPosQueue queue(3);
|
484
|
+
size_t cur_match_pos = 0;
|
485
|
+
for (size_t i = 0; i + 3 < num_bytes; i++) {
|
486
|
+
UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask,
|
487
|
+
max_backward_limit, dist_cache, num_matches[i],
|
488
|
+
&matches[cur_match_pos], &model, &queue, &nodes[0]);
|
489
|
+
cur_match_pos += num_matches[i];
|
490
|
+
// The zopflification can be too slow in case of very long lengths, so in
|
491
|
+
// such case skip it all, it does not cost a lot of compression ratio.
|
492
|
+
if (num_matches[i] == 1 &&
|
493
|
+
matches[cur_match_pos - 1].length() > kMaxZopfliLen) {
|
494
|
+
i += matches[cur_match_pos - 1].length() - 1;
|
495
|
+
queue.Clear();
|
496
|
+
}
|
497
|
+
}
|
498
|
+
ComputeShortestPathFromNodes(num_bytes, &nodes[0], path);
|
499
|
+
}
|
500
|
+
|
501
|
+
|
502
|
+
void ZopfliComputeShortestPath(size_t num_bytes,
|
503
|
+
size_t position,
|
504
|
+
const uint8_t* ringbuffer,
|
505
|
+
size_t ringbuffer_mask,
|
506
|
+
const size_t max_backward_limit,
|
507
|
+
const int* dist_cache,
|
508
|
+
Hashers::H10* hasher,
|
509
|
+
ZopfliNode* nodes,
|
510
|
+
std::vector<uint32_t>* path) {
|
511
|
+
nodes[0].length = 0;
|
512
|
+
nodes[0].cost = 0;
|
513
|
+
ZopfliCostModel* model = new ZopfliCostModel;
|
514
|
+
model->SetFromLiteralCosts(num_bytes, position,
|
515
|
+
ringbuffer, ringbuffer_mask);
|
516
|
+
StartPosQueue queue(3);
|
517
|
+
BackwardMatch matches[Hashers::H10::kMaxNumMatches];
|
518
|
+
for (size_t i = 0; i + 3 < num_bytes; i++) {
|
519
|
+
const size_t max_distance = std::min(position + i, max_backward_limit);
|
520
|
+
size_t num_matches = hasher->FindAllMatches(
|
521
|
+
ringbuffer, ringbuffer_mask, position + i, num_bytes - i, max_distance,
|
522
|
+
matches);
|
523
|
+
if (num_matches > 0 &&
|
524
|
+
matches[num_matches - 1].length() > kMaxZopfliLen) {
|
525
|
+
matches[0] = matches[num_matches - 1];
|
526
|
+
num_matches = 1;
|
527
|
+
}
|
528
|
+
UpdateNodes(num_bytes, position, i, ringbuffer, ringbuffer_mask,
|
529
|
+
max_backward_limit, dist_cache, num_matches, matches,
|
530
|
+
model, &queue, nodes);
|
531
|
+
if (num_matches == 1 && matches[0].length() > kMaxZopfliLen) {
|
532
|
+
for (size_t j = 1; j < matches[0].length() && i + 4 < num_bytes; ++j) {
|
533
|
+
++i;
|
534
|
+
if (matches[0].length() - j < 64 &&
|
535
|
+
num_bytes - i >= kMaxTreeCompLength) {
|
536
|
+
hasher->Store(ringbuffer, ringbuffer_mask, position + i);
|
537
|
+
}
|
538
|
+
}
|
539
|
+
queue.Clear();
|
540
|
+
}
|
541
|
+
}
|
542
|
+
delete model;
|
543
|
+
ComputeShortestPathFromNodes(num_bytes, nodes, path);
|
472
544
|
}
|
473
545
|
|
474
546
|
template<typename Hasher>
|
475
547
|
void CreateBackwardReferences(size_t num_bytes,
|
476
548
|
size_t position,
|
549
|
+
bool is_last,
|
477
550
|
const uint8_t* ringbuffer,
|
478
551
|
size_t ringbuffer_mask,
|
479
|
-
const size_t max_backward_limit,
|
480
552
|
const int quality,
|
553
|
+
const int lgwin,
|
481
554
|
Hasher* hasher,
|
482
555
|
int* dist_cache,
|
483
|
-
|
556
|
+
size_t* last_insert_len,
|
484
557
|
Command* commands,
|
485
558
|
size_t* num_commands,
|
486
|
-
|
559
|
+
size_t* num_literals) {
|
560
|
+
// Set maximum distance, see section 9.1. of the spec.
|
561
|
+
const size_t max_backward_limit = (1 << lgwin) - 16;
|
562
|
+
|
563
|
+
// Choose which init method is faster.
|
564
|
+
// memset is about 100 times faster than hasher->InitForData().
|
565
|
+
const size_t kMaxBytesForPartialHashInit = Hasher::kHashMapSize >> 7;
|
566
|
+
if (position == 0 && is_last && num_bytes <= kMaxBytesForPartialHashInit) {
|
567
|
+
hasher->InitForData(ringbuffer, num_bytes);
|
568
|
+
} else {
|
569
|
+
hasher->Init();
|
570
|
+
}
|
487
571
|
if (num_bytes >= 3 && position >= 3) {
|
488
572
|
// Prepare the hashes for three last bytes of the last write.
|
489
573
|
// These could not be calculated before, since they require knowledge
|
@@ -496,7 +580,7 @@ void CreateBackwardReferences(size_t num_bytes,
|
|
496
580
|
static_cast<uint32_t>(position - 1));
|
497
581
|
}
|
498
582
|
const Command * const orig_commands = commands;
|
499
|
-
|
583
|
+
size_t insert_length = *last_insert_len;
|
500
584
|
size_t i = position & ringbuffer_mask;
|
501
585
|
const size_t i_diff = position - i;
|
502
586
|
const size_t i_end = i + num_bytes;
|
@@ -506,15 +590,14 @@ void CreateBackwardReferences(size_t num_bytes,
|
|
506
590
|
size_t apply_random_heuristics = i + random_heuristics_window_size;
|
507
591
|
|
508
592
|
// Minimum score to accept a backward reference.
|
509
|
-
const
|
593
|
+
const double kMinScore = 4.0;
|
510
594
|
|
511
595
|
while (i + Hasher::kHashTypeLength - 1 < i_end) {
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
int best_dist = 0;
|
596
|
+
size_t max_length = i_end - i;
|
597
|
+
size_t max_distance = std::min(i + i_diff, max_backward_limit);
|
598
|
+
size_t best_len = 0;
|
599
|
+
size_t best_len_code = 0;
|
600
|
+
size_t best_dist = 0;
|
518
601
|
double best_score = kMinScore;
|
519
602
|
bool match_found = hasher->FindLongestMatch(
|
520
603
|
ringbuffer, ringbuffer_mask,
|
@@ -525,13 +608,12 @@ void CreateBackwardReferences(size_t num_bytes,
|
|
525
608
|
int delayed_backward_references_in_row = 0;
|
526
609
|
for (;;) {
|
527
610
|
--max_length;
|
528
|
-
|
529
|
-
|
530
|
-
|
611
|
+
size_t best_len_2 =
|
612
|
+
quality < 5 ? std::min(best_len - 1, max_length) : 0;
|
613
|
+
size_t best_len_code_2 = 0;
|
614
|
+
size_t best_dist_2 = 0;
|
531
615
|
double best_score_2 = kMinScore;
|
532
|
-
max_distance =
|
533
|
-
static_cast<int>(std::min(i + i_diff + 1, max_backward_limit));
|
534
|
-
hasher->Store(ringbuffer + i, static_cast<uint32_t>(i + i_diff));
|
616
|
+
max_distance = std::min(i + i_diff + 1, max_backward_limit);
|
535
617
|
match_found = hasher->FindLongestMatch(
|
536
618
|
ringbuffer, ringbuffer_mask,
|
537
619
|
dist_cache, static_cast<uint32_t>(i + i_diff + 1),
|
@@ -555,15 +637,15 @@ void CreateBackwardReferences(size_t num_bytes,
|
|
555
637
|
}
|
556
638
|
apply_random_heuristics =
|
557
639
|
i + 2 * best_len + random_heuristics_window_size;
|
558
|
-
max_distance =
|
640
|
+
max_distance = std::min(i + i_diff, max_backward_limit);
|
559
641
|
// The first 16 codes are special shortcodes, and the minimum offset is 1.
|
560
|
-
|
642
|
+
size_t distance_code =
|
561
643
|
ComputeDistanceCode(best_dist, max_distance, quality, dist_cache);
|
562
644
|
if (best_dist <= max_distance && distance_code > 0) {
|
563
645
|
dist_cache[3] = dist_cache[2];
|
564
646
|
dist_cache[2] = dist_cache[1];
|
565
647
|
dist_cache[1] = dist_cache[0];
|
566
|
-
dist_cache[0] = best_dist;
|
648
|
+
dist_cache[0] = static_cast<int>(best_dist);
|
567
649
|
}
|
568
650
|
Command cmd(insert_length, best_len, best_len_code, distance_code);
|
569
651
|
*commands++ = cmd;
|
@@ -571,14 +653,13 @@ void CreateBackwardReferences(size_t num_bytes,
|
|
571
653
|
insert_length = 0;
|
572
654
|
// Put the hash keys into the table, if there are enough
|
573
655
|
// bytes left.
|
574
|
-
for (
|
656
|
+
for (size_t j = 2; j < best_len; ++j) {
|
575
657
|
hasher->Store(&ringbuffer[i + j],
|
576
658
|
static_cast<uint32_t>(i + i_diff + j));
|
577
659
|
}
|
578
660
|
i += best_len;
|
579
661
|
} else {
|
580
662
|
++insert_length;
|
581
|
-
hasher->Store(ringbuffer + i, static_cast<uint32_t>(i + i_diff));
|
582
663
|
++i;
|
583
664
|
// If we have not seen matches for a long time, we can skip some
|
584
665
|
// match lookups. Unsuccessful match lookups are very very expensive
|
@@ -608,76 +689,91 @@ void CreateBackwardReferences(size_t num_bytes,
|
|
608
689
|
}
|
609
690
|
}
|
610
691
|
}
|
611
|
-
insert_length +=
|
692
|
+
insert_length += i_end - i;
|
612
693
|
*last_insert_len = insert_length;
|
613
|
-
*num_commands += commands - orig_commands;
|
694
|
+
*num_commands += static_cast<size_t>(commands - orig_commands);
|
614
695
|
}
|
615
696
|
|
616
697
|
void CreateBackwardReferences(size_t num_bytes,
|
617
698
|
size_t position,
|
699
|
+
bool is_last,
|
618
700
|
const uint8_t* ringbuffer,
|
619
701
|
size_t ringbuffer_mask,
|
620
|
-
const size_t max_backward_limit,
|
621
702
|
const int quality,
|
703
|
+
const int lgwin,
|
622
704
|
Hashers* hashers,
|
623
705
|
int hash_type,
|
624
706
|
int* dist_cache,
|
625
|
-
|
707
|
+
size_t* last_insert_len,
|
626
708
|
Command* commands,
|
627
709
|
size_t* num_commands,
|
628
|
-
|
710
|
+
size_t* num_literals) {
|
629
711
|
bool zopflify = quality > 9;
|
630
712
|
if (zopflify) {
|
631
|
-
Hashers::
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
|
713
|
+
Hashers::H10* hasher = hashers->hash_h10;
|
714
|
+
hasher->Init(lgwin, position, num_bytes, is_last);
|
715
|
+
hasher->StitchToPreviousBlock(num_bytes, position,
|
716
|
+
ringbuffer, ringbuffer_mask);
|
717
|
+
// Set maximum distance, see section 9.1. of the spec.
|
718
|
+
const size_t max_backward_limit = (1 << lgwin) - 16;
|
719
|
+
if (quality == 10) {
|
720
|
+
std::vector<ZopfliNode> nodes(num_bytes + 1);
|
721
|
+
std::vector<uint32_t> path;
|
722
|
+
ZopfliComputeShortestPath(num_bytes, position,
|
723
|
+
ringbuffer, ringbuffer_mask,
|
724
|
+
max_backward_limit, dist_cache, hasher,
|
725
|
+
&nodes[0], &path);
|
726
|
+
ZopfliCreateCommands(num_bytes, position, max_backward_limit, path,
|
727
|
+
&nodes[0], dist_cache, last_insert_len, commands,
|
728
|
+
num_literals);
|
729
|
+
*num_commands += path.size();
|
730
|
+
return;
|
642
731
|
}
|
643
|
-
std::vector<
|
644
|
-
std::vector<BackwardMatch> matches(
|
732
|
+
std::vector<uint32_t> num_matches(num_bytes);
|
733
|
+
std::vector<BackwardMatch> matches(4 * num_bytes);
|
645
734
|
size_t cur_match_pos = 0;
|
646
735
|
for (size_t i = 0; i + 3 < num_bytes; ++i) {
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
736
|
+
size_t max_distance = std::min(position + i, max_backward_limit);
|
737
|
+
size_t max_length = num_bytes - i;
|
738
|
+
// Ensure that we have enough free slots.
|
739
|
+
if (matches.size() < cur_match_pos + Hashers::H10::kMaxNumMatches) {
|
740
|
+
matches.resize(cur_match_pos + Hashers::H10::kMaxNumMatches);
|
741
|
+
}
|
742
|
+
size_t num_found_matches = hasher->FindAllMatches(
|
743
|
+
ringbuffer, ringbuffer_mask, position + i, max_length, max_distance,
|
744
|
+
&matches[cur_match_pos]);
|
745
|
+
const size_t cur_match_end = cur_match_pos + num_found_matches;
|
746
|
+
for (size_t j = cur_match_pos; j + 1 < cur_match_end; ++j) {
|
747
|
+
assert(matches[j].length() < matches[j + 1].length());
|
748
|
+
assert(matches[j].distance > max_distance ||
|
749
|
+
matches[j].distance <= matches[j + 1].distance);
|
653
750
|
}
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
&num_matches[i], &matches[cur_match_pos]);
|
658
|
-
hasher->Store(&ringbuffer[(position + i) & ringbuffer_mask],
|
659
|
-
static_cast<uint32_t>(position + i));
|
660
|
-
cur_match_pos += num_matches[i];
|
661
|
-
if (num_matches[i] == 1) {
|
662
|
-
const int match_len = matches[cur_match_pos - 1].length();
|
751
|
+
num_matches[i] = static_cast<uint32_t>(num_found_matches);
|
752
|
+
if (num_found_matches > 0) {
|
753
|
+
const size_t match_len = matches[cur_match_end - 1].length();
|
663
754
|
if (match_len > kMaxZopfliLen) {
|
664
|
-
|
755
|
+
matches[cur_match_pos++] = matches[cur_match_end - 1];
|
756
|
+
num_matches[i] = 1;
|
757
|
+
for (size_t j = 1; j < match_len; ++j) {
|
665
758
|
++i;
|
666
|
-
|
667
|
-
|
759
|
+
if (match_len - j < 64 && num_bytes - i >= kMaxTreeCompLength) {
|
760
|
+
hasher->Store(ringbuffer, ringbuffer_mask, position + i);
|
761
|
+
}
|
668
762
|
num_matches[i] = 0;
|
669
763
|
}
|
764
|
+
} else {
|
765
|
+
cur_match_pos = cur_match_end;
|
670
766
|
}
|
671
767
|
}
|
672
768
|
}
|
673
|
-
|
674
|
-
|
769
|
+
size_t orig_num_literals = *num_literals;
|
770
|
+
size_t orig_last_insert_len = *last_insert_len;
|
675
771
|
int orig_dist_cache[4] = {
|
676
772
|
dist_cache[0], dist_cache[1], dist_cache[2], dist_cache[3]
|
677
773
|
};
|
678
774
|
size_t orig_num_commands = *num_commands;
|
679
|
-
static const
|
680
|
-
for (
|
775
|
+
static const size_t kIterations = 2;
|
776
|
+
for (size_t i = 0; i < kIterations; i++) {
|
681
777
|
ZopfliCostModel model;
|
682
778
|
if (i == 0) {
|
683
779
|
model.SetFromLiteralCosts(num_bytes, position,
|
@@ -692,67 +788,67 @@ void CreateBackwardReferences(size_t num_bytes,
|
|
692
788
|
*num_literals = orig_num_literals;
|
693
789
|
*last_insert_len = orig_last_insert_len;
|
694
790
|
memcpy(dist_cache, orig_dist_cache, 4 * sizeof(dist_cache[0]));
|
791
|
+
std::vector<ZopfliNode> nodes(num_bytes + 1);
|
792
|
+
std::vector<uint32_t> path;
|
695
793
|
ZopfliIterate(num_bytes, position, ringbuffer, ringbuffer_mask,
|
696
|
-
max_backward_limit, model, num_matches, matches,
|
697
|
-
|
794
|
+
max_backward_limit, dist_cache, model, num_matches, matches,
|
795
|
+
&nodes[0], &path);
|
796
|
+
ZopfliCreateCommands(num_bytes, position, max_backward_limit, path,
|
797
|
+
&nodes[0], dist_cache, last_insert_len, commands,
|
798
|
+
num_literals);
|
799
|
+
*num_commands += path.size();
|
698
800
|
}
|
699
801
|
return;
|
700
802
|
}
|
701
803
|
|
702
804
|
switch (hash_type) {
|
703
|
-
case 1:
|
704
|
-
CreateBackwardReferences<Hashers::H1>(
|
705
|
-
num_bytes, position, ringbuffer, ringbuffer_mask, max_backward_limit,
|
706
|
-
quality, hashers->hash_h1, dist_cache, last_insert_len,
|
707
|
-
commands, num_commands, num_literals);
|
708
|
-
break;
|
709
805
|
case 2:
|
710
806
|
CreateBackwardReferences<Hashers::H2>(
|
711
|
-
num_bytes, position, ringbuffer, ringbuffer_mask,
|
712
|
-
quality, hashers->hash_h2, dist_cache,
|
713
|
-
commands, num_commands, num_literals);
|
807
|
+
num_bytes, position, is_last, ringbuffer, ringbuffer_mask,
|
808
|
+
quality, lgwin, hashers->hash_h2, dist_cache,
|
809
|
+
last_insert_len, commands, num_commands, num_literals);
|
714
810
|
break;
|
715
811
|
case 3:
|
716
812
|
CreateBackwardReferences<Hashers::H3>(
|
717
|
-
num_bytes, position, ringbuffer, ringbuffer_mask,
|
718
|
-
quality, hashers->hash_h3, dist_cache,
|
719
|
-
commands, num_commands, num_literals);
|
813
|
+
num_bytes, position, is_last, ringbuffer, ringbuffer_mask,
|
814
|
+
quality, lgwin, hashers->hash_h3, dist_cache,
|
815
|
+
last_insert_len, commands, num_commands, num_literals);
|
720
816
|
break;
|
721
817
|
case 4:
|
722
818
|
CreateBackwardReferences<Hashers::H4>(
|
723
|
-
num_bytes, position, ringbuffer, ringbuffer_mask,
|
724
|
-
quality, hashers->hash_h4, dist_cache,
|
725
|
-
commands, num_commands, num_literals);
|
819
|
+
num_bytes, position, is_last, ringbuffer, ringbuffer_mask,
|
820
|
+
quality, lgwin, hashers->hash_h4, dist_cache,
|
821
|
+
last_insert_len, commands, num_commands, num_literals);
|
726
822
|
break;
|
727
823
|
case 5:
|
728
824
|
CreateBackwardReferences<Hashers::H5>(
|
729
|
-
num_bytes, position, ringbuffer, ringbuffer_mask,
|
730
|
-
quality, hashers->hash_h5, dist_cache,
|
731
|
-
commands, num_commands, num_literals);
|
825
|
+
num_bytes, position, is_last, ringbuffer, ringbuffer_mask,
|
826
|
+
quality, lgwin, hashers->hash_h5, dist_cache,
|
827
|
+
last_insert_len, commands, num_commands, num_literals);
|
732
828
|
break;
|
733
829
|
case 6:
|
734
830
|
CreateBackwardReferences<Hashers::H6>(
|
735
|
-
num_bytes, position, ringbuffer, ringbuffer_mask,
|
736
|
-
quality, hashers->hash_h6, dist_cache,
|
737
|
-
commands, num_commands, num_literals);
|
831
|
+
num_bytes, position, is_last, ringbuffer, ringbuffer_mask,
|
832
|
+
quality, lgwin, hashers->hash_h6, dist_cache,
|
833
|
+
last_insert_len, commands, num_commands, num_literals);
|
738
834
|
break;
|
739
835
|
case 7:
|
740
836
|
CreateBackwardReferences<Hashers::H7>(
|
741
|
-
num_bytes, position, ringbuffer, ringbuffer_mask,
|
742
|
-
quality, hashers->hash_h7, dist_cache,
|
743
|
-
commands, num_commands, num_literals);
|
837
|
+
num_bytes, position, is_last, ringbuffer, ringbuffer_mask,
|
838
|
+
quality, lgwin, hashers->hash_h7, dist_cache,
|
839
|
+
last_insert_len, commands, num_commands, num_literals);
|
744
840
|
break;
|
745
841
|
case 8:
|
746
842
|
CreateBackwardReferences<Hashers::H8>(
|
747
|
-
num_bytes, position, ringbuffer, ringbuffer_mask,
|
748
|
-
quality, hashers->hash_h8, dist_cache,
|
749
|
-
commands, num_commands, num_literals);
|
843
|
+
num_bytes, position, is_last, ringbuffer, ringbuffer_mask,
|
844
|
+
quality, lgwin, hashers->hash_h8, dist_cache,
|
845
|
+
last_insert_len, commands, num_commands, num_literals);
|
750
846
|
break;
|
751
847
|
case 9:
|
752
848
|
CreateBackwardReferences<Hashers::H9>(
|
753
|
-
num_bytes, position, ringbuffer, ringbuffer_mask,
|
754
|
-
quality, hashers->hash_h9, dist_cache,
|
755
|
-
commands, num_commands, num_literals);
|
849
|
+
num_bytes, position, is_last, ringbuffer, ringbuffer_mask,
|
850
|
+
quality, lgwin, hashers->hash_h9, dist_cache,
|
851
|
+
last_insert_len, commands, num_commands, num_literals);
|
756
852
|
break;
|
757
853
|
default:
|
758
854
|
break;
|