2 * Copyright (c) 2007-2008, 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>
36 #define BLOCK_SIZE (65536*1)
37 #if BLOCK_SIZE <= 65536
38 typedef __uint16_t Ref;
40 typedef __uint32_t Ref;
44 The format is tailored for fast decompression (i.e. only byte based),
45 and skewed to ASCII content (highest bit often not set):
48 - self-describing ASCII character hex L
49 b 100lllll <l+1 bytes>
50 - literal run of length l+1
52 - back ref of length l+2, at offset -(o+1) (o < 1 << 10)
54 - back ref of length l+2+8, at offset -(o+1) (o < 1 << 8)
56 - back ref of length l+3, at offset -(o+1) (o < 1 << 16)
57 f1 1111llll <8l> <8o> <8o>
58 - back ref, length l+19 (l < 1<<12), offset -(o+1) (o < 1<<16)
59 f2 11110lll <8l> <8o> <8o>
60 - back ref, length l+19 (l < 1<<11), offset -(o+1) (o < 1<<16)
61 g 11111lll <8l> <8o> <8o> <8o>
62 - back ref, length l+5 (l < 1<<11), offset -(o+1) (o < 1<<24)
64 Generally for a literal of length L we need L+1 bytes, hence it is
65 better to encode also very short backrefs (2 chars) as backrefs if
66 their offset is small, as that only needs two bytes. Except if we
67 already have a literal run, in that case it's better to append there,
68 instead of breaking it for a backref. So given a potential backref
69 at offset O, length L the strategy is as follows:
71 L < 2 : encode as 1-literal
72 L == 2, O > 1024 : encode as 1-literal
73 L == 2, have already literals: encode as 1-literal
75 L >= 2, L <= 9, O < 1024 : encode as c
76 L >= 10, L <= 41, O < 256 : encode as d
77 else we have either O >= 1024, or L >= 42:
78 L < 3 : encode as 1-literal
79 L >= 3, L <= 18, O < 65536 : encode as e
80 L >= 19, L <= 4095+18, O < 65536 : encode as f
81 else we have either L >= 4096+18 or O >= 65536.
82 O >= 65536: encode as 1-literal, too bad
83 (with the current block size this can't happen)
84 L >= 4096+18, so reduce to 4095+18 : encode as f
89 compress_buf(const unsigned char *in, unsigned int in_len,
90 unsigned char *out, unsigned int out_len)
92 unsigned int oo = 0; //out-offset
93 unsigned int io = 0; //in-offset
96 Ref hnext[BLOCK_SIZE];
97 memset(htab, -1, sizeof (htab));
98 memset(hnext, -1, sizeof (hnext));
99 unsigned int litofs = 0;
100 while (io + 2 < in_len)
102 /* Search for a match of the string starting at IN, we have at
103 least three characters. */
104 unsigned int hval = in[io] | in[io + 1] << 8 | in[io + 2] << 16;
105 unsigned int try, mlen, mofs, tries;
106 hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
107 hval = hval & (HS - 1);
109 hnext[io] = htab[hval];
114 for (tries = 0; try != -1 && tries < 12; tries++)
117 && in[try] == in[io] && in[try + 1] == in[io + 1])
120 mofs = (io - try) - 1;
125 for (; try != -1 && tries < 12; tries++)
128 //assert(io + mlen < in_len);
129 /* Try a match starting from [io] with the strings at [try].
130 That's only sensible if TRY actually is before IO (can happen
131 with uninit hash table). If we have a previous match already
132 we're only going to take the new one if it's longer, hence
133 check the potentially last character. */
134 if (try < io && in[try + mlen] == in[io + mlen])
136 unsigned int this_len, this_ofs;
137 if (memcmp(in + try, in + io, mlen))
140 /* Now try extending the match by more characters. */
142 io + this_len < in_len
143 && in[try + this_len] == in[io + this_len]; this_len++)
147 for (testi = 0; testi < this_len; testi++)
148 assert(in[try + testi] == in[io + testi]);
150 this_ofs = (io - try) - 1;
151 /*if (this_ofs > 65535)
154 assert(this_len >= 2);
155 assert(this_len >= mlen);
156 assert(this_len > mlen || (this_len == mlen && this_ofs > mofs));
158 mlen = this_len, mofs = this_ofs;
159 /* If our match extends up to the end of input, no next
160 match can become better. This is not just an
161 optimization, it establishes a loop invariant
162 (io + mlen < in_len). */
163 if (io + mlen >= in_len)
168 /*if (io - try - 1 >= 65536)
175 /*fprintf(stderr, "%d %d\n", mlen, mofs);*/
176 if (mlen == 2 && (litofs || mofs >= 1024))
178 /*else if (mofs >= 65536)
180 else if (mofs >= 65536)
182 if (mlen >= 2048 + 5)
189 /*else if (mlen >= 4096 + 19)
191 else if (mlen >= 2048 + 19)
193 /* Skip this match if the next character would deliver a better one,
194 but only do this if we have the chance to really extend the
195 length (i.e. our current length isn't yet the (conservative)
197 if (mlen && mlen < (2048 + 5) && io + 3 < in_len)
200 in[io + 1] | in[io + 2] << 8 | in[io + 3] << 16;
202 hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
203 hval = hval & (HS - 1);
206 && in[try] == in[io + 1] && in[try + 1] == in[io + 2])
208 unsigned int this_len;
211 io + 1 + this_len < in_len
212 && in[try + this_len] == in[io + 1 + this_len];
215 if (this_len >= mlen)
231 unsigned litlen = io - litofs;
232 //fprintf(stderr, "lit: %d\n", litlen);
235 unsigned int easy_sz;
236 /* Emit everything we can as self-describers. As soon as
237 we hit a byte we can't emit as such we're going to emit
238 a length descriptor anyway, so we can as well include
239 bytes < 0x80 which might follow afterwards in that run. */
241 easy_sz < litlen && in[litofs + easy_sz] < 0x80;
246 if (oo + easy_sz >= out_len)
248 memcpy(out + oo, in + litofs, easy_sz);
257 if (oo + 1 + litlen >= out_len)
259 out[oo++] = 0x80 | (litlen - 1);
261 out[oo++] = in[litofs++];
266 /* Literal length > 32, so chunk it. */
267 if (oo + 1 + 32 >= out_len)
269 out[oo++] = 0x80 | 31;
270 memcpy(out + oo, in + litofs, 32);
279 //fprintf(stderr, "ref: %d @ %d\n", mlen, mofs);
281 if (mlen >= 2 && mlen <= 9 && mofs < 1024)
283 if (oo + 2 >= out_len)
285 out[oo++] = 0xa0 | ((mofs & 0x300) >> 5) | (mlen - 2);
286 out[oo++] = mofs & 0xff;
288 else if (mlen >= 10 && mlen <= 41 && mofs < 256)
290 if (oo + 2 >= out_len)
292 out[oo++] = 0xc0 | (mlen - 10);
295 else if (mofs >= 65536)
297 assert(mlen >= 5 && mlen < 2048 + 5);
298 if (oo + 5 >= out_len)
300 out[oo++] = 0xf8 | ((mlen - 5) >> 8);
301 out[oo++] = (mlen - 5) & 0xff;
302 out[oo++] = mofs & 0xff;
303 out[oo++] = (mofs >> 8) & 0xff;
304 out[oo++] = mofs >> 16;
306 else if (mlen >= 3 && mlen <= 18)
308 assert(mofs < 65536);
309 if (oo + 3 >= out_len)
311 out[oo++] = 0xe0 | (mlen - 3);
312 out[oo++] = mofs & 0xff;
313 out[oo++] = mofs >> 8;
317 assert(mlen >= 19 && mlen <= 4095 + 19 && mofs < 65536);
318 if (oo + 4 >= out_len)
320 out[oo++] = 0xf0 | ((mlen - 19) >> 8);
321 out[oo++] = (mlen - 19) & 0xff;
322 out[oo++] = mofs & 0xff;
323 out[oo++] = mofs >> 8;
325 /* Insert the hashes for the compressed run [io..io+mlen-1].
326 For [io] we have it already done at the start of the loop.
327 So it's from [io+1..io+mlen-1], and we need three chars per
328 hash, so the accessed characters will be [io+1..io+mlen-1+2],
329 ergo io+mlen+1 < in_len. */
337 in[io] | in[io + 1] << 8 | in[io + 2] << 16;
338 hval = (hval ^ (hval << 5) ^ (hval >> 5)) - hval * 5;
339 hval = hval & (HS - 1);
340 hnext[io] = htab[hval];
347 /* We might have some characters left. */
348 if (io < in_len && !litofs)
354 unsigned litlen = io - litofs;
355 //fprintf(stderr, "lit: %d\n", litlen);
358 unsigned int easy_sz;
359 /* Emit everything we can as self-describers. As soon as we hit a
360 byte we can't emit as such we're going to emit a length
361 descriptor anyway, so we can as well include bytes < 0x80 which
362 might follow afterwards in that run. */
363 for (easy_sz = 0; easy_sz < litlen && in[litofs + easy_sz] < 0x80;
368 if (oo + easy_sz >= out_len)
370 memcpy(out + oo, in + litofs, easy_sz);
379 if (oo + 1 + litlen >= out_len)
381 out[oo++] = 0x80 | (litlen - 1);
383 out[oo++] = in[litofs++];
388 /* Literal length > 32, so chunk it. */
389 if (oo + 1 + 32 >= out_len)
391 out[oo++] = 0x80 | 31;
392 memcpy(out + oo, in + litofs, 32);
404 unchecked_decompress_buf(const unsigned char *in, unsigned int in_len,
406 unsigned int out_len __attribute__((unused)))
408 unsigned char *orig_out = out;
409 const unsigned char *in_end = in + in_len;
412 unsigned int first = *in++;
417 /* This default case can't happen, but GCCs VRP is not strong
418 enough to see this, so make this explicitely not fall to
419 the end of the switch, so that we don't have to initialize
427 //fprintf (stderr, "lit: 1\n");
431 //b 100lllll <l+1 bytes>
433 unsigned int l = first & 31;
434 //fprintf (stderr, "lit: %d\n", l);
443 o = first & (3 << 3);
444 o = (o << 5) | *in++;
445 first = (first & 7) + 2;
452 first = (first & 31) + 10;
456 // e 1110llll <8o> <8o>
458 o = in[0] | (in[1] << 8);
465 //f1 1111llll <8o> <8o> <8l>
466 //f2 11110lll <8o> <8o> <8l>
467 // g 11111lll <8o> <8o> <8o> <8l>
472 first = (((first - 8) << 8) | in[0]) + 5;
473 o = in[1] | (in[2] << 8) | (in[3] << 16);
478 first = ((first << 8) | in[0]) + 19;
479 o = in[1] | (in[2] << 8);
485 //fprintf(stderr, "ref: %d @ %d\n", first, o);
489 /* We know that first will not be zero, and this loop structure is
490 better optimizable. */
500 case 18: *out = *(out + o); out++;
501 case 17: *out = *(out + o); out++;
502 case 16: *out = *(out + o); out++;
503 case 15: *out = *(out + o); out++;
504 case 14: *out = *(out + o); out++;
505 case 13: *out = *(out + o); out++;
506 case 12: *out = *(out + o); out++;
507 case 11: *out = *(out + o); out++;
508 case 10: *out = *(out + o); out++;
509 case 9: *out = *(out + o); out++;
510 case 8: *out = *(out + o); out++;
511 case 7: *out = *(out + o); out++;
512 case 6: *out = *(out + o); out++;
513 case 5: *out = *(out + o); out++;
514 case 4: *out = *(out + o); out++;
515 case 3: *out = *(out + o); out++;
516 case 2: *out = *(out + o); out++;
517 case 1: *out = *(out + o); out++;
525 case 0: *out = *(out + o); out++;
526 case 15: *out = *(out + o); out++;
527 case 14: *out = *(out + o); out++;
528 case 13: *out = *(out + o); out++;
529 case 12: *out = *(out + o); out++;
530 case 11: *out = *(out + o); out++;
531 case 10: *out = *(out + o); out++;
532 case 9: *out = *(out + o); out++;
533 case 8: *out = *(out + o); out++;
534 case 7: *out = *(out + o); out++;
535 case 6: *out = *(out + o); out++;
536 case 5: *out = *(out + o); out++;
537 case 4: *out = *(out + o); out++;
538 case 3: *out = *(out + o); out++;
539 case 2: *out = *(out + o); out++;
540 case 1: *out = *(out + o); out++;
542 while ((int)(first -= 16) > 0);
548 return out - orig_out;
551 /**********************************************************************/
553 void repopagestore_init(Repopagestore *store)
555 memset(store, 0, sizeof(*store));
559 void repopagestore_free(Repopagestore *store)
561 sat_free(store->blob_store);
562 sat_free(store->pages);
563 sat_free(store->mapped);
564 if (store->pagefd != -1)
565 close(store->pagefd);
569 /**********************************************************************/
572 repopagestore_load_page_range(Repopagestore *store, unsigned int pstart, unsigned int pend)
574 /* Make sure all pages from PSTART to PEND (inclusive) are loaded,
575 and are consecutive. Return a pointer to the mapping of PSTART. */
576 unsigned char buf[BLOB_PAGESIZE];
579 /* Quick check in case all pages are there already and consecutive. */
580 for (i = pstart; i <= pend; i++)
581 if (store->pages[i].mapped_at == -1
583 && store->pages[i].mapped_at
584 != store->pages[i-1].mapped_at + BLOB_PAGESIZE))
587 return store->blob_store + store->pages[pstart].mapped_at;
589 if (store->pagefd == -1)
592 /* Ensure that we can map the numbers of pages we need at all. */
593 if (pend - pstart + 1 > store->ncanmap)
595 unsigned int oldcan = store->ncanmap;
596 store->ncanmap = pend - pstart + 1;
597 if (store->ncanmap < 4)
599 store->mapped = sat_realloc2(store->mapped, store->ncanmap, sizeof(store->mapped[0]));
600 memset(store->mapped + oldcan, 0, (store->ncanmap - oldcan) * sizeof (store->mapped[0]));
601 store->blob_store = sat_realloc2(store->blob_store, store->ncanmap, BLOB_PAGESIZE);
603 fprintf(stderr, "PAGE: can map %d pages\n", store->ncanmap);
607 /* Now search for "cheap" space in our store. Space is cheap if it's either
608 free (very cheap) or contains pages we search for anyway. */
610 /* Setup cost array. */
611 unsigned int cost[store->ncanmap];
612 for (i = 0; i < store->ncanmap; i++)
614 unsigned int pnum = store->mapped[i];
620 Attrblobpage *p = store->pages + pnum;
621 assert(p->mapped_at != -1);
622 if (pnum >= pstart && pnum <= pend)
629 /* And search for cheapest space. */
630 unsigned int best_cost = -1;
631 unsigned int best = 0;
632 unsigned int same_cost = 0;
633 for (i = 0; i + pend - pstart < store->ncanmap; i++)
635 unsigned int c = cost[i];
637 for (j = 0; j < pend - pstart + 1; j++)
640 best_cost = c, best = i;
641 else if (c == best_cost)
643 /* A null cost won't become better. */
647 /* If all places have the same cost we would thrash on slot 0. Avoid
648 this by doing a round-robin strategy in this case. */
649 if (same_cost == store->ncanmap - pend + pstart - 1)
650 best = store->rr_counter++ % (store->ncanmap - pend + pstart);
652 /* So we want to map our pages from [best] to [best+pend-pstart].
653 Use a very simple strategy, which doesn't make the best use of
654 our resources, but works. Throw away all pages in that range
655 (even ours) then copy around ours (in case they were outside the
656 range) or read them in. */
657 for (i = best; i < best + pend - pstart + 1; i++)
659 unsigned int pnum = store->mapped[i];
661 /* If this page is exactly at the right place already,
662 no need to evict it. */
663 && pnum != pstart + i - best)
665 /* Evict this page. */
667 fprintf(stderr, "PAGE: evict page %d from %d\n", pnum, i);
670 store->mapped[i] = 0;
671 store->pages[pnum].mapped_at = -1;
675 /* Everything is free now. Read in the pages we want. */
676 for (i = pstart; i <= pend; i++)
678 Attrblobpage *p = store->pages + i;
679 unsigned int pnum = i - pstart + best;
680 void *dest = store->blob_store + pnum * BLOB_PAGESIZE;
681 if (p->mapped_at != -1)
683 if (p->mapped_at != pnum * BLOB_PAGESIZE)
686 fprintf(stderr, "PAGECOPY: %d to %d\n", i, pnum);
688 /* Still mapped somewhere else, so just copy it from there. */
689 memcpy(dest, store->blob_store + p->mapped_at, BLOB_PAGESIZE);
690 store->mapped[p->mapped_at / BLOB_PAGESIZE] = 0;
695 unsigned int in_len = p->file_size;
696 unsigned int compressed = in_len & 1;
699 fprintf(stderr, "PAGEIN: %d to %d", i, pnum);
701 if (pread(store->pagefd, compressed ? buf : dest, in_len, p->file_offset) != in_len)
703 perror("mapping pread");
708 unsigned int out_len;
709 out_len = unchecked_decompress_buf(buf, in_len,
710 dest, BLOB_PAGESIZE);
711 if (out_len != BLOB_PAGESIZE && i < store->num_pages - 1)
714 fprintf(stderr, "can't decompress\n");
719 fprintf(stderr, " (expand %d to %d)", in_len, out_len);
723 fprintf(stderr, "\n");
726 p->mapped_at = pnum * BLOB_PAGESIZE;
727 store->mapped[pnum] = i + 1;
729 return store->blob_store + best * BLOB_PAGESIZE;
733 repopagestore_compress_page(unsigned char *page, unsigned int len, unsigned char *cpage, unsigned int max)
735 return compress_buf(page, len, cpage, max);
738 #define SOLV_ERROR_EOF 3
739 #define SOLV_ERROR_CORRUPT 6
741 static inline unsigned int
747 for (i = 0; i < 4; i++)
757 /* Try to either setup on-demand paging (using FP as backing
758 file), or in case that doesn't work (FP not seekable) slurps in
759 all pages and deactivates paging. */
761 repopagestore_read_or_setup_pages(Repopagestore *store, FILE *fp, unsigned int pagesz, unsigned int blobsz)
765 unsigned int can_seek;
767 unsigned char buf[BLOB_PAGESIZE];
769 if (pagesz != BLOB_PAGESIZE)
771 /* We could handle this by slurping in everything. */
772 return SOLV_ERROR_CORRUPT;
775 if ((cur_file_ofs = ftell(fp)) < 0)
779 store->pagefd = dup(fileno(fp));
780 if (store->pagefd == -1)
784 fprintf(stderr, "can %sseek\n", can_seek ? "" : "NOT ");
786 npages = (blobsz + BLOB_PAGESIZE - 1) / BLOB_PAGESIZE;
788 store->num_pages = npages;
789 store->pages = sat_malloc2(npages, sizeof(store->pages[0]));
791 /* If we can't seek on our input we have to slurp in everything. */
793 store->blob_store = sat_malloc2(npages, BLOB_PAGESIZE);
794 for (i = 0; i < npages; i++)
796 unsigned int in_len = read_u32(fp);
797 unsigned int compressed = in_len & 1;
798 Attrblobpage *p = store->pages + i;
801 fprintf(stderr, "page %d: len %d (%scompressed)\n",
802 i, in_len, compressed ? "" : "not ");
808 p->file_offset = cur_file_ofs;
809 p->file_size = in_len * 2 + compressed;
810 if (fseek(fp, in_len, SEEK_CUR) < 0)
812 /* We can't fall back to non-seeking behaviour as we already
813 read over some data pages without storing them away. */
814 close(store->pagefd);
816 return SOLV_ERROR_EOF;
818 cur_file_ofs += in_len;
822 unsigned int out_len;
823 void *dest = store->blob_store + i * BLOB_PAGESIZE;
824 p->mapped_at = i * BLOB_PAGESIZE;
827 /* We can't seek, so suck everything in. */
828 if (fread(compressed ? buf : dest, in_len, 1, fp) != 1)
831 return SOLV_ERROR_EOF;
835 out_len = unchecked_decompress_buf(buf, in_len, dest, BLOB_PAGESIZE);
836 if (out_len != BLOB_PAGESIZE && i < npages - 1)
838 return SOLV_ERROR_CORRUPT;
847 repopagestore_disable_paging(Repopagestore *store)
849 if (store->num_pages)
850 repopagestore_load_page_range(store, 0, store->num_pages - 1);
856 transfer_file(FILE * from, FILE * to, int compress)
858 unsigned char inb[BLOCK_SIZE];
859 unsigned char outb[BLOCK_SIZE];
860 while (!feof (from) && !ferror (from))
862 unsigned int in_len, out_len;
865 in_len = fread(inb, 1, BLOCK_SIZE, from);
868 unsigned char *b = outb;
869 out_len = compress_buf(inb, in_len, outb, sizeof (outb));
871 b = inb, out_len = in_len;
872 if (fwrite(&out_len, sizeof (out_len), 1, to) != 1)
874 perror("write size");
877 if (fwrite(b, out_len, 1, to) != 1)
879 perror("write data");
886 if (fread(&in_len, sizeof(in_len), 1, from) != 1)
890 perror("can't read size");
893 if (fread(inb, in_len, 1, from) != 1)
895 perror("can't read data");
899 unchecked_decompress_buf(inb, in_len, outb, sizeof(outb));
900 if (fwrite(outb, out_len, 1, to) != 1)
902 perror("can't write output");
909 /* Just for benchmarking purposes. */
911 dumb_memcpy(void *dest, const void *src, unsigned int len)
920 benchmark(FILE * from)
922 unsigned char inb[BLOCK_SIZE];
923 unsigned char outb[BLOCK_SIZE];
924 unsigned int in_len = fread(inb, 1, BLOCK_SIZE, from);
925 unsigned int out_len;
928 perror("can't read from input");
932 unsigned int calib_loop;
933 unsigned int per_loop;
942 while ((clock() - start) < CLOCKS_PER_SEC / 4)
945 for (i = 0; i < calib_loop; i++)
946 dumb_memcpy(outb, inb, in_len);
947 per_loop += calib_loop;
950 fprintf(stderr, "memcpy:\nCalibrated to %d iterations per loop\n",
954 for (i = 0; i < 10; i++)
955 for (j = 0; j < per_loop; j++)
956 dumb_memcpy(outb, inb, in_len);
958 seconds = (end - start) / (float) CLOCKS_PER_SEC;
959 fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
960 ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
966 while ((clock() - start) < CLOCKS_PER_SEC / 4)
969 for (i = 0; i < calib_loop; i++)
970 compress_buf(inb, in_len, outb, sizeof(outb));
971 per_loop += calib_loop;
974 fprintf(stderr, "compression:\nCalibrated to %d iterations per loop\n",
978 for (i = 0; i < 10; i++)
979 for (j = 0; j < per_loop; j++)
980 compress_buf(inb, in_len, outb, sizeof(outb));
982 seconds = (end - start) / (float) CLOCKS_PER_SEC;
983 fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
984 ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
986 out_len = compress_buf(inb, in_len, outb, sizeof(outb));
991 while ((clock() - start) < CLOCKS_PER_SEC / 4)
994 for (i = 0; i < calib_loop; i++)
995 unchecked_decompress_buf(outb, out_len, inb, sizeof(inb));
996 per_loop += calib_loop;
999 fprintf(stderr, "decompression:\nCalibrated to %d iterations per loop\n",
1003 for (i = 0; i < 10; i++)
1004 for (j = 0; j < per_loop; j++)
1005 unchecked_decompress_buf(outb, out_len, inb, sizeof(inb));
1007 seconds = (end - start) / (float) CLOCKS_PER_SEC;
1008 fprintf(stderr, "%.2f seconds == %.2f MB/s\n", seconds,
1009 ((long long) in_len * per_loop * 10) / (1024 * 1024 * seconds));
1013 main(int argc, char *argv[])
1016 if (argc > 1 && !strcmp(argv[1], "-d"))
1018 if (argc > 1 && !strcmp(argv[1], "-b"))
1021 transfer_file(stdin, stdout, compress);