- bring back dataiterator_match for now (to be removed again)
[platform/upstream/libsolv.git] / src / fastlz.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #include <sys/types.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <assert.h>
13 #include <time.h>
14
15 #define BLOCK_SIZE (65536*1)
16 #if BLOCK_SIZE <= 65536
17 typedef __uint16_t Ref;
18 #else
19 typedef __uint32_t Ref;
20 #endif
21
22 /*
23    The format is tailored for fast decompression (i.e. only byte based),
24    and skewed to ASCII content (highest bit often not set):
25    
26    a 0LLLLLLL
27         - self-describing ASCII character hex L
28    b 100lllll <l+1 bytes>
29         - literal run of length l+1
30    c 101oolll <8o>
31         - back ref of length l+2, at offset -(o+1) (o < 1 << 10)
32    d 110lllll <8o>
33         - back ref of length l+2+8, at offset -(o+1) (o < 1 << 8)
34    e 1110llll <8o> <8o>
35         - back ref of length l+3, at offset -(o+1) (o < 1 << 16)
36   f1 1111llll <8l> <8o> <8o>
37         - back ref, length l+19 (l < 1<<12), offset -(o+1) (o < 1<<16)
38   f2 11110lll <8l> <8o> <8o>
39         - back ref, length l+19 (l < 1<<11), offset -(o+1) (o < 1<<16)
40    g 11111lll <8l> <8o> <8o> <8o>
41         - back ref, length l+5 (l < 1<<11), offset -(o+1) (o < 1<<24)
42
43    Generally for a literal of length L we need L+1 bytes, hence it is
44    better to encode also very short backrefs (2 chars) as backrefs if
45    their offset is small, as that only needs two bytes.  Except if we
46    already have a literal run, in that case it's better to append there,
47    instead of breaking it for a backref.  So given a potential backref
48    at offset O, length L the strategy is as follows:
49
50    L < 2 : encode as 1-literal
51    L == 2, O > 1024 : encode as 1-literal
52    L == 2, have already literals: encode as 1-literal
53    O = O - 1
54    L >= 2, L <= 9, O < 1024                            : encode as c
55    L >= 10, L <= 41, O < 256                           : encode as d
56    else we have either O >= 1024, or L >= 42:
57    L < 3 : encode as 1-literal
58    L >= 3, L <= 18, O < 65536                          : encode as e
59    L >= 19, L <= 4095+18, O < 65536                    : encode as f
60    else we have either L >= 4096+18 or O >= 65536.
61    O >= 65536: encode as 1-literal, too bad
62      (with the current block size this can't happen)
63    L >= 4096+18, so reduce to 4095+18                  : encode as f
64 */
65
66
67 unsigned int
68 compress_buf (const unsigned char *in, unsigned int in_len,
69               unsigned char *out, unsigned int out_len)
70 {
71   unsigned int oo = 0;          //out-offset
72   unsigned int io = 0;          //in-offset
73 #define HS (65536)
74   Ref htab[HS];
75   Ref hnext[BLOCK_SIZE];
76   memset (htab, -1, sizeof (htab));
77   memset (hnext, -1, sizeof (hnext));
78   unsigned int litofs = 0;
79   while (io + 2 < in_len)
80     {
81       /* Search for a match of the string starting at IN, we have at
82          least three characters.  */
83       unsigned int hval = in[io] | in[io + 1] << 8 | in[io + 2] << 16;
84       unsigned int try, mlen, mofs, tries;
85       hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
86       hval = hval & (HS - 1);
87       try = htab[hval];
88       hnext[io] = htab[hval];
89       htab[hval] = io;
90       mlen = 0;
91       mofs = 0;
92
93       for (tries = 0; try != -1 && tries < 12; tries++)
94         {
95           if (try < io
96               && in[try] == in[io] && in[try + 1] == in[io + 1])
97             {
98               mlen = 2;
99               mofs = (io - try) - 1;
100               break;
101             }
102           try = hnext[try];
103         }
104       for (; try != -1 && tries < 12; tries++)
105         {
106           //assert (mlen >= 2);
107           //assert (io + mlen < in_len);
108           /* Try a match starting from [io] with the strings at [try].
109              That's only sensible if TRY actually is before IO (can happen
110              with uninit hash table).  If we have a previous match already
111              we're only going to take the new one if it's longer, hence
112              check the potentially last character.  */
113           if (try < io && in[try + mlen] == in[io + mlen])
114             {
115               unsigned int this_len, this_ofs;
116               if (memcmp (in + try, in + io, mlen))
117                 goto no_match;
118               this_len = mlen + 1;
119               /* Now try extending the match by more characters.  */
120               for (;
121                    io + this_len < in_len
122                    && in[try + this_len] == in[io + this_len]; this_len++)
123                 ;
124 #if 0
125               unsigned int testi;
126               for (testi = 0; testi < this_len; testi++)
127                 assert (in[try + testi] == in[io + testi]);
128 #endif
129               this_ofs = (io - try) - 1;
130               /*if (this_ofs > 65535)
131                  goto no_match; */
132 #if 0
133               assert (this_len >= 2);
134               assert (this_len >= mlen);
135               assert (this_len > mlen || (this_len == mlen && this_ofs > mofs));
136 #endif
137               mlen = this_len, mofs = this_ofs;
138               /* If our match extends up to the end of input, no next
139                  match can become better.  This is not just an
140                  optimization, it establishes a loop invariant
141                  (io + mlen < in_len).  */
142               if (io + mlen >= in_len)
143                 goto match_done;
144             }
145         no_match:
146           try = hnext[try];
147           /*if (io - try - 1 >= 65536)
148             break;*/
149         }
150
151 match_done:
152       if (mlen)
153         {
154           //fprintf (stderr, "%d %d\n", mlen, mofs);
155           if (mlen == 2 && (litofs || mofs >= 1024))
156             mlen = 0;
157           /*else if (mofs >= 65536)
158             mlen = 0;*/
159           else if (mofs >= 65536)
160             {
161               if (mlen >= 2048 + 5)
162                 mlen = 2047 + 5;
163               else if (mlen < 5)
164                 mlen = 0;
165             }
166           else if (mlen < 3)
167             mlen = 0;
168           /*else if (mlen >= 4096 + 19)
169             mlen = 4095 + 19;*/
170           else if (mlen >= 2048 + 19)
171             mlen = 2047 + 19;
172           /* Skip this match if the next character would deliver a better one,
173              but only do this if we have the chance to really extend the
174              length (i.e. our current length isn't yet the (conservative)
175              maximum).  */
176           if (mlen && mlen < (2048 + 5) && io + 3 < in_len)
177             {
178               unsigned int hval =
179                 in[io + 1] | in[io + 2] << 8 | in[io + 3] << 16;
180               unsigned int try;
181               hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
182               hval = hval & (HS - 1);
183               try = htab[hval];
184               if (try < io + 1
185                   && in[try] == in[io + 1] && in[try + 1] == in[io + 2])
186                 {
187                   unsigned int this_len;
188                   this_len = 2;
189                   for (;
190                        io + 1 + this_len < in_len
191                        && in[try + this_len] == in[io + 1 + this_len];
192                        this_len++)
193                     ;
194                   if (this_len >= mlen)
195                     mlen = 0;
196                 }
197             }
198         }
199       if (!mlen)
200         {
201           if (!litofs)
202             litofs = io + 1;
203           io++;
204         }
205       else
206         {
207           if (litofs)
208             {
209               litofs--;
210               unsigned litlen = io - litofs;
211               //fprintf (stderr, "lit: %d\n", litlen);
212               while (litlen)
213                 {
214                   unsigned int easy_sz;
215                   /* Emit everything we can as self-describers.  As soon as
216                      we hit a byte we can't emit as such we're going to emit
217                      a length descriptor anyway, so we can as well include
218                      bytes < 0x80 which might follow afterwards in that run.  */
219                   for (easy_sz = 0;
220                        easy_sz < litlen && in[litofs + easy_sz] < 0x80;
221                        easy_sz++)
222                     ;
223                   if (easy_sz)
224                     {
225                       if (oo + easy_sz >= out_len)
226                         return 0;
227                       memcpy (out + oo, in + litofs, easy_sz);
228                       litofs += easy_sz;
229                       oo += easy_sz;
230                       litlen -= easy_sz;
231                       if (!litlen)
232                         break;
233                     }
234                   if (litlen <= 32)
235                     {
236                       if (oo + 1 + litlen >= out_len)
237                         return 0;
238                       out[oo++] = 0x80 | (litlen - 1);
239                       while (litlen--)
240                         out[oo++] = in[litofs++];
241                       break;
242                     }
243                   else
244                     {
245                       /* Literal length > 32, so chunk it.  */
246                       if (oo + 1 + 32 >= out_len)
247                         return 0;
248                       out[oo++] = 0x80 | 31;
249                       memcpy (out + oo, in + litofs, 32);
250                       oo += 32;
251                       litofs += 32;
252                       litlen -= 32;
253                     }
254                 }
255               litofs = 0;
256             }
257
258           //fprintf (stderr, "ref: %d @ %d\n", mlen, mofs);
259
260           if (mlen >= 2 && mlen <= 9 && mofs < 1024)
261             {
262               if (oo + 2 >= out_len)
263                 return 0;
264               out[oo++] = 0xa0 | ((mofs & 0x300) >> 5) | (mlen - 2);
265               out[oo++] = mofs & 0xff;
266             }
267           else if (mlen >= 10 && mlen <= 41 && mofs < 256)
268             {
269               if (oo + 2 >= out_len)
270                 return 0;
271               out[oo++] = 0xc0 | (mlen - 10);
272               out[oo++] = mofs;
273             }
274           else if (mofs >= 65536)
275             {
276               assert (mlen >= 5 && mlen < 2048 + 5);
277               if (oo + 5 >= out_len)
278                 return 0;
279               out[oo++] = 0xf8 | ((mlen - 5) >> 8);
280               out[oo++] = (mlen - 5) & 0xff;
281               out[oo++] = mofs & 0xff;
282               out[oo++] = (mofs >> 8) & 0xff;
283               out[oo++] = mofs >> 16;
284             }
285           else if (mlen >= 3 && mlen <= 18)
286             {
287               assert (mofs < 65536);
288               if (oo + 3 >= out_len)
289                 return 0;
290               out[oo++] = 0xe0 | (mlen - 3);
291               out[oo++] = mofs & 0xff;
292               out[oo++] = mofs >> 8;
293             }
294           else
295             {
296               assert (mlen >= 19 && mlen <= 4095 + 19 && mofs < 65536);
297               if (oo + 4 >= out_len)
298                 return 0;
299               out[oo++] = 0xf0 | ((mlen - 19) >> 8);
300               out[oo++] = (mlen - 19) & 0xff;
301               out[oo++] = mofs & 0xff;
302               out[oo++] = mofs >> 8;
303             }
304           /* Insert the hashes for the compressed run [io..io+mlen-1].
305              For [io] we have it already done at the start of the loop.
306              So it's from [io+1..io+mlen-1], and we need three chars per
307              hash, so the accessed characters will be [io+1..io+mlen-1+2],
308              ergo io+mlen+1 < in_len.  */
309           mlen--;
310           io++;
311           while (mlen--)
312             {
313               if (io + 2 < in_len)
314                 {
315                   unsigned int hval =
316                     in[io] | in[io + 1] << 8 | in[io + 2] << 16;
317                   hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
318                   hval = hval & (HS - 1);
319                   hnext[io] = htab[hval];
320                   htab[hval] = io;
321                 }
322               io++;
323             };
324         }
325     }
326   /* We might have some characters left.  */
327   if (io < in_len && !litofs)
328     litofs = io + 1;
329   io = in_len;
330   if (litofs)
331     {
332       litofs--;
333       unsigned litlen = io - litofs;
334       //fprintf (stderr, "lit: %d\n", litlen);
335       while (litlen)
336         {
337           unsigned int easy_sz;
338           /* Emit everything we can as self-describers.  As soon as we hit a
339              byte we can't emit as such we're going to emit a length
340              descriptor anyway, so we can as well include bytes < 0x80 which
341              might follow afterwards in that run.  */
342           for (easy_sz = 0; easy_sz < litlen && in[litofs + easy_sz] < 0x80;
343                easy_sz++)
344             ;
345           if (easy_sz)
346             {
347               if (oo + easy_sz >= out_len)
348                 return 0;
349               memcpy (out + oo, in + litofs, easy_sz);
350               litofs += easy_sz;
351               oo += easy_sz;
352               litlen -= easy_sz;
353               if (!litlen)
354                 break;
355             }
356           if (litlen <= 32)
357             {
358               if (oo + 1 + litlen >= out_len)
359                 return 0;
360               out[oo++] = 0x80 | (litlen - 1);
361               while (litlen--)
362                 out[oo++] = in[litofs++];
363               break;
364             }
365           else
366             {
367               /* Literal length > 32, so chunk it.  */
368               if (oo + 1 + 32 >= out_len)
369                 return 0;
370               out[oo++] = 0x80 | 31;
371               memcpy (out + oo, in + litofs, 32);
372               oo += 32;
373               litofs += 32;
374               litlen -= 32;
375             }
376         }
377       litofs = 0;
378     }
379   return oo;
380 }
381
382 unsigned int
383 unchecked_decompress_buf (const unsigned char *in, unsigned int in_len,
384                           unsigned char *out,
385                           unsigned int out_len __attribute__((unused)))
386 {
387   unsigned char *orig_out = out;
388   const unsigned char *in_end = in + in_len;
389   while (in < in_end)
390     {
391       unsigned int first = *in++;
392       int o;
393       switch (first >> 4)
394         {
395         default:
396           /* This default case can't happen, but GCCs VRP is not strong
397              enough to see this, so make this explicitely not fall to
398              the end of the switch, so that we don't have to initialize
399              o above.  */
400           continue;
401         case 0: case 1:
402         case 2: case 3:
403         case 4: case 5:
404         case 6: case 7:
405           //a 0LLLLLLL
406           //fprintf (stderr, "lit: 1\n");
407           *out++ = first;
408           continue;
409         case 8: case 9:
410           //b 100lllll <l+1 bytes>
411           {
412             unsigned int l = first & 31;
413             //fprintf (stderr, "lit: %d\n", l);
414             do
415               *out++ = *in++;
416             while (l--);
417             continue;
418           }
419         case 10: case 11:
420           //c 101oolll <8o>
421           {
422             o = first & (3 << 3);
423             o = (o << 5) | *in++;
424             first = (first & 7) + 2;
425             break;
426           }
427         case 12: case 13:
428           //d 110lllll <8o>
429           {
430             o = *in++;
431             first = (first & 31) + 10;
432             break;
433           }
434         case 14:
435           // e 1110llll <8o> <8o>
436           {
437             o = in[0] | (in[1] << 8);
438             in += 2;
439             first = first & 31;
440             first += 3;
441             break;
442           }
443         case 15:
444           //f1 1111llll <8o> <8o> <8l>
445           //f2 11110lll <8o> <8o> <8l>
446           // g 11111lll <8o> <8o> <8o> <8l>
447           {
448             first = first & 15;
449             if (first >= 8)
450               {
451                 first = (((first - 8) << 8) | in[0]) + 5;
452                 o = in[1] | (in[2] << 8) | (in[3] << 16);
453                 in += 4;
454               }
455             else
456               {
457                 first = ((first << 8) | in[0]) + 19;
458                 o = in[1] | (in[2] << 8);
459                 in += 3;
460               }
461             break;
462           }
463         }
464       //fprintf (stderr, "ref: %d @ %d\n", first, o);
465       o++;
466       o = -o;
467 #if 0
468       /* We know that first will not be zero, and this loop structure is
469          better optimizable.  */
470       do
471         {
472           *out = *(out - o);
473           out++;
474         }
475       while (--first);
476 #else
477       switch (first)
478         {
479           case 18: *out = *(out + o); out++;
480           case 17: *out = *(out + o); out++;
481           case 16: *out = *(out + o); out++;
482           case 15: *out = *(out + o); out++;
483           case 14: *out = *(out + o); out++;
484           case 13: *out = *(out + o); out++;
485           case 12: *out = *(out + o); out++;
486           case 11: *out = *(out + o); out++;
487           case 10: *out = *(out + o); out++;
488           case  9: *out = *(out + o); out++;
489           case  8: *out = *(out + o); out++;
490           case  7: *out = *(out + o); out++;
491           case  6: *out = *(out + o); out++;
492           case  5: *out = *(out + o); out++;
493           case  4: *out = *(out + o); out++;
494           case  3: *out = *(out + o); out++;
495           case  2: *out = *(out + o); out++;
496           case  1: *out = *(out + o); out++;
497           case  0: break;
498           default:
499             /* Duff duff :-) */
500             switch (first & 15)
501               {
502                 do
503                   {
504                     case  0: *out = *(out + o); out++;
505                     case 15: *out = *(out + o); out++;
506                     case 14: *out = *(out + o); out++;
507                     case 13: *out = *(out + o); out++;
508                     case 12: *out = *(out + o); out++;
509                     case 11: *out = *(out + o); out++;
510                     case 10: *out = *(out + o); out++;
511                     case  9: *out = *(out + o); out++;
512                     case  8: *out = *(out + o); out++;
513                     case  7: *out = *(out + o); out++;
514                     case  6: *out = *(out + o); out++;
515                     case  5: *out = *(out + o); out++;
516                     case  4: *out = *(out + o); out++;
517                     case  3: *out = *(out + o); out++;
518                     case  2: *out = *(out + o); out++;
519                     case  1: *out = *(out + o); out++;
520                   }
521                 while ((int)(first -= 16) > 0);
522               }
523             break;
524         }
525 #endif
526     }
527   return out - orig_out;
528 }
529
530 #ifdef STANDALONE
531
532 static void
533 transfer_file (FILE * from, FILE * to, int compress)
534 {
535   unsigned char inb[BLOCK_SIZE];
536   unsigned char outb[BLOCK_SIZE];
537   while (!feof (from) && !ferror (from))
538     {
539       unsigned int in_len, out_len;
540       if (compress)
541         {
542           in_len = fread (inb, 1, BLOCK_SIZE, from);
543           if (in_len)
544             {
545               unsigned char *b = outb;
546               out_len = compress_buf (inb, in_len, outb, sizeof (outb));
547               if (!out_len)
548                 b = inb, out_len = in_len;
549               if (fwrite (&out_len, sizeof (out_len), 1, to) != 1)
550                 {
551                   perror ("write size");
552                   exit (1);
553                 }
554               if (fwrite (b, out_len, 1, to) != 1)
555                 {
556                   perror ("write data");
557                   exit (1);
558                 }
559             }
560         }
561       else
562         {
563           if (fread (&in_len, sizeof (in_len), 1, from) != 1)
564             {
565               if (feof (from))
566                 return;
567               perror ("can't read size");
568               exit (1);
569             }
570           if (fread (inb, in_len, 1, from) != 1)
571             {
572               perror ("can't read data");
573               exit (1);
574             }
575           out_len =
576             unchecked_decompress_buf (inb, in_len, outb, sizeof (outb));
577           if (fwrite (outb, out_len, 1, to) != 1)
578             {
579               perror ("can't write output");
580               exit (1);
581             }
582         }
583     }
584 }
585
586 /* Just for benchmarking purposes.  */
587 static void
588 dumb_memcpy (void *dest, const void *src, unsigned int len)
589 {
590   char *d = dest;
591   const char *s = src;
592   while (len--)
593     *d++ = *s++;
594 }
595
596 static void
597 benchmark (FILE * from)
598 {
599   unsigned char inb[BLOCK_SIZE];
600   unsigned char outb[BLOCK_SIZE];
601   unsigned int in_len = fread (inb, 1, BLOCK_SIZE, from);
602   unsigned int out_len;
603   if (!in_len)
604     {
605       perror ("can't read from input");
606       exit (1);
607     }
608
609   unsigned int calib_loop;
610   unsigned int per_loop;
611   unsigned int i, j;
612   clock_t start, end;
613   float seconds;
614
615 #if 0
616   calib_loop = 1;
617   per_loop = 0;
618   start = clock ();
619   while ((clock () - start) < CLOCKS_PER_SEC / 4)
620     {
621       calib_loop *= 2;
622       for (i = 0; i < calib_loop; i++)
623         dumb_memcpy (outb, inb, in_len);
624       per_loop += calib_loop;
625     }
626
627   fprintf (stderr, "memcpy:\nCalibrated to %d iterations per loop\n",
628            per_loop);
629
630   start = clock ();
631   for (i = 0; i < 10; i++)
632     for (j = 0; j < per_loop; j++)
633       dumb_memcpy (outb, inb, in_len);
634   end = clock ();
635   seconds = (end - start) / (float) CLOCKS_PER_SEC;
636   fprintf (stderr, "%.2f seconds == %.2f MB/s\n", seconds,
637            ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
638 #endif
639
640   calib_loop = 1;
641   per_loop = 0;
642   start = clock ();
643   while ((clock () - start) < CLOCKS_PER_SEC / 4)
644     {
645       calib_loop *= 2;
646       for (i = 0; i < calib_loop; i++)
647         compress_buf (inb, in_len, outb, sizeof (outb));
648       per_loop += calib_loop;
649     }
650
651   fprintf (stderr, "compression:\nCalibrated to %d iterations per loop\n",
652            per_loop);
653
654   start = clock ();
655   for (i = 0; i < 10; i++)
656     for (j = 0; j < per_loop; j++)
657       compress_buf (inb, in_len, outb, sizeof (outb));
658   end = clock ();
659   seconds = (end - start) / (float) CLOCKS_PER_SEC;
660   fprintf (stderr, "%.2f seconds == %.2f MB/s\n", seconds,
661            ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
662
663   out_len = compress_buf (inb, in_len, outb, sizeof (outb));
664
665   calib_loop = 1;
666   per_loop = 0;
667   start = clock ();
668   while ((clock () - start) < CLOCKS_PER_SEC / 4)
669     {
670       calib_loop *= 2;
671       for (i = 0; i < calib_loop; i++)
672         unchecked_decompress_buf (outb, out_len, inb, sizeof (inb));
673       per_loop += calib_loop;
674     }
675
676   fprintf (stderr, "decompression:\nCalibrated to %d iterations per loop\n",
677            per_loop);
678
679   start = clock ();
680   for (i = 0; i < 10; i++)
681     for (j = 0; j < per_loop; j++)
682       unchecked_decompress_buf (outb, out_len, inb, sizeof (inb));
683   end = clock ();
684   seconds = (end - start) / (float) CLOCKS_PER_SEC;
685   fprintf (stderr, "%.2f seconds == %.2f MB/s\n", seconds,
686            ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
687 }
688
689 int
690 main (int argc, char *argv[])
691 {
692   int compress = 1;
693   if (argc > 1 && !strcmp (argv[1], "-d"))
694     compress = 0;
695   if (argc > 1 && !strcmp (argv[1], "-b"))
696     benchmark (stdin);
697   else
698     transfer_file (stdin, stdout, compress);
699   return 0;
700 }
701
702 #endif