brotli 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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;
|