summaryrefslogtreecommitdiff
path: root/sys/src/9/port/thwack.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/thwack.c
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/thwack.c')
-rwxr-xr-xsys/src/9/port/thwack.c381
1 files changed, 381 insertions, 0 deletions
diff --git a/sys/src/9/port/thwack.c b/sys/src/9/port/thwack.c
new file mode 100755
index 000000000..2b48de671
--- /dev/null
+++ b/sys/src/9/port/thwack.c
@@ -0,0 +1,381 @@
+#include "u.h"
+#include "lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+
+#include "thwack.h"
+
+typedef struct Huff Huff;
+
+enum
+{
+ MaxFastLen = 9,
+ BigLenCode = 0x1f4, /* minimum code for large lenth encoding */
+ BigLenBits = 9,
+ BigLenBase = 4 /* starting items to encode for big lens */
+};
+
+enum
+{
+ StatBytes,
+ StatOutBytes,
+ StatLits,
+ StatMatches,
+ StatOffBits,
+ StatLenBits,
+
+ StatDelay,
+ StatHist,
+
+ MaxStat
+};
+
+struct Huff
+{
+ short bits; /* length of the code */
+ ulong encode; /* the code */
+};
+
+static Huff lentab[MaxFastLen] =
+{
+ {2, 0x2}, /* 10 */
+ {3, 0x6}, /* 110 */
+ {5, 0x1c}, /* 11100 */
+ {5, 0x1d}, /* 11101 */
+ {6, 0x3c}, /* 111100 */
+ {7, 0x7a}, /* 1111010 */
+ {7, 0x7b}, /* 1111011 */
+ {8, 0xf8}, /* 11111000 */
+ {8, 0xf9}, /* 11111001 */
+};
+
+void
+thwackinit(Thwack *tw)
+{
+ int i;
+
+ memset(tw, 0, sizeof *tw);
+ for(i = 0; i < EWinBlocks; i++){
+ tw->blocks[i].data = tw->data[i];
+ tw->blocks[i].edata = tw->blocks[i].data;
+ tw->blocks[i].hash = tw->hash[i];
+ tw->blocks[i].acked = 0;
+ }
+}
+
+/*
+ * acknowledgement for block seq & nearby preds
+ */
+void
+thwackack(Thwack *tw, ulong seq, ulong mask)
+{
+ int slot, b;
+
+ slot = tw->slot;
+ for(;;){
+ slot--;
+ if(slot < 0)
+ slot += EWinBlocks;
+ if(slot == tw->slot)
+ break;
+ if(tw->blocks[slot].seq != seq)
+ continue;
+
+ tw->blocks[slot].acked = 1;
+
+ if(mask == 0)
+ break;
+ do{
+ b = mask & 1;
+ seq--;
+ mask >>= 1;
+ }while(!b);
+ }
+}
+
+/*
+ * find a string in the dictionary
+ */
+static int
+thwmatch(ThwBlock *b, ThwBlock *eblocks, uchar **ss, uchar *esrc, ulong h)
+{
+ int then, toff, w, ok;
+ uchar *s, *t;
+
+ s = *ss;
+ if(esrc < s + MinMatch)
+ return 0;
+
+ toff = 0;
+ for(; b < eblocks; b++){
+ then = b->hash[(h ^ b->seq) & HashMask];
+ toff += b->maxoff;
+ w = (ushort)(then - b->begin);
+
+ if(w >= b->maxoff)
+ continue;
+
+
+ /*
+ * don't need to check for the end because
+ * 1) s too close check above
+ * 2) entries too close not added to hash tables
+ */
+ t = w + b->data;
+ if(s[0] != t[0] || s[1] != t[1] || s[2] != t[2])
+ continue;
+ ok = b->edata - t;
+ if(esrc - s > ok)
+ esrc = s + ok;
+
+ t += 3;
+ for(s += 3; s < esrc; s++){
+ if(*s != *t)
+ break;
+ t++;
+ }
+ *ss = s;
+ return toff - w;
+ }
+ return 0;
+}
+
+/*
+ * knuth vol. 3 multiplicative hashing
+ * each byte x chosen according to rules
+ * 1/4 < x < 3/10, 1/3 x < < 3/7, 4/7 < x < 2/3, 7/10 < x < 3/4
+ * with reasonable spread between the bytes & their complements
+ *
+ * the 3 byte value appears to be as almost good as the 4 byte value,
+ * and might be faster on some machines
+ */
+/*
+#define hashit(c) (((ulong)(c) * 0x6b43a9) >> (24 - HashLog))
+*/
+#define hashit(c) ((((ulong)(c) & 0xffffff) * 0x6b43a9b5) >> (32 - HashLog))
+
+/*
+ * lz77 compression with single lookup in a hash table for each block
+ */
+int
+thwack(Thwack *tw, uchar *dst, uchar *src, int n, ulong seq, ulong stats[ThwStats])
+{
+ ThwBlock *eblocks, *b, blocks[CompBlocks];
+ uchar *s, *ss, *sss, *esrc, *half, *twdst, *twdmax;
+ ulong cont, cseq, bseq, cmask, code, twbits;
+ int now, toff, lithist, h, len, slot, bits, use, twnbits, lits, matches, offbits, lenbits, nhist;
+
+ if(n > ThwMaxBlock || n < MinMatch)
+ return -1;
+
+ twdst = dst;
+ twdmax = dst + n;
+
+ /*
+ * add source to the coding window
+ * there is always enough space
+ */
+ slot = tw->slot;
+ b = &tw->blocks[slot];
+ b->seq = seq;
+ b->acked = 0;
+ now = b->begin + b->maxoff;
+ s = b->data;
+ memmove(s, src, n);
+ b->edata = s + n;
+ b->begin = now;
+ b->maxoff = n;
+
+ /*
+ * set up the history blocks
+ */
+ cseq = seq;
+ cmask = 0;
+ *blocks = *b;
+ b = blocks;
+ b->maxoff = 0;
+ b++;
+ nhist = 0;
+ while(b < blocks + CompBlocks){
+ slot--;
+ if(slot < 0)
+ slot += EWinBlocks;
+ if(slot == tw->slot)
+ break;
+ if(!tw->blocks[slot].acked)
+ continue;
+ bseq = tw->blocks[slot].seq;
+ if(cseq == seq){
+ if(seq - bseq >= MaxSeqStart)
+ break;
+ cseq = bseq;
+ }else if(cseq - bseq > MaxSeqMask)
+ break;
+ else
+ cmask |= 1 << (cseq - bseq - 1);
+ *b = tw->blocks[slot];
+ nhist += b->maxoff;
+ b++;
+ }
+ eblocks = b;
+ *twdst++ = seq - cseq;
+ *twdst++ = cmask;
+
+ cont = (s[0] << 16) | (s[1] << 8) | s[2];
+
+ esrc = s + n;
+ half = s + (n >> 1);
+ twnbits = 0;
+ twbits = 0;
+ lits = 0;
+ matches = 0;
+ offbits = 0;
+ lenbits = 0;
+ lithist = ~0;
+ while(s < esrc){
+ h = hashit(cont);
+
+ sss = s;
+ toff = thwmatch(blocks, eblocks, &sss, esrc, h);
+ ss = sss;
+
+ len = ss - s;
+ for(; twnbits >= 8; twnbits -= 8){
+ if(twdst >= twdmax)
+ return -1;
+ *twdst++ = twbits >> (twnbits - 8);
+ }
+ if(len < MinMatch){
+ toff = *s;
+ lithist = (lithist << 1) | (toff < 32) | (toff > 127);
+ if(lithist & 0x1e){
+ twbits = (twbits << 9) | toff;
+ twnbits += 9;
+ }else if(lithist & 1){
+ toff = (toff + 64) & 0xff;
+ if(toff < 96){
+ twbits = (twbits << 10) | toff;
+ twnbits += 10;
+ }else{
+ twbits = (twbits << 11) | toff;
+ twnbits += 11;
+ }
+ }else{
+ twbits = (twbits << 8) | toff;
+ twnbits += 8;
+ }
+ lits++;
+ blocks->maxoff++;
+
+ /*
+ * speed hack
+ * check for compression progress, bail if none achieved
+ */
+ if(s > half){
+ if(4 * blocks->maxoff < 5 * lits)
+ return -1;
+ half = esrc;
+ }
+
+ if(s + MinMatch <= esrc){
+ blocks->hash[(h ^ blocks->seq) & HashMask] = now;
+ if(s + MinMatch < esrc)
+ cont = (cont << 8) | s[MinMatch];
+ }
+ now++;
+ s++;
+ continue;
+ }
+
+ blocks->maxoff += len;
+ matches++;
+
+ /*
+ * length of match
+ */
+ len -= MinMatch;
+ if(len < MaxFastLen){
+ bits = lentab[len].bits;
+ twbits = (twbits << bits) | lentab[len].encode;
+ twnbits += bits;
+ lenbits += bits;
+ }else{
+ code = BigLenCode;
+ bits = BigLenBits;
+ use = BigLenBase;
+ len -= MaxFastLen;
+ while(len >= use){
+ len -= use;
+ code = (code + use) << 1;
+ use <<= (bits & 1) ^ 1;
+ bits++;
+ }
+ twbits = (twbits << bits) | (code + len);
+ twnbits += bits;
+ lenbits += bits;
+
+ for(; twnbits >= 8; twnbits -= 8){
+ if(twdst >= twdmax)
+ return -1;
+ *twdst++ = twbits >> (twnbits - 8);
+ }
+ }
+
+ /*
+ * offset in history
+ */
+ toff--;
+ for(bits = OffBase; toff >= (1 << bits); bits++)
+ ;
+ if(bits < MaxOff+OffBase-1){
+ twbits = (twbits << 3) | (bits - OffBase);
+ if(bits != OffBase)
+ bits--;
+ twnbits += bits + 3;
+ offbits += bits + 3;
+ }else{
+ twbits = (twbits << 4) | 0xe | (bits - (MaxOff+OffBase-1));
+ bits--;
+ twnbits += bits + 4;
+ offbits += bits + 4;
+ }
+ twbits = (twbits << bits) | toff & ((1 << bits) - 1);
+
+ for(; s != ss; s++){
+ if(s + MinMatch <= esrc){
+ h = hashit(cont);
+ blocks->hash[(h ^ blocks->seq) & HashMask] = now;
+ if(s + MinMatch < esrc)
+ cont = (cont << 8) | s[MinMatch];
+ }
+ now++;
+ }
+ }
+
+
+ if(twnbits & 7){
+ twbits <<= 8 - (twnbits & 7);
+ twnbits += 8 - (twnbits & 7);
+ }
+ for(; twnbits >= 8; twnbits -= 8){
+ if(twdst >= twdmax)
+ return -1;
+ *twdst++ = twbits >> (twnbits - 8);
+ }
+
+ tw->slot++;
+ if(tw->slot >= EWinBlocks)
+ tw->slot = 0;
+
+ stats[StatBytes] += blocks->maxoff;
+ stats[StatLits] += lits;
+ stats[StatMatches] += matches;
+ stats[StatOffBits] += offbits;
+ stats[StatLenBits] += lenbits;
+ stats[StatDelay] = stats[StatDelay]*7/8 + dst[0];
+ stats[StatHist] = stats[StatHist]*7/8 + nhist;
+ stats[StatOutBytes] += twdst - dst;
+
+ return twdst - dst;
+}