2 * Copyright (c) 2007-2011, Novell Inc.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
11 * Paging and compression functions for the vertical repository data.
12 * Vertical data is grouped by key, normal data is grouped by solvable.
13 * This makes searching for a string in vertical data fast as there's
14 * no need to skip over data if keys we're not interested in.
16 * The vertical data is split into pages, each page is compressed with a fast
17 * compression algorithm. These pages are read in on demand, not recently used
18 * pages automatically get dropped.
21 #define _XOPEN_SOURCE 500
23 #include <sys/types.h>
38 #define BLOCK_SIZE (65536*1)
39 #if BLOCK_SIZE <= 65536
46 The format is tailored for fast decompression (i.e. only byte based),
47 and skewed to ASCII content (highest bit often not set):
50 - self-describing ASCII character hex L
51 b 100lllll <l+1 bytes>
52 - literal run of length l+1
54 - back ref of length l+2, at offset -(o+1) (o < 1 << 10)
56 - back ref of length l+2+8, at offset -(o+1) (o < 1 << 8)
58 - back ref of length l+3, at offset -(o+1) (o < 1 << 16)
59 f1 1111llll <8l> <8o> <8o>
60 - back ref, length l+19 (l < 1<<12), offset -(o+1) (o < 1<<16)
61 f2 11110lll <8l> <8o> <8o>
62 - back ref, length l+19 (l < 1<<11), offset -(o+1) (o < 1<<16)
63 g 11111lll <8l> <8o> <8o> <8o>
64 - back ref, length l+5 (l < 1<<11), offset -(o+1) (o < 1<<24)
66 Generally for a literal of length L we need L+1 bytes, hence it is
67 better to encode also very short backrefs (2 chars) as backrefs if
68 their offset is small, as that only needs two bytes. Except if we
69 already have a literal run, in that case it's better to append there,
70 instead of breaking it for a backref. So given a potential backref
71 at offset O, length L the strategy is as follows:
73 L < 2 : encode as 1-literal
74 L == 2, O > 1024 : encode as 1-literal
75 L == 2, have already literals: encode as 1-literal
77 L >= 2, L <= 9, O < 1024 : encode as c
78 L >= 10, L <= 41, O < 256 : encode as d
79 else we have either O >= 1024, or L >= 42:
80 L < 3 : encode as 1-literal
81 L >= 3, L <= 18, O < 65536 : encode as e
82 L >= 19, L <= 4095+18, O < 65536 : encode as f
83 else we have either L >= 4096+18 or O >= 65536.
84 O >= 65536: encode as 1-literal, too bad
85 (with the current block size this can't happen)
86 L >= 4096+18, so reduce to 4095+18 : encode as f
91 compress_buf(const unsigned char *in, unsigned int in_len,
92 unsigned char *out, unsigned int out_len)
94 unsigned int oo = 0; /* out-offset */
95 unsigned int io = 0; /* in-offset */
98 Ref hnext[BLOCK_SIZE];
99 unsigned int litofs = 0;
100 memset(htab, -1, sizeof (htab));
101 memset(hnext, -1, sizeof (hnext));
102 while (io + 2 < in_len)
104 /* Search for a match of the string starting at IN, we have at
105 least three characters. */
106 unsigned int hval = in[io] | in[io + 1] << 8 | in[io + 2] << 16;
107 unsigned int try, mlen, mofs, tries;
108 hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
109 hval = hval & (HS - 1);
111 hnext[io] = htab[hval];
116 for (tries = 0; try != -1 && tries < 12; tries++)
119 && in[try] == in[io] && in[try + 1] == in[io + 1])
122 mofs = (io - try) - 1;
127 for (; try != -1 && tries < 12; tries++)
129 /* assert(mlen >= 2); */
130 /* assert(io + mlen < in_len); */
131 /* Try a match starting from [io] with the strings at [try].
132 That's only sensible if TRY actually is before IO (can happen
133 with uninit hash table). If we have a previous match already
134 we're only going to take the new one if it's longer, hence
135 check the potentially last character. */
136 if (try < io && in[try + mlen] == in[io + mlen])
138 unsigned int this_len, this_ofs;
139 if (memcmp(in + try, in + io, mlen))
142 /* Now try extending the match by more characters. */
144 io + this_len < in_len
145 && in[try + this_len] == in[io + this_len]; this_len++)
149 for (testi = 0; testi < this_len; testi++)
150 assert(in[try + testi] == in[io + testi]);
152 this_ofs = (io - try) - 1;
153 /*if (this_ofs > 65535)
156 assert(this_len >= 2);
157 assert(this_len >= mlen);
158 assert(this_len > mlen || (this_len == mlen && this_ofs > mofs));
160 mlen = this_len, mofs = this_ofs;
161 /* If our match extends up to the end of input, no next
162 match can become better. This is not just an
163 optimization, it establishes a loop invariant
164 (io + mlen < in_len). */
165 if (io + mlen >= in_len)
170 /*if (io - try - 1 >= 65536)
177 /*fprintf(stderr, "%d %d\n", mlen, mofs);*/
178 if (mlen == 2 && (litofs || mofs >= 1024))
180 /*else if (mofs >= 65536)
182 else if (mofs >= 65536)
184 if (mlen >= 2048 + 5)
191 /*else if (mlen >= 4096 + 19)
193 else if (mlen >= 2048 + 19)
195 /* Skip this match if the next character would deliver a better one,
196 but only do this if we have the chance to really extend the
197 length (i.e. our current length isn't yet the (conservative)
199 if (mlen && mlen < (2048 + 5) && io + 3 < in_len)
202 in[io + 1] | in[io + 2] << 8 | in[io + 3] << 16;
204 hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
205 hval = hval & (HS - 1);
208 && in[try] == in[io + 1] && in[try + 1] == in[io + 2])
210 unsigned int this_len;
213 io + 1 + this_len < in_len
214 && in[try + this_len] == in[io + 1 + this_len];
217 if (this_len >= mlen)
234 litlen = io - litofs;
235 /* fprintf(stderr, "lit: %d\n", litlen); */
238 unsigned int easy_sz;
239 /* Emit everything we can as self-describers. As soon as
240 we hit a byte we can't emit as such we're going to emit
241 a length descriptor anyway, so we can as well include
242 bytes < 0x80 which might follow afterwards in that run. */
244 easy_sz < litlen && in[litofs + easy_sz] < 0x80;
249 if (oo + easy_sz >= out_len)
251 memcpy(out + oo, in + litofs, easy_sz);
260 if (oo + 1 + litlen >= out_len)
262 out[oo++] = 0x80 | (litlen - 1);
264 out[oo++] = in[litofs++];
269 /* Literal length > 32, so chunk it. */
270 if (oo + 1 + 32 >= out_len)
272 out[oo++] = 0x80 | 31;
273 memcpy(out + oo, in + litofs, 32);
282 /* fprintf(stderr, "ref: %d @ %d\n", mlen, mofs); */
284 if (mlen >= 2 && mlen <= 9 && mofs < 1024)
286 if (oo + 2 >= out_len)
288 out[oo++] = 0xa0 | ((mofs & 0x300) >> 5) | (mlen - 2);
289 out[oo++] = mofs & 0xff;
291 else if (mlen >= 10 && mlen <= 41 && mofs < 256)
293 if (oo + 2 >= out_len)
295 out[oo++] = 0xc0 | (mlen - 10);
298 else if (mofs >= 65536)
300 assert(mlen >= 5 && mlen < 2048 + 5);
301 if (oo + 5 >= out_len)
303 out[oo++] = 0xf8 | ((mlen - 5) >> 8);
304 out[oo++] = (mlen - 5) & 0xff;
305 out[oo++] = mofs & 0xff;
306 out[oo++] = (mofs >> 8) & 0xff;
307 out[oo++] = mofs >> 16;
309 else if (mlen >= 3 && mlen <= 18)
311 assert(mofs < 65536);
312 if (oo + 3 >= out_len)
314 out[oo++] = 0xe0 | (mlen - 3);
315 out[oo++] = mofs & 0xff;
316 out[oo++] = mofs >> 8;
320 assert(mlen >= 19 && mlen <= 4095 + 19 && mofs < 65536);
321 if (oo + 4 >= out_len)
323 out[oo++] = 0xf0 | ((mlen - 19) >> 8);
324 out[oo++] = (mlen - 19) & 0xff;
325 out[oo++] = mofs & 0xff;
326 out[oo++] = mofs >> 8;
328 /* Insert the hashes for the compressed run [io..io+mlen-1].
329 For [io] we have it already done at the start of the loop.
330 So it's from [io+1..io+mlen-1], and we need three chars per
331 hash, so the accessed characters will be [io+1..io+mlen-1+2],
332 ergo io+mlen+1 < in_len. */
340 in[io] | in[io + 1] << 8 | in[io + 2] << 16;
341 hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
342 hval = hval & (HS - 1);
343 hnext[io] = htab[hval];
350 /* We might have some characters left. */
351 if (io < in_len && !litofs)
358 litlen = io - litofs;
359 /* fprintf(stderr, "lit: %d\n", litlen); */
362 unsigned int easy_sz;
363 /* Emit everything we can as self-describers. As soon as we hit a
364 byte we can't emit as such we're going to emit a length
365 descriptor anyway, so we can as well include bytes < 0x80 which
366 might follow afterwards in that run. */
367 for (easy_sz = 0; easy_sz < litlen && in[litofs + easy_sz] < 0x80;
372 if (oo + easy_sz >= out_len)
374 memcpy(out + oo, in + litofs, easy_sz);
383 if (oo + 1 + litlen >= out_len)
385 out[oo++] = 0x80 | (litlen - 1);
387 out[oo++] = in[litofs++];
392 /* Literal length > 32, so chunk it. */
393 if (oo + 1 + 32 >= out_len)
395 out[oo++] = 0x80 | 31;
396 memcpy(out + oo, in + litofs, 32);
408 unchecked_decompress_buf(const unsigned char *in, unsigned int in_len,
410 unsigned int out_len __attribute__((unused)))
412 unsigned char *orig_out = out;
413 const unsigned char *in_end = in + in_len;
416 unsigned int first = *in++;
421 /* This default case can't happen, but GCCs VRP is not strong
422 enough to see this, so make this explicitely not fall to
423 the end of the switch, so that we don't have to initialize
431 /* fprintf (stderr, "lit: 1\n"); */
435 /* b 100lllll <l+1 bytes> */
437 unsigned int l = first & 31;
438 /* fprintf (stderr, "lit: %d\n", l); */
445 /* c 101oolll <8o> */
447 o = first & (3 << 3);
448 o = (o << 5) | *in++;
449 first = (first & 7) + 2;
453 /* d 110lllll <8o> */
456 first = (first & 31) + 10;
460 /* e 1110llll <8o> <8o> */
462 o = in[0] | (in[1] << 8);
469 /* f1 1111llll <8o> <8o> <8l> */
470 /* f2 11110lll <8o> <8o> <8l> */
471 /* g 11111lll <8o> <8o> <8o> <8l> */
476 first = (((first - 8) << 8) | in[0]) + 5;
477 o = in[1] | (in[2] << 8) | (in[3] << 16);
482 first = ((first << 8) | in[0]) + 19;
483 o = in[1] | (in[2] << 8);
489 /* fprintf(stderr, "ref: %d @ %d\n", first, o); */
493 /* We know that first will not be zero, and this loop structure is
494 better optimizable. */
504 case 18: *out = *(out + o); out++;
505 case 17: *out = *(out + o); out++;
506 case 16: *out = *(out + o); out++;
507 case 15: *out = *(out + o); out++;
508 case 14: *out = *(out + o); out++;
509 case 13: *out = *(out + o); out++;
510 case 12: *out = *(out + o); out++;
511 case 11: *out = *(out + o); out++;
512 case 10: *out = *(out + o); out++;
513 case 9: *out = *(out + o); out++;
514 case 8: *out = *(out + o); out++;
515 case 7: *out = *(out + o); out++;
516 case 6: *out = *(out + o); out++;
517 case 5: *out = *(out + o); out++;
518 case 4: *out = *(out + o); out++;
519 case 3: *out = *(out + o); out++;
520 case 2: *out = *(out + o); out++;
521 case 1: *out = *(out + o); out++;
529 case 0: *out = *(out + o); out++;
530 case 15: *out = *(out + o); out++;
531 case 14: *out = *(out + o); out++;
532 case 13: *out = *(out + o); out++;
533 case 12: *out = *(out + o); out++;
534 case 11: *out = *(out + o); out++;
535 case 10: *out = *(out + o); out++;
536 case 9: *out = *(out + o); out++;
537 case 8: *out = *(out + o); out++;
538 case 7: *out = *(out + o); out++;
539 case 6: *out = *(out + o); out++;
540 case 5: *out = *(out + o); out++;
541 case 4: *out = *(out + o); out++;
542 case 3: *out = *(out + o); out++;
543 case 2: *out = *(out + o); out++;
544 case 1: *out = *(out + o); out++;
546 while ((int)(first -= 16) > 0);
552 return out - orig_out;
555 /**********************************************************************/
557 void repopagestore_init(Repopagestore *store)
559 memset(store, 0, sizeof(*store));
563 void repopagestore_free(Repopagestore *store)
565 solv_free(store->blob_store);
566 solv_free(store->pages);
567 solv_free(store->mapped);
568 if (store->pagefd != -1)
569 close(store->pagefd);
573 /**********************************************************************/
576 repopagestore_load_page_range(Repopagestore *store, unsigned int pstart, unsigned int pend)
578 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
579 and are consecutive. Return a pointer to the mapping of PSTART. */
580 unsigned char buf[REPOPAGE_BLOBSIZE];
581 unsigned int i, best;
583 /* Quick check in case all pages are there already and consecutive. */
584 for (i = pstart; i <= pend; i++)
585 if (store->pages[i].mapped_at == -1
587 && store->pages[i].mapped_at
588 != store->pages[i-1].mapped_at + REPOPAGE_BLOBSIZE))
591 return store->blob_store + store->pages[pstart].mapped_at;
593 if (store->pagefd == -1)
597 fprintf(stderr, "PAGE: want %d pages starting at %d\n", pend - pstart + 1, pstart);
600 /* Ensure that we can map the numbers of pages we need at all. */
601 if (pend - pstart + 1 > store->ncanmap)
603 unsigned int oldcan = store->ncanmap;
604 store->ncanmap = pend - pstart + 1;
605 if (store->ncanmap < 4)
607 store->mapped = solv_realloc2(store->mapped, store->ncanmap, sizeof(store->mapped[0]));
608 memset(store->mapped + oldcan, 0, (store->ncanmap - oldcan) * sizeof (store->mapped[0]));
609 store->blob_store = solv_realloc2(store->blob_store, store->ncanmap, REPOPAGE_BLOBSIZE);
611 fprintf(stderr, "PAGE: can map %d pages\n", store->ncanmap);
615 /* Now search for "cheap" space in our store. Space is cheap if it's either
616 free (very cheap) or contains pages we search for anyway. */
619 unsigned int best_cost = -1;
620 unsigned int same_cost = 0;
621 unsigned int cost = 0;
625 for (i = 0, ii = -(pend - pstart); i < store->ncanmap; i++, ii++)
627 unsigned int pnum = store->mapped[i];
630 Attrblobpage *p = store->pages + --pnum;
631 assert(p->mapped_at != -1);
632 cost += pnum >= pstart && pnum <= pend ? 1 : 3;
635 continue; /* still need to accummulate cost */
636 if (cost < best_cost)
637 best_cost = cost, best = ii;
638 else if (cost == best_cost)
641 break; /* it won't get any better */
642 /* now remove the cost of page ii again */
648 pnum = store->mapped[ii];
651 Attrblobpage *p = store->pages + --pnum;
652 assert(p->mapped_at != -1);
653 cost -= pnum >= pstart && pnum <= pend ? 1 : 3;
658 fprintf(stderr, "PAGE: best %d at cost %d, same %d\n", best, best_cost, same_cost);
661 /* If all places have the same cost we would thrash on slot 0. Avoid
662 this by doing a round-robin strategy in this case. */
663 if (same_cost == store->ncanmap - pend + pstart - 1)
664 best = store->rr_counter++ % (store->ncanmap - pend + pstart);
667 /* So we want to map our pages from [best] to [best+pend-pstart].
668 Use a very simple strategy, which doesn't make the best use of
669 our resources, but works. Throw away all pages in that range
670 (even ours) then copy around ours or read them in. */
671 for (i = best; i < best + pend - pstart + 1; i++)
673 unsigned int pnum = store->mapped[i];
675 /* If this page is exactly at the right place already,
676 no need to evict it. */
677 && pnum != pstart + i - best)
680 /* Evict this page. */
682 fprintf(stderr, "PAGE: evict page %d from %d\n", pnum, i);
684 store->mapped[i] = 0;
685 store->pages[pnum].mapped_at = -1;
686 /* check if we can copy the correct content */
687 p = store->pages + (pstart + i - best);
688 if (p->mapped_at != -1 && p->mapped_at != i * REPOPAGE_BLOBSIZE)
690 void *dest = store->blob_store + i * REPOPAGE_BLOBSIZE;
692 fprintf(stderr, "PAGECOPY: %d from %d to %d\n", pstart + i - best, p->mapped_at / REPOPAGE_BLOBSIZE, i);
694 memcpy(dest, store->blob_store + p->mapped_at, REPOPAGE_BLOBSIZE);
695 store->mapped[p->mapped_at / REPOPAGE_BLOBSIZE] = 0;
696 p->mapped_at = i * REPOPAGE_BLOBSIZE;
697 store->mapped[i] = pstart + i - best + 1;
702 /* Everything is free now. Read in the pages we want. */
703 for (i = pstart; i <= pend; i++)
705 Attrblobpage *p = store->pages + i;
706 unsigned int pnum = i - pstart + best;
707 void *dest = store->blob_store + pnum * REPOPAGE_BLOBSIZE;
708 if (p->mapped_at != -1)
710 if (p->mapped_at != pnum * REPOPAGE_BLOBSIZE)
713 fprintf(stderr, "PAGECOPY: %d from %d to %d\n", i, p->mapped_at / REPOPAGE_BLOBSIZE, pnum);
715 /* Still mapped somewhere else, so just copy it from there. */
716 memcpy(dest, store->blob_store + p->mapped_at, REPOPAGE_BLOBSIZE);
717 store->mapped[p->mapped_at / REPOPAGE_BLOBSIZE] = 0;
722 unsigned int in_len = p->file_size;
723 unsigned int compressed = in_len & 1;
726 fprintf(stderr, "PAGEIN: %d to %d", i, pnum);
728 if (pread(store->pagefd, compressed ? buf : dest, in_len, p->file_offset) != in_len)
730 perror("mapping pread");
735 unsigned int out_len;
736 out_len = unchecked_decompress_buf(buf, in_len,
737 dest, REPOPAGE_BLOBSIZE);
738 if (out_len != REPOPAGE_BLOBSIZE && i < store->num_pages - 1)
741 fprintf(stderr, "can't decompress\n");
746 fprintf(stderr, " (expand %d to %d)", in_len, out_len);
750 fprintf(stderr, "\n");
753 p->mapped_at = pnum * REPOPAGE_BLOBSIZE;
754 store->mapped[pnum] = i + 1;
756 return store->blob_store + best * REPOPAGE_BLOBSIZE;
760 repopagestore_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
762 return compress_buf(page, len, cpage, max);
765 #define SOLV_ERROR_EOF 3
766 #define SOLV_ERROR_CORRUPT 6
768 static inline unsigned int
774 for (i = 0; i < 4; i++)
784 /* Try to either setup on-demand paging (using FP as backing
785 file), or in case that doesn't work (FP not seekable) slurps in
786 all pages and deactivates paging. */
788 repopagestore_read_or_setup_pages(Repopagestore *store, FILE *fp, unsigned int pagesz, unsigned int blobsz)
792 unsigned int can_seek;
794 unsigned char buf[REPOPAGE_BLOBSIZE];
796 if (pagesz != REPOPAGE_BLOBSIZE)
798 /* We could handle this by slurping in everything. */
799 return SOLV_ERROR_CORRUPT;
802 if ((cur_file_ofs = ftell(fp)) < 0)
806 store->pagefd = dup(fileno(fp));
807 if (store->pagefd == -1)
810 fcntl(store->pagefd, F_SETFD, FD_CLOEXEC);
813 fprintf(stderr, "can %sseek\n", can_seek ? "" : "NOT ");
815 npages = (blobsz + REPOPAGE_BLOBSIZE - 1) / REPOPAGE_BLOBSIZE;
817 store->num_pages = npages;
818 store->pages = solv_malloc2(npages, sizeof(store->pages[0]));
820 /* If we can't seek on our input we have to slurp in everything. */
822 store->blob_store = solv_malloc2(npages, REPOPAGE_BLOBSIZE);
823 for (i = 0; i < npages; i++)
825 unsigned int in_len = read_u32(fp);
826 unsigned int compressed = in_len & 1;
827 Attrblobpage *p = store->pages + i;
830 fprintf(stderr, "page %d: len %d (%scompressed)\n",
831 i, in_len, compressed ? "" : "not ");
837 p->file_offset = cur_file_ofs;
838 p->file_size = in_len * 2 + compressed;
839 if (fseek(fp, in_len, SEEK_CUR) < 0)
841 /* We can't fall back to non-seeking behaviour as we already
842 read over some data pages without storing them away. */
843 close(store->pagefd);
845 return SOLV_ERROR_EOF;
847 cur_file_ofs += in_len;
851 unsigned int out_len;
852 void *dest = store->blob_store + i * REPOPAGE_BLOBSIZE;
853 p->mapped_at = i * REPOPAGE_BLOBSIZE;
856 /* We can't seek, so suck everything in. */
857 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
860 return SOLV_ERROR_EOF;
864 out_len = unchecked_decompress_buf(buf, in_len, dest, REPOPAGE_BLOBSIZE);
865 if (out_len != REPOPAGE_BLOBSIZE && i < npages - 1)
867 return SOLV_ERROR_CORRUPT;
876 repopagestore_disable_paging(Repopagestore *store)
878 if (store->num_pages)
879 repopagestore_load_page_range(store, 0, store->num_pages - 1);
885 transfer_file(FILE * from, FILE * to, int compress)
887 unsigned char inb[BLOCK_SIZE];
888 unsigned char outb[BLOCK_SIZE];
889 while (!feof (from) && !ferror (from))
891 unsigned int in_len, out_len;
894 in_len = fread(inb, 1, BLOCK_SIZE, from);
897 unsigned char *b = outb;
898 out_len = compress_buf(inb, in_len, outb, sizeof (outb));
900 b = inb, out_len = in_len;
901 if (fwrite(&out_len, sizeof (out_len), 1, to) != 1)
903 perror("write size");
906 if (fwrite(b, out_len, 1, to) != 1)
908 perror("write data");
915 if (fread(&in_len, sizeof(in_len), 1, from) != 1)
919 perror("can't read size");
922 if (fread(inb, in_len, 1, from) != 1)
924 perror("can't read data");
928 unchecked_decompress_buf(inb, in_len, outb, sizeof(outb));
929 if (fwrite(outb, out_len, 1, to) != 1)
931 perror("can't write output");
938 /* Just for benchmarking purposes. */
940 dumb_memcpy(void *dest, const void *src, unsigned int len)
949 benchmark(FILE * from)
951 unsigned char inb[BLOCK_SIZE];
952 unsigned char outb[BLOCK_SIZE];
953 unsigned int in_len = fread(inb, 1, BLOCK_SIZE, from);
954 unsigned int out_len;
957 perror("can't read from input");
961 unsigned int calib_loop;
962 unsigned int per_loop;
971 while ((clock() - start) < CLOCKS_PER_SEC / 4)
974 for (i = 0; i < calib_loop; i++)
975 dumb_memcpy(outb, inb, in_len);
976 per_loop += calib_loop;
979 fprintf(stderr, "memcpy:\nCalibrated to %d iterations per loop\n",
983 for (i = 0; i < 10; i++)
984 for (j = 0; j < per_loop; j++)
985 dumb_memcpy(outb, inb, in_len);
987 seconds = (end - start) / (float) CLOCKS_PER_SEC;
988 fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
989 ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
995 while ((clock() - start) < CLOCKS_PER_SEC / 4)
998 for (i = 0; i < calib_loop; i++)
999 compress_buf(inb, in_len, outb, sizeof(outb));
1000 per_loop += calib_loop;
1003 fprintf(stderr, "compression:\nCalibrated to %d iterations per loop\n",
1007 for (i = 0; i < 10; i++)
1008 for (j = 0; j < per_loop; j++)
1009 compress_buf(inb, in_len, outb, sizeof(outb));
1011 seconds = (end - start) / (float) CLOCKS_PER_SEC;
1012 fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
1013 ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
1015 out_len = compress_buf(inb, in_len, outb, sizeof(outb));
1020 while ((clock() - start) < CLOCKS_PER_SEC / 4)
1023 for (i = 0; i < calib_loop; i++)
1024 unchecked_decompress_buf(outb, out_len, inb, sizeof(inb));
1025 per_loop += calib_loop;
1028 fprintf(stderr, "decompression:\nCalibrated to %d iterations per loop\n",
1032 for (i = 0; i < 10; i++)
1033 for (j = 0; j < per_loop; j++)
1034 unchecked_decompress_buf(outb, out_len, inb, sizeof(inb));
1036 seconds = (end - start) / (float) CLOCKS_PER_SEC;
1037 fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
1038 ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
1042 main(int argc, char *argv[])
1045 if (argc > 1 && !strcmp(argv[1], "-d"))
1047 if (argc > 1 && !strcmp(argv[1], "-b"))
1050 transfer_file(stdin, stdout, compress);