19a747e4fSDavid du Colombier /*
29a747e4fSDavid du Colombier * Extraordinarily brute force Lempel & Ziv-like
39a747e4fSDavid du Colombier * compressor. The input file must fit in memory
49a747e4fSDavid du Colombier * during compression, and the output file will
59a747e4fSDavid du Colombier * be reconstructed in memory during decompression.
69a747e4fSDavid du Colombier * We search for large common sequences and use a
79a747e4fSDavid du Colombier * greedy algorithm to choose which sequence gets
89a747e4fSDavid du Colombier * compressed first.
99a747e4fSDavid du Colombier *
109a747e4fSDavid du Colombier * Files begin with "BLZ\n" and a 32-bit uncompressed file length.
119a747e4fSDavid du Colombier *
129a747e4fSDavid du Colombier * Output format is a series of blocks followed by
139a747e4fSDavid du Colombier * a raw data section. Each block begins with a 32-bit big-endian
149a747e4fSDavid du Colombier * number. The top bit is type and the next 31 bits
159a747e4fSDavid du Colombier * are uncompressed size. Type is one of
169a747e4fSDavid du Colombier * 0 - use raw data for this length
179a747e4fSDavid du Colombier * 1 - a 32-bit offset follows
189a747e4fSDavid du Colombier * After the blocks come the raw data. (The end of the blocks can be
199a747e4fSDavid du Colombier * noted by summing block lengths until you reach the file length.)
209a747e4fSDavid du Colombier */
219a747e4fSDavid du Colombier
229a747e4fSDavid du Colombier #include <u.h>
239a747e4fSDavid du Colombier #include <libc.h>
249a747e4fSDavid du Colombier #include <bio.h>
259a747e4fSDavid du Colombier
263ff48bf5SDavid du Colombier #define malloc sbrk
273ff48bf5SDavid du Colombier
289a747e4fSDavid du Colombier int minrun = 16;
299a747e4fSDavid du Colombier int win = 16;
309a747e4fSDavid du Colombier ulong outn;
319a747e4fSDavid du Colombier int verbose;
329a747e4fSDavid du Colombier int mindist;
339a747e4fSDavid du Colombier
349a747e4fSDavid du Colombier enum { Prime = 16777213 }; /* smallest prime < 2^24 (so p*256+256 < 2^32) */
353ff48bf5SDavid du Colombier enum { NOFF = 3 };
369a747e4fSDavid du Colombier
379a747e4fSDavid du Colombier Biobuf bout;
389a747e4fSDavid du Colombier ulong length;
399a747e4fSDavid du Colombier uchar *data;
409a747e4fSDavid du Colombier ulong sum32(ulong, void*, long);
419a747e4fSDavid du Colombier uchar *odat;
429a747e4fSDavid du Colombier int nodat;
439a747e4fSDavid du Colombier int nraw;
449a747e4fSDavid du Colombier int rawstart;
459a747e4fSDavid du Colombier int acct;
469a747e4fSDavid du Colombier int zlength;
479a747e4fSDavid du Colombier int maxchain;
489a747e4fSDavid du Colombier int maxrle[256];
493ff48bf5SDavid du Colombier int nnew;
509a747e4fSDavid du Colombier
519a747e4fSDavid du Colombier typedef struct Node Node;
529a747e4fSDavid du Colombier struct Node {
539a747e4fSDavid du Colombier Node *link;
543ff48bf5SDavid du Colombier ulong key;
553ff48bf5SDavid du Colombier ulong offset[NOFF];
569a747e4fSDavid du Colombier };
579a747e4fSDavid du Colombier
589a747e4fSDavid du Colombier Node *nodepool;
599a747e4fSDavid du Colombier int nnodepool;
609a747e4fSDavid du Colombier
613ff48bf5SDavid du Colombier Node **hash;
623ff48bf5SDavid du Colombier uint nhash;
633ff48bf5SDavid du Colombier
643ff48bf5SDavid du Colombier uint maxlen;
653ff48bf5SDavid du Colombier uint maxsame;
663ff48bf5SDavid du Colombier uint replacesame = 8*1024*1024;
673ff48bf5SDavid du Colombier
683ff48bf5SDavid du Colombier Node *freelist, **freeend;
693ff48bf5SDavid du Colombier uint nalloc;
709a747e4fSDavid du Colombier
719a747e4fSDavid du Colombier Node*
allocnode(void)729a747e4fSDavid du Colombier allocnode(void)
739a747e4fSDavid du Colombier {
743ff48bf5SDavid du Colombier int i;
753ff48bf5SDavid du Colombier Node *n;
763ff48bf5SDavid du Colombier
773ff48bf5SDavid du Colombier if(nnodepool == 0){
783ff48bf5SDavid du Colombier nnodepool = 256*1024;
793ff48bf5SDavid du Colombier nodepool = malloc(sizeof(Node)*nnodepool);
803ff48bf5SDavid du Colombier }
813ff48bf5SDavid du Colombier if(freelist){
823ff48bf5SDavid du Colombier n = freelist;
833ff48bf5SDavid du Colombier freelist = n->link;
843ff48bf5SDavid du Colombier return n;
859a747e4fSDavid du Colombier }
869a747e4fSDavid du Colombier assert(nnodepool > 0);
873ff48bf5SDavid du Colombier nalloc++;
883ff48bf5SDavid du Colombier n = &nodepool[--nnodepool];
893ff48bf5SDavid du Colombier for(i=0; i<NOFF; i++)
903ff48bf5SDavid du Colombier n->offset[i] = -1;
913ff48bf5SDavid du Colombier
923ff48bf5SDavid du Colombier return n;
933ff48bf5SDavid du Colombier }
943ff48bf5SDavid du Colombier
953ff48bf5SDavid du Colombier void
freenode(Node * n)963ff48bf5SDavid du Colombier freenode(Node *n)
973ff48bf5SDavid du Colombier {
983ff48bf5SDavid du Colombier if(freelist == nil)
993ff48bf5SDavid du Colombier freelist = n;
1003ff48bf5SDavid du Colombier else
1013ff48bf5SDavid du Colombier *freeend = n;
1023ff48bf5SDavid du Colombier freeend = &n->link;
1033ff48bf5SDavid du Colombier n->link = nil;
1049a747e4fSDavid du Colombier }
1059a747e4fSDavid du Colombier
1069a747e4fSDavid du Colombier Node**
llookup(ulong key)1073ff48bf5SDavid du Colombier llookup(ulong key)
1089a747e4fSDavid du Colombier {
1093ff48bf5SDavid du Colombier uint c;
1103ff48bf5SDavid du Colombier Node **l, **top, *n;
1119a747e4fSDavid du Colombier
1123ff48bf5SDavid du Colombier if(nhash == 0){
1133ff48bf5SDavid du Colombier uint x;
1143ff48bf5SDavid du Colombier
1153ff48bf5SDavid du Colombier x = length/8;
1163ff48bf5SDavid du Colombier for(nhash=1; nhash<x; nhash<<=1)
1173ff48bf5SDavid du Colombier ;
1183ff48bf5SDavid du Colombier hash = sbrk(sizeof(Node*)*nhash);
1199a747e4fSDavid du Colombier }
1203ff48bf5SDavid du Colombier
1213ff48bf5SDavid du Colombier top = &hash[key&(nhash-1)];
1223ff48bf5SDavid du Colombier c = 0;
1233ff48bf5SDavid du Colombier for(l=top; *l; l=&(*l)->link){
1243ff48bf5SDavid du Colombier c++;
1253ff48bf5SDavid du Colombier if((*l)->key == key){
1263ff48bf5SDavid du Colombier /* move to front */
1273ff48bf5SDavid du Colombier n = *l;
1283ff48bf5SDavid du Colombier *l = n->link;
1293ff48bf5SDavid du Colombier n->link = *top;
1303ff48bf5SDavid du Colombier *top = n;
1313ff48bf5SDavid du Colombier return top;
1329a747e4fSDavid du Colombier }
1339a747e4fSDavid du Colombier }
1343ff48bf5SDavid du Colombier if(c > maxlen)
1353ff48bf5SDavid du Colombier maxlen = c;
1369a747e4fSDavid du Colombier return l;
1379a747e4fSDavid du Colombier }
1389a747e4fSDavid du Colombier
1399a747e4fSDavid du Colombier Node*
lookup(ulong key)1409a747e4fSDavid du Colombier lookup(ulong key)
1419a747e4fSDavid du Colombier {
1423ff48bf5SDavid du Colombier return *llookup(key);
1433ff48bf5SDavid du Colombier }
1443ff48bf5SDavid du Colombier
1453ff48bf5SDavid du Colombier void
insertnode(ulong key,ulong offset)1463ff48bf5SDavid du Colombier insertnode(ulong key, ulong offset)
1473ff48bf5SDavid du Colombier {
1483ff48bf5SDavid du Colombier int i;
1493ff48bf5SDavid du Colombier Node *n, **l;
1503ff48bf5SDavid du Colombier
1513ff48bf5SDavid du Colombier l = llookup(key);
1523ff48bf5SDavid du Colombier if(*l == nil){
1533ff48bf5SDavid du Colombier if(l==&hash[key&(nhash-1)])
1543ff48bf5SDavid du Colombier nnew++;
1553ff48bf5SDavid du Colombier *l = allocnode();
1563ff48bf5SDavid du Colombier (*l)->key = key;
1573ff48bf5SDavid du Colombier }
1583ff48bf5SDavid du Colombier n = *l;
1593ff48bf5SDavid du Colombier
1603ff48bf5SDavid du Colombier /* add or replace last */
1613ff48bf5SDavid du Colombier for(i=0; i<NOFF-1 && n->offset[i]!=-1; i++)
1623ff48bf5SDavid du Colombier ;
1633ff48bf5SDavid du Colombier n->offset[i] = offset;
1649a747e4fSDavid du Colombier }
1659a747e4fSDavid du Colombier
1669a747e4fSDavid du Colombier void
Bputint(Biobufhdr * b,int n)1679a747e4fSDavid du Colombier Bputint(Biobufhdr *b, int n)
1689a747e4fSDavid du Colombier {
1699a747e4fSDavid du Colombier uchar p[4];
1709a747e4fSDavid du Colombier
1719a747e4fSDavid du Colombier p[0] = n>>24;
1729a747e4fSDavid du Colombier p[1] = n>>16;
1739a747e4fSDavid du Colombier p[2] = n>>8;
1749a747e4fSDavid du Colombier p[3] = n;
1759a747e4fSDavid du Colombier Bwrite(b, p, 4);
1769a747e4fSDavid du Colombier }
1779a747e4fSDavid du Colombier
1789a747e4fSDavid du Colombier void
flushraw(void)1799a747e4fSDavid du Colombier flushraw(void)
1809a747e4fSDavid du Colombier {
1819a747e4fSDavid du Colombier if(nraw){
1829a747e4fSDavid du Colombier if(verbose)
1839a747e4fSDavid du Colombier fprint(2, "Raw %d+%d\n", rawstart, nraw);
1849a747e4fSDavid du Colombier zlength += 4+nraw;
1859a747e4fSDavid du Colombier Bputint(&bout, (1<<31)|nraw);
1869a747e4fSDavid du Colombier memmove(odat+nodat, data+rawstart, nraw);
1879a747e4fSDavid du Colombier nodat += nraw;
1889a747e4fSDavid du Colombier nraw = 0;
1899a747e4fSDavid du Colombier }
1909a747e4fSDavid du Colombier }
1919a747e4fSDavid du Colombier
1929a747e4fSDavid du Colombier int
rawbyte(int i)1939a747e4fSDavid du Colombier rawbyte(int i)
1949a747e4fSDavid du Colombier {
1959a747e4fSDavid du Colombier assert(acct == i);
1969a747e4fSDavid du Colombier if(nraw == 0)
1979a747e4fSDavid du Colombier rawstart = i;
1989a747e4fSDavid du Colombier acct++;
1999a747e4fSDavid du Colombier nraw++;
2009a747e4fSDavid du Colombier return 1;
2019a747e4fSDavid du Colombier }
2029a747e4fSDavid du Colombier
2039a747e4fSDavid du Colombier int
refblock(int i,int len,int off)2049a747e4fSDavid du Colombier refblock(int i, int len, int off)
2059a747e4fSDavid du Colombier {
2069a747e4fSDavid du Colombier assert(acct == i);
2079a747e4fSDavid du Colombier acct += len;
2089a747e4fSDavid du Colombier if(nraw)
2099a747e4fSDavid du Colombier flushraw();
2109a747e4fSDavid du Colombier if(verbose)
2119a747e4fSDavid du Colombier fprint(2, "Copy %d+%d from %d\n", i, len, off);
2129a747e4fSDavid du Colombier Bputint(&bout, len);
2139a747e4fSDavid du Colombier Bputint(&bout, off);
2149a747e4fSDavid du Colombier zlength += 4+4;
2159a747e4fSDavid du Colombier return len;
2169a747e4fSDavid du Colombier }
2179a747e4fSDavid du Colombier
2189a747e4fSDavid du Colombier int
cmprun(uchar * a,uchar * b,int len)2199a747e4fSDavid du Colombier cmprun(uchar *a, uchar *b, int len)
2209a747e4fSDavid du Colombier {
2219a747e4fSDavid du Colombier int i;
2229a747e4fSDavid du Colombier
2239a747e4fSDavid du Colombier if(a==b)
2249a747e4fSDavid du Colombier return 0;
2259a747e4fSDavid du Colombier for(i=0; i<len && a[i]==b[i]; i++)
2269a747e4fSDavid du Colombier ;
2279a747e4fSDavid du Colombier return i;
2289a747e4fSDavid du Colombier }
2299a747e4fSDavid du Colombier
2309a747e4fSDavid du Colombier int
countrle(uchar * a)2319a747e4fSDavid du Colombier countrle(uchar *a)
2329a747e4fSDavid du Colombier {
233*0a75e54aSDavid du Colombier uchar a0;
234*0a75e54aSDavid du Colombier uchar *p;
2359a747e4fSDavid du Colombier
236*0a75e54aSDavid du Colombier p = a;
237*0a75e54aSDavid du Colombier a0 = *p;
238*0a75e54aSDavid du Colombier while(*++p == a0)
2399a747e4fSDavid du Colombier ;
240*0a75e54aSDavid du Colombier return p - a;
2419a747e4fSDavid du Colombier }
2429a747e4fSDavid du Colombier
2439a747e4fSDavid du Colombier void
compress(void)2449a747e4fSDavid du Colombier compress(void)
2459a747e4fSDavid du Colombier {
2463ff48bf5SDavid du Colombier int best, i, j, o, rle, run, maxrun, maxoff;
2479a747e4fSDavid du Colombier ulong sum;
2489a747e4fSDavid du Colombier Node *n;
2499a747e4fSDavid du Colombier
2509a747e4fSDavid du Colombier sum = 0;
2519a747e4fSDavid du Colombier for(i=0; i<win && i<length; i++)
2529a747e4fSDavid du Colombier sum = (sum*256+data[i])%Prime;
2539a747e4fSDavid du Colombier for(i=0; i<length-win; ){
2549a747e4fSDavid du Colombier maxrun = 0;
2559a747e4fSDavid du Colombier maxoff = 0;
2569a747e4fSDavid du Colombier if(verbose)
2579a747e4fSDavid du Colombier fprint(2, "look %.6lux\n", sum);
2583ff48bf5SDavid du Colombier n = lookup(sum);
2593ff48bf5SDavid du Colombier if(n){
2603ff48bf5SDavid du Colombier best = -1;
2613ff48bf5SDavid du Colombier for(o=0; o<NOFF; o++){
2623ff48bf5SDavid du Colombier if(n->offset[o] == -1)
2633ff48bf5SDavid du Colombier break;
2643ff48bf5SDavid du Colombier run = cmprun(data+i, data+n->offset[o], length-i);
2653ff48bf5SDavid du Colombier if(run > maxrun && n->offset[o]+mindist < i){
2669a747e4fSDavid du Colombier maxrun = run;
2673ff48bf5SDavid du Colombier maxoff = n->offset[o];
2683ff48bf5SDavid du Colombier best = o;
2699a747e4fSDavid du Colombier }
2709a747e4fSDavid du Colombier }
2713ff48bf5SDavid du Colombier if(best > 0){
2723ff48bf5SDavid du Colombier o = n->offset[best];
2733ff48bf5SDavid du Colombier for(j=best; j>0; j--)
2743ff48bf5SDavid du Colombier n->offset[j] = n->offset[j-1];
2753ff48bf5SDavid du Colombier n->offset[0] = o;
2769a747e4fSDavid du Colombier }
2773ff48bf5SDavid du Colombier }
2783ff48bf5SDavid du Colombier
2799a747e4fSDavid du Colombier if(maxrun >= minrun)
2809a747e4fSDavid du Colombier j = i+refblock(i, maxrun, maxoff);
2819a747e4fSDavid du Colombier else
2829a747e4fSDavid du Colombier j = i+rawbyte(i);
2839a747e4fSDavid du Colombier for(; i<j; i++){
2849a747e4fSDavid du Colombier /* avoid huge chains from large runs of same byte */
2859a747e4fSDavid du Colombier rle = countrle(data+i);
2869a747e4fSDavid du Colombier if(rle<4)
2879a747e4fSDavid du Colombier insertnode(sum, i);
2889a747e4fSDavid du Colombier else if(rle>maxrle[data[i]]){
2899a747e4fSDavid du Colombier maxrle[data[i]] = rle;
2909a747e4fSDavid du Colombier insertnode(sum, i);
2919a747e4fSDavid du Colombier }
2929a747e4fSDavid du Colombier sum = (sum*256+data[i+win]) % Prime;
2939a747e4fSDavid du Colombier sum = (sum + data[i]*outn) % Prime;
2949a747e4fSDavid du Colombier }
2959a747e4fSDavid du Colombier }
2969a747e4fSDavid du Colombier /* could do better here */
2979a747e4fSDavid du Colombier for(; i<length; i++)
2989a747e4fSDavid du Colombier rawbyte(i);
2999a747e4fSDavid du Colombier flushraw();
3009a747e4fSDavid du Colombier }
3019a747e4fSDavid du Colombier
3029a747e4fSDavid du Colombier void
usage(void)3039a747e4fSDavid du Colombier usage(void)
3049a747e4fSDavid du Colombier {
3053ff48bf5SDavid du Colombier fprint(2, "usage: bflz [-n winsize] [file]\n");
3069a747e4fSDavid du Colombier exits("usage");
3079a747e4fSDavid du Colombier }
3089a747e4fSDavid du Colombier
3099a747e4fSDavid du Colombier void
main(int argc,char ** argv)3109a747e4fSDavid du Colombier main(int argc, char **argv)
3119a747e4fSDavid du Colombier {
3129a747e4fSDavid du Colombier int fd, i, n;
3139a747e4fSDavid du Colombier char buf[10485760];
3149a747e4fSDavid du Colombier
3159a747e4fSDavid du Colombier ARGBEGIN{
3169a747e4fSDavid du Colombier case 'd':
3179a747e4fSDavid du Colombier verbose = 1;
3189a747e4fSDavid du Colombier break;
3193ff48bf5SDavid du Colombier case 's':
3203ff48bf5SDavid du Colombier replacesame = atoi(EARGF(usage()));
3213ff48bf5SDavid du Colombier break;
3229a747e4fSDavid du Colombier case 'm':
3239a747e4fSDavid du Colombier mindist = atoi(EARGF(usage()));
3249a747e4fSDavid du Colombier break;
3259a747e4fSDavid du Colombier case 'n':
3269a747e4fSDavid du Colombier win = atoi(EARGF(usage()));
3279a747e4fSDavid du Colombier minrun = win;
3289a747e4fSDavid du Colombier break;
3299a747e4fSDavid du Colombier default:
3309a747e4fSDavid du Colombier usage();
3319a747e4fSDavid du Colombier }ARGEND
3329a747e4fSDavid du Colombier
3339a747e4fSDavid du Colombier switch(argc){
3349a747e4fSDavid du Colombier default:
3359a747e4fSDavid du Colombier usage();
3369a747e4fSDavid du Colombier case 0:
3379a747e4fSDavid du Colombier fd = 0;
3389a747e4fSDavid du Colombier break;
3399a747e4fSDavid du Colombier case 1:
3409a747e4fSDavid du Colombier if((fd = open(argv[0], OREAD)) < 0)
3419a747e4fSDavid du Colombier sysfatal("open %s: %r", argv[0]);
3429a747e4fSDavid du Colombier break;
3439a747e4fSDavid du Colombier }
3449a747e4fSDavid du Colombier
3459a747e4fSDavid du Colombier while((n = readn(fd, buf, sizeof buf)) > 0){
3469a747e4fSDavid du Colombier data = realloc(data, length+n);
3479a747e4fSDavid du Colombier if(data == nil)
3489a747e4fSDavid du Colombier sysfatal("realloc: %r");
3499a747e4fSDavid du Colombier memmove(data+length, buf, n);
3509a747e4fSDavid du Colombier length += n;
3519a747e4fSDavid du Colombier if(n < sizeof buf)
3529a747e4fSDavid du Colombier break;
3539a747e4fSDavid du Colombier }
3549a747e4fSDavid du Colombier odat = malloc(length);
3559a747e4fSDavid du Colombier if(odat == nil)
3569a747e4fSDavid du Colombier sysfatal("malloc: %r");
3579a747e4fSDavid du Colombier
3589a747e4fSDavid du Colombier Binit(&bout, 1, OWRITE);
3599a747e4fSDavid du Colombier Bprint(&bout, "BLZ\n");
3609a747e4fSDavid du Colombier Bputint(&bout, length);
3619a747e4fSDavid du Colombier outn = 1;
3629a747e4fSDavid du Colombier for(i=0; i<win; i++)
3639a747e4fSDavid du Colombier outn = (outn * 256) % Prime;
3649a747e4fSDavid du Colombier
3659a747e4fSDavid du Colombier if(verbose)
3669a747e4fSDavid du Colombier fprint(2, "256^%d = %.6lux\n", win, outn);
3679a747e4fSDavid du Colombier outn = Prime - outn;
3689a747e4fSDavid du Colombier if(verbose)
3699a747e4fSDavid du Colombier fprint(2, "outn = %.6lux\n", outn);
3709a747e4fSDavid du Colombier
3719a747e4fSDavid du Colombier compress();
3729a747e4fSDavid du Colombier Bwrite(&bout, odat, nodat);
3739a747e4fSDavid du Colombier Bterm(&bout);
3743ff48bf5SDavid du Colombier fprint(2, "brk %p\n", sbrk(1));
3753ff48bf5SDavid du Colombier fprint(2, "%d nodes used; %d of %d hash slots used\n", nalloc, nnew, nhash);
3769a747e4fSDavid du Colombier exits(nil);
3779a747e4fSDavid du Colombier }
378