2 * Copyright (c) 2007-2012, 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>
44 #define BLOCK_SIZE (65536*1)
45 #if BLOCK_SIZE <= 65536
52 The format is tailored for fast decompression (i.e. only byte based),
53 and skewed to ASCII content (highest bit often not set):
56 - self-describing ASCII character hex L
57 b 100lllll <l+1 bytes>
58 - literal run of length l+1
60 - back ref of length l+2, at offset -(o+1) (o < 1 << 10)
62 - back ref of length l+2+8, at offset -(o+1) (o < 1 << 8)
64 - back ref of length l+3, at offset -(o+1) (o < 1 << 16)
65 f1 1111llll <8l> <8o> <8o>
66 - back ref, length l+19 (l < 1<<12), offset -(o+1) (o < 1<<16)
67 f2 11110lll <8l> <8o> <8o>
68 - back ref, length l+19 (l < 1<<11), offset -(o+1) (o < 1<<16)
69 g 11111lll <8l> <8o> <8o> <8o>
70 - back ref, length l+5 (l < 1<<11), offset -(o+1) (o < 1<<24)
72 Generally for a literal of length L we need L+1 bytes, hence it is
73 better to encode also very short backrefs (2 chars) as backrefs if
74 their offset is small, as that only needs two bytes. Except if we
75 already have a literal run, in that case it's better to append there,
76 instead of breaking it for a backref. So given a potential backref
77 at offset O, length L the strategy is as follows:
79 L < 2 : encode as 1-literal
80 L == 2, O > 1024 : encode as 1-literal
81 L == 2, have already literals: encode as 1-literal
83 L >= 2, L <= 9, O < 1024 : encode as c
84 L >= 10, L <= 41, O < 256 : encode as d
85 else we have either O >= 1024, or L >= 42:
86 L < 3 : encode as 1-literal
87 L >= 3, L <= 18, O < 65536 : encode as e
88 L >= 19, L <= 4095+18, O < 65536 : encode as f
89 else we have either L >= 4096+18 or O >= 65536.
90 O >= 65536: encode as 1-literal, too bad
91 (with the current block size this can't happen)
92 L >= 4096+18, so reduce to 4095+18 : encode as f
97 compress_buf(const unsigned char *in, unsigned int in_len,
98 unsigned char *out, unsigned int out_len)
100 unsigned int oo = 0; /* out-offset */
101 unsigned int io = 0; /* in-offset */
104 Ref hnext[BLOCK_SIZE];
105 unsigned int litofs = 0;
106 memset(htab, -1, sizeof (htab));
107 memset(hnext, -1, sizeof (hnext));
108 if (in_len > BLOCK_SIZE)
110 while (io + 2 < in_len)
112 /* Search for a match of the string starting at IN, we have at
113 least three characters. */
114 unsigned int hval = in[io] | in[io + 1] << 8 | in[io + 2] << 16;
115 unsigned int try, mlen, mofs, tries;
116 hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
117 hval = hval & (HS - 1);
119 hnext[io] = htab[hval];
124 for (tries = 0; try != (Ref)-1 && tries < 12; tries++, try = hnext[try])
126 if (in[try] == in[io] && in[try + 1] == in[io + 1])
129 mofs = (io - try) - 1;
133 for (; try != (Ref)-1 && tries < 12; tries++, try = hnext[try])
135 /* assert(io + mlen < in_len); */
136 /* Try a match starting from [io] with the strings at [try].
137 If we have a previous match already we're only going to take
138 the new one if it's longer, hence check the potentially last
140 if (in[try + mlen] == in[io + mlen] && !memcmp(in + try, in + io, mlen))
142 /* Found a longer match */
144 /* Now try extending the match by more characters. */
145 while (io + mlen < in_len && in[try + mlen] == in[io + mlen])
147 mofs = (io - try) - 1;
148 /* If our match extends up to the end of input, no next
149 match can become better. This is not just an
150 optimization, it establishes the loop invariant
151 (io + mlen < in_len). */
152 if (io + mlen >= in_len)
160 /*fprintf(stderr, "%d %d\n", mlen, mofs);*/
161 #if BLOCK_SIZE > 65536
164 if (mlen >= 2048 + 5)
170 if (mlen >= 2048 + 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)
176 if (mlen && mlen < (2048 + 5) && io + 3 < in_len)
179 in[io + 1] | in[io + 2] << 8 | in[io + 3] << 16;
181 hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
182 hval = hval & (HS - 1);
184 if (try != (Ref)-1 && in[try] == in[io + 1] && in[try + 1] == in[io + 2])
186 unsigned int this_len = 2;
187 while (io + 1 + this_len < in_len && in[try + this_len] == in[io + 1 + this_len])
189 if (this_len >= mlen)
196 /* Found no match, start/extend literal */
203 /* Found a match. First dump literals */
208 litlen = io - litofs;
209 /* fprintf(stderr, "lit: %d\n", litlen); */
212 unsigned int easy_sz;
213 /* Emit everything we can as self-describers. As soon as
214 we hit a byte we can't emit as such we're going to emit
215 a length descriptor anyway, so we can as well include
216 bytes < 0x80 which might follow afterwards in that run. */
218 easy_sz < litlen && in[litofs + easy_sz] < 0x80;
223 if (oo + easy_sz >= out_len)
225 memcpy(out + oo, in + litofs, easy_sz);
234 if (oo + 1 + litlen >= out_len)
236 out[oo++] = 0x80 | (litlen - 1);
238 out[oo++] = in[litofs++];
243 /* Literal length > 32, so chunk it. */
244 if (oo + 1 + 32 >= out_len)
246 out[oo++] = 0x80 | 31;
247 memcpy(out + oo, in + litofs, 32);
256 /* fprintf(stderr, "ref: %d @ %d\n", mlen, mofs); */
258 if (mlen >= 2 && mlen <= 9 && mofs < 1024)
260 if (oo + 2 >= out_len)
262 out[oo++] = 0xa0 | ((mofs & 0x300) >> 5) | (mlen - 2);
263 out[oo++] = mofs & 0xff;
265 else if (mlen >= 10 && mlen <= 41 && mofs < 256)
267 if (oo + 2 >= out_len)
269 out[oo++] = 0xc0 | (mlen - 10);
272 else if (mofs >= 65536)
274 #if BLOCK_SIZE <= 65536
277 assert(mlen >= 5 && mlen < 2048 + 5);
278 if (oo + 5 >= out_len)
280 out[oo++] = 0xf8 | ((mlen - 5) >> 8);
281 out[oo++] = (mlen - 5) & 0xff;
282 out[oo++] = mofs & 0xff;
283 out[oo++] = (mofs >> 8) & 0xff;
284 out[oo++] = mofs >> 16;
287 else if (mlen >= 3 && mlen <= 18)
289 assert(mofs < 65536);
290 if (oo + 3 >= out_len)
292 out[oo++] = 0xe0 | (mlen - 3);
293 out[oo++] = mofs & 0xff;
294 out[oo++] = mofs >> 8;
298 assert(mlen >= 19 && mlen <= 4095 + 19 && mofs < 65536);
299 if (oo + 4 >= out_len)
301 out[oo++] = 0xf0 | ((mlen - 19) >> 8);
302 out[oo++] = (mlen - 19) & 0xff;
303 out[oo++] = mofs & 0xff;
304 out[oo++] = mofs >> 8;
306 /* Insert the hashes for the compressed run [io..io+mlen-1].
307 For [io] we have it already done at the start of the loop.
308 So it's from [io+1..io+mlen-1], and we need three chars per
309 hash, so the accessed characters will be [io+1..io+mlen-1+2],
310 ergo io+mlen+1 < in_len. */
318 in[io] | in[io + 1] << 8 | in[io + 2] << 16;
319 hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
320 hval = hval & (HS - 1);
321 hnext[io] = htab[hval];
328 /* We might have some characters left. */
329 if (io < in_len && !litofs)
336 litlen = io - litofs;
337 /* fprintf(stderr, "lit: %d\n", litlen); */
340 unsigned int easy_sz;
341 /* Emit everything we can as self-describers. As soon as we hit a
342 byte we can't emit as such we're going to emit a length
343 descriptor anyway, so we can as well include bytes < 0x80 which
344 might follow afterwards in that run. */
345 for (easy_sz = 0; easy_sz < litlen && in[litofs + easy_sz] < 0x80;
350 if (oo + easy_sz >= out_len)
352 memcpy(out + oo, in + litofs, easy_sz);
361 if (oo + 1 + litlen >= out_len)
363 out[oo++] = 0x80 | (litlen - 1);
365 out[oo++] = in[litofs++];
370 /* Literal length > 32, so chunk it. */
371 if (oo + 1 + 32 >= out_len)
373 out[oo++] = 0x80 | 31;
374 memcpy(out + oo, in + litofs, 32);
385 unchecked_decompress_buf(const unsigned char *in, unsigned int in_len,
387 unsigned int out_len __attribute__((unused)))
389 unsigned char *orig_out = out;
390 const unsigned char *in_end = in + in_len;
393 unsigned int first = *in++;
398 /* This default case can't happen, but GCCs VRP is not strong
399 enough to see this, so make this explicitely not fall to
400 the end of the switch, so that we don't have to initialize
408 /* fprintf (stderr, "lit: 1\n"); */
412 /* b 100lllll <l+1 bytes> */
414 unsigned int l = first & 31;
415 /* fprintf (stderr, "lit: %d\n", l); */
422 /* c 101oolll <8o> */
424 o = first & (3 << 3);
425 o = (o << 5) | *in++;
426 first = (first & 7) + 2;
430 /* d 110lllll <8o> */
433 first = (first & 31) + 10;
437 /* e 1110llll <8o> <8o> */
439 o = in[0] | (in[1] << 8);
441 first = (first & 15) + 3;
445 /* f2 11110lll <8l> <8o> <8o> */
446 /* g 11111lll <8l> <8o> <8o> <8o> */
451 first = (((first - 8) << 8) | in[0]) + 5;
452 o = in[1] | (in[2] << 8) | (in[3] << 16);
457 first = ((first << 8) | in[0]) + 19;
458 o = in[1] | (in[2] << 8);
464 /* fprintf(stderr, "ref: %d @ %d\n", first, o); */
468 /* We know that first will not be zero, and this loop structure is
469 better optimizable. */
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++;
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++;
521 while ((int)(first -= 16) > 0);
527 return out - orig_out;
531 check_decompress_buf(const unsigned char *in, unsigned int in_len)
533 unsigned int out_len = 0;
534 const unsigned char *in_end = in + in_len;
537 unsigned int first = *in++;
542 /* This default case can't happen, but GCCs VRP is not strong
543 enough to see this, so make this explicitely not fall to
544 the end of the switch, so that we don't have to initialize
554 /* b 100lllll <l+1 bytes> */
555 first = (first & 31) + 1;
560 /* c 101oolll <8o> */
561 o = (first & (3 << 3)) << 5 | *in++;
562 first = (first & 7) + 2;
565 /* d 110lllll <8o> */
567 first = (first & 31) + 10;
570 /* e 1110llll <8o> <8o> */
571 o = in[0] | (in[1] << 8);
573 first = (first & 15) + 3;
576 /* f1 1111llll <8l> <8o> <8o> */
577 /* g 11111lll <8l> <8o> <8o> <8o> */
581 first = (((first - 8) << 8) | in[0]) + 5;
582 o = in[1] | (in[2] << 8) | (in[3] << 16);
587 first = ((first << 8) | in[0]) + 19;
588 o = in[1] | (in[2] << 8);
593 /* fprintf(stderr, "ref: %d @ %d\n", first, o); */
601 /**********************************************************************/
603 void repopagestore_init(Repopagestore *store)
605 memset(store, 0, sizeof(*store));
609 void repopagestore_free(Repopagestore *store)
611 store->blob_store = solv_free(store->blob_store);
612 store->file_pages = solv_free(store->file_pages);
613 store->mapped_at = solv_free(store->mapped_at);
614 store->mapped = solv_free(store->mapped);
615 if (store->pagefd != -1)
616 close(store->pagefd);
621 /**********************************************************************/
624 repopagestore_load_page_range(Repopagestore *store, unsigned int pstart, unsigned int pend)
626 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
627 and are consecutive. Return a pointer to the mapping of PSTART. */
628 unsigned char buf[REPOPAGE_BLOBSIZE];
629 unsigned int i, best, pnum;
633 /* Quick check in case the requested page is already mapped */
634 if (store->mapped_at[pstart] != -1)
635 return store->blob_store + store->mapped_at[pstart];
639 /* Quick check in case all pages are already mapped and consecutive. */
640 for (pnum = pstart; pnum <= pend; pnum++)
641 if (store->mapped_at[pnum] == -1
643 && store->mapped_at[pnum]
644 != store->mapped_at[pnum-1] + REPOPAGE_BLOBSIZE))
647 return store->blob_store + store->mapped_at[pstart];
650 if (store->pagefd == -1 || !store->file_pages)
651 return 0; /* no backing file */
654 fprintf(stderr, "PAGE: want %d pages starting at %d\n", pend - pstart + 1, pstart);
657 /* Ensure that we can map the numbers of pages we need at all. */
658 if (pend - pstart + 1 > store->nmapped)
660 unsigned int oldcan = store->nmapped;
661 store->nmapped = pend - pstart + 1;
662 if (store->nmapped < 4)
664 store->mapped = solv_realloc2(store->mapped, store->nmapped, sizeof(store->mapped[0]));
665 for (i = oldcan; i < store->nmapped; i++)
666 store->mapped[i] = -1;
667 store->blob_store = solv_realloc2(store->blob_store, store->nmapped, REPOPAGE_BLOBSIZE);
669 fprintf(stderr, "PAGE: can map %d pages\n", store->nmapped);
673 if (store->mapped_at[pstart] != -1)
675 /* assume forward search */
676 best = store->mapped_at[pstart] / REPOPAGE_BLOBSIZE;
677 if (best + (pend - pstart) >= store->nmapped)
680 else if (store->mapped_at[pend] != -1)
682 /* assume backward search */
683 best = store->mapped_at[pend] / REPOPAGE_BLOBSIZE;
684 if (best < pend - pstart)
685 best = store->nmapped - 1;
686 best -= pend - pstart;
690 /* choose some "random" location to avoid thrashing */
691 best = (pstart + store->rr_counter++) % (store->nmapped - pend + pstart);
694 /* So we want to map our pages from [best] to [best+pend-pstart].
695 Use a very simple strategy, which doesn't make the best use of
696 our resources, but works. Throw away all pages in that range
697 (even ours) then copy around ours or read them in. */
698 for (i = best, pnum = pstart; pnum <= pend; i++, pnum++)
700 unsigned int pnum_mapped_at;
701 unsigned int oldpnum = store->mapped[i];
705 continue; /* already have the correct page */
706 /* Evict this page. */
708 fprintf(stderr, "PAGE: evict page %d from %d\n", oldpnum, i);
710 store->mapped[i] = -1;
711 store->mapped_at[oldpnum] = -1;
713 /* check if we can copy the correct content (before it gets evicted) */
714 pnum_mapped_at = store->mapped_at[pnum];
715 if (pnum_mapped_at != -1 && pnum_mapped_at != i * REPOPAGE_BLOBSIZE)
717 void *dest = store->blob_store + i * REPOPAGE_BLOBSIZE;
719 fprintf(stderr, "PAGECOPY: %d from %d to %d\n", pnum, pnum_mapped_at / REPOPAGE_BLOBSIZE, i);
721 memcpy(dest, store->blob_store + pnum_mapped_at, REPOPAGE_BLOBSIZE);
722 store->mapped[pnum_mapped_at / REPOPAGE_BLOBSIZE] = -1;
723 store->mapped[i] = pnum;
724 store->mapped_at[pnum] = i * REPOPAGE_BLOBSIZE;
728 /* Everything is free now. Read in or copy the pages we want. */
729 for (i = best, pnum = pstart; pnum <= pend; i++, pnum++)
731 void *dest = store->blob_store + i * REPOPAGE_BLOBSIZE;
732 if (store->mapped_at[pnum] != -1)
734 unsigned int pnum_mapped_at = store->mapped_at[pnum];
735 if (pnum_mapped_at != i * REPOPAGE_BLOBSIZE)
738 fprintf(stderr, "PAGECOPY: %d from %d to %d\n", pnum, pnum_mapped_at / REPOPAGE_BLOBSIZE, i);
740 /* Still mapped somewhere else, so just copy it from there. */
741 memcpy(dest, store->blob_store + pnum_mapped_at, REPOPAGE_BLOBSIZE);
742 store->mapped[pnum_mapped_at / REPOPAGE_BLOBSIZE] = -1;
747 Attrblobpage *p = store->file_pages + pnum;
748 unsigned int in_len = p->page_size;
749 unsigned int compressed = in_len & 1;
752 fprintf(stderr, "PAGEIN: %d to %d", pnum, i);
755 if (pread(store->pagefd, compressed ? buf : dest, in_len, store->file_offset + p->page_offset) != in_len)
757 perror("mapping pread");
762 OVERLAPPED ovlp = {0};
763 ovlp.Offset = store->file_offset + p->page_offset;
764 if (!ReadFile((HANDLE) _get_osfhandle(store->pagefd), compressed ? buf : dest, in_len, &read_len, &ovlp) || read_len != in_len)
766 perror("mapping ReadFile");
772 unsigned int out_len;
773 out_len = unchecked_decompress_buf(buf, in_len, dest, REPOPAGE_BLOBSIZE);
774 if (out_len != REPOPAGE_BLOBSIZE && pnum < store->num_pages - 1)
777 fprintf(stderr, "can't decompress\n");
782 fprintf(stderr, " (expand %d to %d)", in_len, out_len);
786 fprintf(stderr, "\n");
789 store->mapped_at[pnum] = i * REPOPAGE_BLOBSIZE;
790 store->mapped[i] = pnum;
792 return store->blob_store + best * REPOPAGE_BLOBSIZE;
796 repopagestore_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
798 return compress_buf(page, len, cpage, max);
802 repopagestore_decompress_page(const unsigned char *cpage, unsigned int len, unsigned char *page, unsigned int max)
804 unsigned int l = check_decompress_buf(cpage, len);
805 if (l == 0 || l > max)
807 return unchecked_decompress_buf(cpage, len, page, max);
811 #define SOLV_ERROR_EOF 3
812 #define SOLV_ERROR_CORRUPT 6
814 static inline unsigned int
820 for (i = 0; i < 4; i++)
830 /* Try to either setup on-demand paging (using FP as backing
831 file), or in case that doesn't work (FP not seekable) slurps in
832 all pages and deactivates paging. */
834 repopagestore_read_or_setup_pages(Repopagestore *store, FILE *fp, unsigned int pagesz, unsigned int blobsz)
838 unsigned int can_seek;
839 unsigned int cur_page_ofs;
840 unsigned char buf[REPOPAGE_BLOBSIZE];
842 if (pagesz != REPOPAGE_BLOBSIZE)
844 /* We could handle this by slurping in everything. */
845 return SOLV_ERROR_CORRUPT;
848 if ((store->file_offset = ftell(fp)) < 0)
852 store->pagefd = dup(fileno(fp));
853 if (store->pagefd == -1)
856 solv_setcloexec(store->pagefd, 1);
859 fprintf(stderr, "can %sseek\n", can_seek ? "" : "NOT ");
861 npages = (blobsz + REPOPAGE_BLOBSIZE - 1) / REPOPAGE_BLOBSIZE;
863 store->num_pages = npages;
864 store->mapped_at = solv_malloc2(npages, sizeof(*store->mapped_at));
866 /* If we can't seek on our input we have to slurp in everything.
867 * Otherwise set up file_pages containing offest/length of the
870 store->file_pages = solv_malloc2(npages, sizeof(*store->file_pages));
872 store->blob_store = solv_malloc2(npages, REPOPAGE_BLOBSIZE);
874 for (i = 0; i < npages; i++)
876 unsigned int in_len = read_u32(fp);
877 unsigned int compressed = in_len & 1;
880 fprintf(stderr, "page %d: len %d (%scompressed)\n",
881 i, in_len, compressed ? "" : "not ");
885 Attrblobpage *p = store->file_pages + i;
887 store->mapped_at[i] = -1; /* not mapped yet */
888 p->page_offset = cur_page_ofs;
889 p->page_size = in_len * 2 + compressed;
890 if (fseek(fp, in_len, SEEK_CUR) < 0)
892 /* We can't fall back to non-seeking behaviour as we already
893 read over some data pages without storing them away. */
894 close(store->pagefd);
896 return SOLV_ERROR_EOF;
898 cur_page_ofs += in_len;
902 unsigned int out_len;
903 void *dest = store->blob_store + i * REPOPAGE_BLOBSIZE;
904 store->mapped_at[i] = i * REPOPAGE_BLOBSIZE;
905 /* We can't seek, so suck everything in. */
906 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
909 return SOLV_ERROR_EOF;
913 out_len = unchecked_decompress_buf(buf, in_len, dest, REPOPAGE_BLOBSIZE);
914 if (out_len != REPOPAGE_BLOBSIZE && i < npages - 1)
916 return SOLV_ERROR_CORRUPT;
925 repopagestore_disable_paging(Repopagestore *store)
927 if (store->num_pages)
928 repopagestore_load_page_range(store, 0, store->num_pages - 1);
934 transfer_file(FILE * from, FILE * to, int compress)
936 unsigned char inb[BLOCK_SIZE];
937 unsigned char outb[BLOCK_SIZE];
938 while (!feof (from) && !ferror (from))
940 unsigned int in_len, out_len;
943 in_len = fread(inb, 1, BLOCK_SIZE, from);
946 unsigned char *b = outb;
947 out_len = compress_buf(inb, in_len, outb, sizeof (outb));
949 b = inb, out_len = in_len;
950 if (fwrite(&out_len, sizeof (out_len), 1, to) != 1)
952 perror("write size");
955 if (fwrite(b, out_len, 1, to) != 1)
957 perror("write data");
964 if (fread(&in_len, sizeof(in_len), 1, from) != 1)
968 perror("can't read size");
971 if (fread(inb, in_len, 1, from) != 1)
973 perror("can't read data");
977 unchecked_decompress_buf(inb, in_len, outb, sizeof(outb));
978 if (fwrite(outb, out_len, 1, to) != 1)
980 perror("can't write output");
987 /* Just for benchmarking purposes. */
989 dumb_memcpy(void *dest, const void *src, unsigned int len)
998 benchmark(FILE * from)
1000 unsigned char inb[BLOCK_SIZE];
1001 unsigned char outb[BLOCK_SIZE];
1002 unsigned int in_len = fread(inb, 1, BLOCK_SIZE, from);
1003 unsigned int out_len;
1006 perror("can't read from input");
1010 unsigned int calib_loop;
1011 unsigned int per_loop;
1020 while ((clock() - start) < CLOCKS_PER_SEC / 4)
1023 for (i = 0; i < calib_loop; i++)
1024 dumb_memcpy(outb, inb, in_len);
1025 per_loop += calib_loop;
1028 fprintf(stderr, "memcpy:\nCalibrated to %d iterations per loop\n",
1032 for (i = 0; i < 10; i++)
1033 for (j = 0; j < per_loop; j++)
1034 dumb_memcpy(outb, inb, in_len);
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));
1044 while ((clock() - start) < CLOCKS_PER_SEC / 4)
1047 for (i = 0; i < calib_loop; i++)
1048 compress_buf(inb, in_len, outb, sizeof(outb));
1049 per_loop += calib_loop;
1052 fprintf(stderr, "compression:\nCalibrated to %d iterations per loop\n",
1056 for (i = 0; i < 10; i++)
1057 for (j = 0; j < per_loop; j++)
1058 compress_buf(inb, in_len, outb, sizeof(outb));
1060 seconds = (end - start) / (float) CLOCKS_PER_SEC;
1061 fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
1062 ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
1064 out_len = compress_buf(inb, in_len, outb, sizeof(outb));
1069 while ((clock() - start) < CLOCKS_PER_SEC / 4)
1072 for (i = 0; i < calib_loop; i++)
1073 unchecked_decompress_buf(outb, out_len, inb, sizeof(inb));
1074 per_loop += calib_loop;
1077 fprintf(stderr, "decompression:\nCalibrated to %d iterations per loop\n",
1081 for (i = 0; i < 10; i++)
1082 for (j = 0; j < per_loop; j++)
1083 unchecked_decompress_buf(outb, out_len, inb, sizeof(inb));
1085 seconds = (end - start) / (float) CLOCKS_PER_SEC;
1086 fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
1087 ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
1091 main(int argc, char *argv[])
1094 if (argc > 1 && !strcmp(argv[1], "-d"))
1096 if (argc > 1 && !strcmp(argv[1], "-b"))
1099 transfer_file(stdin, stdout, compress);