blocksplitter.c revision 1.1.1.1 1 1.1 tls /*
2 1.1 tls Copyright 2011 Google Inc. All Rights Reserved.
3 1.1 tls
4 1.1 tls Licensed under the Apache License, Version 2.0 (the "License");
5 1.1 tls you may not use this file except in compliance with the License.
6 1.1 tls You may obtain a copy of the License at
7 1.1 tls
8 1.1 tls http://www.apache.org/licenses/LICENSE-2.0
9 1.1 tls
10 1.1 tls Unless required by applicable law or agreed to in writing, software
11 1.1 tls distributed under the License is distributed on an "AS IS" BASIS,
12 1.1 tls WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 1.1 tls See the License for the specific language governing permissions and
14 1.1 tls limitations under the License.
15 1.1 tls
16 1.1 tls Author: lode.vandevenne (at) gmail.com (Lode Vandevenne)
17 1.1 tls Author: jyrki.alakuijala (at) gmail.com (Jyrki Alakuijala)
18 1.1 tls */
19 1.1 tls
20 1.1 tls #include "blocksplitter.h"
21 1.1 tls
22 1.1 tls #include <assert.h>
23 1.1 tls #include <stdio.h>
24 1.1 tls #include <stdlib.h>
25 1.1 tls
26 1.1 tls #include "deflate.h"
27 1.1 tls #include "lz77.h"
28 1.1 tls #include "squeeze.h"
29 1.1 tls #include "tree.h"
30 1.1 tls #include "util.h"
31 1.1 tls
32 1.1 tls /*
33 1.1 tls The "f" for the FindMinimum function below.
34 1.1 tls i: the current parameter of f(i)
35 1.1 tls context: for your implementation
36 1.1 tls */
37 1.1 tls typedef double FindMinimumFun(size_t i, void* context);
38 1.1 tls
39 1.1 tls /*
40 1.1 tls Finds minimum of function f(i) where is is of type size_t, f(i) is of type
41 1.1 tls double, i is in range start-end (excluding end).
42 1.1 tls */
43 1.1 tls static size_t FindMinimum(FindMinimumFun f, void* context,
44 1.1 tls size_t start, size_t end) {
45 1.1 tls if (end - start < 1024) {
46 1.1 tls double best = ZOPFLI_LARGE_FLOAT;
47 1.1 tls size_t result = start;
48 1.1 tls size_t i;
49 1.1 tls for (i = start; i < end; i++) {
50 1.1 tls double v = f(i, context);
51 1.1 tls if (v < best) {
52 1.1 tls best = v;
53 1.1 tls result = i;
54 1.1 tls }
55 1.1 tls }
56 1.1 tls return result;
57 1.1 tls } else {
58 1.1 tls /* Try to find minimum faster by recursively checking multiple points. */
59 1.1 tls #define NUM 9 /* Good value: 9. */
60 1.1 tls size_t i;
61 1.1 tls size_t p[NUM];
62 1.1 tls double vp[NUM];
63 1.1 tls size_t besti;
64 1.1 tls double best;
65 1.1 tls double lastbest = ZOPFLI_LARGE_FLOAT;
66 1.1 tls size_t pos = start;
67 1.1 tls
68 1.1 tls for (;;) {
69 1.1 tls if (end - start <= NUM) break;
70 1.1 tls
71 1.1 tls for (i = 0; i < NUM; i++) {
72 1.1 tls p[i] = start + (i + 1) * ((end - start) / (NUM + 1));
73 1.1 tls vp[i] = f(p[i], context);
74 1.1 tls }
75 1.1 tls besti = 0;
76 1.1 tls best = vp[0];
77 1.1 tls for (i = 1; i < NUM; i++) {
78 1.1 tls if (vp[i] < best) {
79 1.1 tls best = vp[i];
80 1.1 tls besti = i;
81 1.1 tls }
82 1.1 tls }
83 1.1 tls if (best > lastbest) break;
84 1.1 tls
85 1.1 tls start = besti == 0 ? start : p[besti - 1];
86 1.1 tls end = besti == NUM - 1 ? end : p[besti + 1];
87 1.1 tls
88 1.1 tls pos = p[besti];
89 1.1 tls lastbest = best;
90 1.1 tls }
91 1.1 tls return pos;
92 1.1 tls #undef NUM
93 1.1 tls }
94 1.1 tls }
95 1.1 tls
96 1.1 tls /*
97 1.1 tls Returns estimated cost of a block in bits. It includes the size to encode the
98 1.1 tls tree and the size to encode all literal, length and distance symbols and their
99 1.1 tls extra bits.
100 1.1 tls
101 1.1 tls litlens: lz77 lit/lengths
102 1.1 tls dists: ll77 distances
103 1.1 tls lstart: start of block
104 1.1 tls lend: end of block (not inclusive)
105 1.1 tls */
106 1.1 tls static double EstimateCost(const unsigned short* litlens,
107 1.1 tls const unsigned short* dists,
108 1.1 tls size_t lstart, size_t lend) {
109 1.1 tls return ZopfliCalculateBlockSize(litlens, dists, lstart, lend, 2);
110 1.1 tls }
111 1.1 tls
112 1.1 tls typedef struct SplitCostContext {
113 1.1 tls const unsigned short* litlens;
114 1.1 tls const unsigned short* dists;
115 1.1 tls size_t llsize;
116 1.1 tls size_t start;
117 1.1 tls size_t end;
118 1.1 tls } SplitCostContext;
119 1.1 tls
120 1.1 tls
121 1.1 tls /*
122 1.1 tls Gets the cost which is the sum of the cost of the left and the right section
123 1.1 tls of the data.
124 1.1 tls type: FindMinimumFun
125 1.1 tls */
126 1.1 tls static double SplitCost(size_t i, void* context) {
127 1.1 tls SplitCostContext* c = (SplitCostContext*)context;
128 1.1 tls return EstimateCost(c->litlens, c->dists, c->start, i) +
129 1.1 tls EstimateCost(c->litlens, c->dists, i, c->end);
130 1.1 tls }
131 1.1 tls
132 1.1 tls static void AddSorted(size_t value, size_t** out, size_t* outsize) {
133 1.1 tls size_t i;
134 1.1 tls ZOPFLI_APPEND_DATA(value, out, outsize);
135 1.1 tls if (*outsize > 0) {
136 1.1 tls for (i = 0; i < *outsize - 1; i++) {
137 1.1 tls if ((*out)[i] > value) {
138 1.1 tls size_t j;
139 1.1 tls for (j = *outsize - 1; j > i; j--) {
140 1.1 tls (*out)[j] = (*out)[j - 1];
141 1.1 tls }
142 1.1 tls (*out)[i] = value;
143 1.1 tls break;
144 1.1 tls }
145 1.1 tls }
146 1.1 tls }
147 1.1 tls }
148 1.1 tls
149 1.1 tls /*
150 1.1 tls Prints the block split points as decimal and hex values in the terminal.
151 1.1 tls */
152 1.1 tls static void PrintBlockSplitPoints(const unsigned short* litlens,
153 1.1 tls const unsigned short* dists,
154 1.1 tls size_t llsize, const size_t* lz77splitpoints,
155 1.1 tls size_t nlz77points) {
156 1.1 tls size_t* splitpoints = 0;
157 1.1 tls size_t npoints = 0;
158 1.1 tls size_t i;
159 1.1 tls /* The input is given as lz77 indices, but we want to see the uncompressed
160 1.1 tls index values. */
161 1.1 tls size_t pos = 0;
162 1.1 tls if (nlz77points > 0) {
163 1.1 tls for (i = 0; i < llsize; i++) {
164 1.1 tls size_t length = dists[i] == 0 ? 1 : litlens[i];
165 1.1 tls if (lz77splitpoints[npoints] == i) {
166 1.1 tls ZOPFLI_APPEND_DATA(pos, &splitpoints, &npoints);
167 1.1 tls if (npoints == nlz77points) break;
168 1.1 tls }
169 1.1 tls pos += length;
170 1.1 tls }
171 1.1 tls }
172 1.1 tls assert(npoints == nlz77points);
173 1.1 tls
174 1.1 tls fprintf(stderr, "block split points: ");
175 1.1 tls for (i = 0; i < npoints; i++) {
176 1.1 tls fprintf(stderr, "%d ", (int)splitpoints[i]);
177 1.1 tls }
178 1.1 tls fprintf(stderr, "(hex:");
179 1.1 tls for (i = 0; i < npoints; i++) {
180 1.1 tls fprintf(stderr, " %x", (int)splitpoints[i]);
181 1.1 tls }
182 1.1 tls fprintf(stderr, ")\n");
183 1.1 tls
184 1.1 tls free(splitpoints);
185 1.1 tls }
186 1.1 tls
187 1.1 tls /*
188 1.1 tls Finds next block to try to split, the largest of the available ones.
189 1.1 tls The largest is chosen to make sure that if only a limited amount of blocks is
190 1.1 tls requested, their sizes are spread evenly.
191 1.1 tls llsize: the size of the LL77 data, which is the size of the done array here.
192 1.1 tls done: array indicating which blocks starting at that position are no longer
193 1.1 tls splittable (splitting them increases rather than decreases cost).
194 1.1 tls splitpoints: the splitpoints found so far.
195 1.1 tls npoints: the amount of splitpoints found so far.
196 1.1 tls lstart: output variable, giving start of block.
197 1.1 tls lend: output variable, giving end of block.
198 1.1 tls returns 1 if a block was found, 0 if no block found (all are done).
199 1.1 tls */
200 1.1 tls static int FindLargestSplittableBlock(
201 1.1 tls size_t llsize, const unsigned char* done,
202 1.1 tls const size_t* splitpoints, size_t npoints,
203 1.1 tls size_t* lstart, size_t* lend) {
204 1.1 tls size_t longest = 0;
205 1.1 tls int found = 0;
206 1.1 tls size_t i;
207 1.1 tls for (i = 0; i <= npoints; i++) {
208 1.1 tls size_t start = i == 0 ? 0 : splitpoints[i - 1];
209 1.1 tls size_t end = i == npoints ? llsize - 1 : splitpoints[i];
210 1.1 tls if (!done[start] && end - start > longest) {
211 1.1 tls *lstart = start;
212 1.1 tls *lend = end;
213 1.1 tls found = 1;
214 1.1 tls longest = end - start;
215 1.1 tls }
216 1.1 tls }
217 1.1 tls return found;
218 1.1 tls }
219 1.1 tls
220 1.1 tls void ZopfliBlockSplitLZ77(const ZopfliOptions* options,
221 1.1 tls const unsigned short* litlens,
222 1.1 tls const unsigned short* dists,
223 1.1 tls size_t llsize, size_t maxblocks,
224 1.1 tls size_t** splitpoints, size_t* npoints) {
225 1.1 tls size_t lstart, lend;
226 1.1 tls size_t i;
227 1.1 tls size_t llpos = 0;
228 1.1 tls size_t numblocks = 1;
229 1.1 tls unsigned char* done;
230 1.1 tls double splitcost, origcost;
231 1.1 tls
232 1.1 tls if (llsize < 10) return; /* This code fails on tiny files. */
233 1.1 tls
234 1.1 tls done = (unsigned char*)malloc(llsize);
235 1.1 tls if (!done) exit(-1); /* Allocation failed. */
236 1.1 tls for (i = 0; i < llsize; i++) done[i] = 0;
237 1.1 tls
238 1.1 tls lstart = 0;
239 1.1 tls lend = llsize;
240 1.1 tls for (;;) {
241 1.1 tls SplitCostContext c;
242 1.1 tls
243 1.1 tls if (maxblocks > 0 && numblocks >= maxblocks) {
244 1.1 tls break;
245 1.1 tls }
246 1.1 tls
247 1.1 tls c.litlens = litlens;
248 1.1 tls c.dists = dists;
249 1.1 tls c.llsize = llsize;
250 1.1 tls c.start = lstart;
251 1.1 tls c.end = lend;
252 1.1 tls assert(lstart < lend);
253 1.1 tls llpos = FindMinimum(SplitCost, &c, lstart + 1, lend);
254 1.1 tls
255 1.1 tls assert(llpos > lstart);
256 1.1 tls assert(llpos < lend);
257 1.1 tls
258 1.1 tls splitcost = EstimateCost(litlens, dists, lstart, llpos) +
259 1.1 tls EstimateCost(litlens, dists, llpos, lend);
260 1.1 tls origcost = EstimateCost(litlens, dists, lstart, lend);
261 1.1 tls
262 1.1 tls if (splitcost > origcost || llpos == lstart + 1 || llpos == lend) {
263 1.1 tls done[lstart] = 1;
264 1.1 tls } else {
265 1.1 tls AddSorted(llpos, splitpoints, npoints);
266 1.1 tls numblocks++;
267 1.1 tls }
268 1.1 tls
269 1.1 tls if (!FindLargestSplittableBlock(
270 1.1 tls llsize, done, *splitpoints, *npoints, &lstart, &lend)) {
271 1.1 tls break; /* No further split will probably reduce compression. */
272 1.1 tls }
273 1.1 tls
274 1.1 tls if (lend - lstart < 10) {
275 1.1 tls break;
276 1.1 tls }
277 1.1 tls }
278 1.1 tls
279 1.1 tls if (options->verbose) {
280 1.1 tls PrintBlockSplitPoints(litlens, dists, llsize, *splitpoints, *npoints);
281 1.1 tls }
282 1.1 tls
283 1.1 tls free(done);
284 1.1 tls }
285 1.1 tls
286 1.1 tls void ZopfliBlockSplit(const ZopfliOptions* options,
287 1.1 tls const unsigned char* in, size_t instart, size_t inend,
288 1.1 tls size_t maxblocks, size_t** splitpoints, size_t* npoints) {
289 1.1 tls size_t pos = 0;
290 1.1 tls size_t i;
291 1.1 tls ZopfliBlockState s;
292 1.1 tls size_t* lz77splitpoints = 0;
293 1.1 tls size_t nlz77points = 0;
294 1.1 tls ZopfliLZ77Store store;
295 1.1 tls
296 1.1 tls ZopfliInitLZ77Store(&store);
297 1.1 tls
298 1.1 tls s.options = options;
299 1.1 tls s.blockstart = instart;
300 1.1 tls s.blockend = inend;
301 1.1 tls #ifdef ZOPFLI_LONGEST_MATCH_CACHE
302 1.1 tls s.lmc = 0;
303 1.1 tls #endif
304 1.1 tls
305 1.1 tls *npoints = 0;
306 1.1 tls *splitpoints = 0;
307 1.1 tls
308 1.1 tls /* Unintuitively, Using a simple LZ77 method here instead of ZopfliLZ77Optimal
309 1.1 tls results in better blocks. */
310 1.1 tls ZopfliLZ77Greedy(&s, in, instart, inend, &store);
311 1.1 tls
312 1.1 tls ZopfliBlockSplitLZ77(options,
313 1.1 tls store.litlens, store.dists, store.size, maxblocks,
314 1.1 tls &lz77splitpoints, &nlz77points);
315 1.1 tls
316 1.1 tls /* Convert LZ77 positions to positions in the uncompressed input. */
317 1.1 tls pos = instart;
318 1.1 tls if (nlz77points > 0) {
319 1.1 tls for (i = 0; i < store.size; i++) {
320 1.1 tls size_t length = store.dists[i] == 0 ? 1 : store.litlens[i];
321 1.1 tls if (lz77splitpoints[*npoints] == i) {
322 1.1 tls ZOPFLI_APPEND_DATA(pos, splitpoints, npoints);
323 1.1 tls if (*npoints == nlz77points) break;
324 1.1 tls }
325 1.1 tls pos += length;
326 1.1 tls }
327 1.1 tls }
328 1.1 tls assert(*npoints == nlz77points);
329 1.1 tls
330 1.1 tls free(lz77splitpoints);
331 1.1 tls ZopfliCleanLZ77Store(&store);
332 1.1 tls }
333 1.1 tls
334 1.1 tls void ZopfliBlockSplitSimple(const unsigned char* in,
335 1.1 tls size_t instart, size_t inend,
336 1.1 tls size_t blocksize,
337 1.1 tls size_t** splitpoints, size_t* npoints) {
338 1.1 tls size_t i = instart;
339 1.1 tls while (i < inend) {
340 1.1 tls ZOPFLI_APPEND_DATA(i, splitpoints, npoints);
341 1.1 tls i += blocksize;
342 1.1 tls }
343 1.1 tls (void)in;
344 1.1 tls }
345