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