thwack.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       thwack.c (7255B)
       ---
            1 #include "u.h"
            2 #include "lib.h"
            3 #include "mem.h"
            4 #include "dat.h"
            5 #include "fns.h"
            6 
            7 #include "thwack.h"
            8 
            9 typedef struct Huff        Huff;
           10 
           11 enum
           12 {
           13         MaxFastLen        = 9,
           14         BigLenCode        = 0x1f4,        /* minimum code for large lenth encoding */
           15         BigLenBits        = 9,
           16         BigLenBase        = 4                /* starting items to encode for big lens */
           17 };
           18 
           19 enum
           20 {
           21         StatBytes,
           22         StatOutBytes,
           23         StatLits,
           24         StatMatches,
           25         StatOffBits,
           26         StatLenBits,
           27 
           28         StatDelay,
           29         StatHist,
           30 
           31         MaxStat
           32 };
           33 
           34 struct Huff
           35 {
           36         short        bits;                                /* length of the code */
           37         ulong        encode;                                /* the code */
           38 };
           39 
           40 static        Huff        lentab[MaxFastLen] =
           41 {
           42         {2,        0x2},                /* 10 */
           43         {3,        0x6},                /* 110 */
           44         {5,        0x1c},                /* 11100 */
           45         {5,        0x1d},                /* 11101 */
           46         {6,        0x3c},                /* 111100 */
           47         {7,        0x7a},                /* 1111010 */
           48         {7,        0x7b},                /* 1111011 */
           49         {8,        0xf8},                /* 11111000 */
           50         {8,        0xf9},                /* 11111001 */
           51 };
           52 
           53 void
           54 thwackinit(Thwack *tw)
           55 {
           56         int i;
           57 
           58         memset(tw, 0, sizeof *tw);
           59         for(i = 0; i < EWinBlocks; i++){
           60                 tw->blocks[i].data = tw->data[i];
           61                 tw->blocks[i].edata = tw->blocks[i].data;
           62                 tw->blocks[i].hash = tw->hash[i];
           63                 tw->blocks[i].acked = 0;
           64         }
           65 }
           66 
           67 /*
           68  * acknowledgement for block seq & nearby preds
           69  */
           70 void
           71 thwackack(Thwack *tw, ulong seq, ulong mask)
           72 {
           73         int slot, b;
           74 
           75         slot = tw->slot;
           76         for(;;){
           77                 slot--;
           78                 if(slot < 0)
           79                         slot += EWinBlocks;
           80                 if(slot == tw->slot)
           81                         break;
           82                 if(tw->blocks[slot].seq != seq)
           83                         continue;
           84 
           85                 tw->blocks[slot].acked = 1;
           86 
           87                 if(mask == 0)
           88                         break;
           89                 do{
           90                         b = mask & 1;
           91                         seq--;
           92                         mask >>= 1;
           93                 }while(!b);
           94         }
           95 }
           96 
           97 /*
           98  * find a string in the dictionary
           99  */
          100 static int
          101 thwmatch(ThwBlock *b, ThwBlock *eblocks, uchar **ss, uchar *esrc, ulong h)
          102 {
          103         int then, toff, w, ok;
          104         uchar *s, *t;
          105 
          106         s = *ss;
          107         if(esrc < s + MinMatch)
          108                 return 0;
          109 
          110         toff = 0;
          111         for(; b < eblocks; b++){
          112                 then = b->hash[(h ^ b->seq) & HashMask];
          113                 toff += b->maxoff;
          114                 w = (ushort)(then - b->begin);
          115 
          116                 if(w >= b->maxoff)
          117                         continue;
          118 
          119 
          120                 /*
          121                  * don't need to check for the end because
          122                  * 1) s too close check above
          123                  * 2) entries too close not added to hash tables
          124                  */
          125                 t = w + b->data;
          126                 if(s[0] != t[0] || s[1] != t[1] || s[2] != t[2])
          127                         continue;
          128                 ok = b->edata - t;
          129                 if(esrc - s > ok)
          130                         esrc = s + ok;
          131 
          132                 t += 3;
          133                 for(s += 3; s < esrc; s++){
          134                         if(*s != *t)
          135                                 break;
          136                         t++;
          137                 }
          138                 *ss = s;
          139                 return toff - w;
          140         }
          141         return 0;
          142 }
          143 
          144 /*
          145  * knuth vol. 3 multiplicative hashing
          146  * each byte x chosen according to rules
          147  * 1/4 < x < 3/10, 1/3 x < < 3/7, 4/7 < x < 2/3, 7/10 < x < 3/4
          148  * with reasonable spread between the bytes & their complements
          149  *
          150  * the 3 byte value appears to be as almost good as the 4 byte value,
          151  * and might be faster on some machines
          152  */
          153 /*
          154 #define hashit(c)        (((ulong)(c) * 0x6b43a9) >> (24 - HashLog))
          155 */
          156 #define hashit(c)        ((((ulong)(c) & 0xffffff) * 0x6b43a9b5) >> (32 - HashLog))
          157 
          158 /*
          159  * lz77 compression with single lookup in a hash table for each block
          160  */
          161 int
          162 thwack(Thwack *tw, uchar *dst, uchar *src, int n, ulong seq, ulong stats[ThwStats])
          163 {
          164         ThwBlock *eblocks, *b, blocks[CompBlocks];
          165         uchar *s, *ss, *sss, *esrc, *half, *twdst, *twdmax;
          166         ulong cont, cseq, bseq, cmask, code, twbits;
          167         int now, toff, lithist, h, len, slot, bits, use, twnbits, lits, matches, offbits, lenbits, nhist;
          168 
          169         if(n > ThwMaxBlock || n < MinMatch)
          170                 return -1;
          171 
          172         twdst = dst;
          173         twdmax = dst + n;
          174 
          175         /*
          176          * add source to the coding window
          177          * there is always enough space
          178          */
          179         slot = tw->slot;
          180         b = &tw->blocks[slot];
          181         b->seq = seq;
          182         b->acked = 0;
          183         now = b->begin + b->maxoff;
          184         s = b->data;
          185         memmove(s, src, n);
          186         b->edata = s + n;
          187         b->begin = now;
          188         b->maxoff = n;
          189 
          190         /*
          191          * set up the history blocks
          192          */
          193         cseq = seq;
          194         cmask = 0;
          195         *blocks = *b;
          196         b = blocks;
          197         b->maxoff = 0;
          198         b++;
          199         nhist = 0;
          200         while(b < blocks + CompBlocks){
          201                 slot--;
          202                 if(slot < 0)
          203                         slot += EWinBlocks;
          204                 if(slot == tw->slot)
          205                         break;
          206                 if(!tw->blocks[slot].acked)
          207                         continue;
          208                 bseq = tw->blocks[slot].seq;
          209                 if(cseq == seq){
          210                         if(seq - bseq >= MaxSeqStart)
          211                                 break;
          212                         cseq = bseq;
          213                 }else if(cseq - bseq > MaxSeqMask)
          214                         break;
          215                 else
          216                         cmask |= 1 << (cseq - bseq - 1);
          217                 *b = tw->blocks[slot];
          218                 nhist += b->maxoff;
          219                 b++;
          220         }
          221         eblocks = b;
          222         *twdst++ = seq - cseq;
          223         *twdst++ = cmask;
          224 
          225         cont = (s[0] << 16) | (s[1] << 8) | s[2];
          226 
          227         esrc = s + n;
          228         half = s + (n >> 1);
          229         twnbits = 0;
          230         twbits = 0;
          231         lits = 0;
          232         matches = 0;
          233         offbits = 0;
          234         lenbits = 0;
          235         lithist = ~0;
          236         while(s < esrc){
          237                 h = hashit(cont);
          238 
          239                 sss = s;
          240                 toff = thwmatch(blocks, eblocks, &sss, esrc, h);
          241                 ss = sss;
          242 
          243                 len = ss - s;
          244                 for(; twnbits >= 8; twnbits -= 8){
          245                         if(twdst >= twdmax)
          246                                 return -1;
          247                         *twdst++ = twbits >> (twnbits - 8);
          248                 }
          249                 if(len < MinMatch){
          250                         toff = *s;
          251                         lithist = (lithist << 1) | (toff < 32) | (toff > 127);
          252                         if(lithist & 0x1e){
          253                                 twbits = (twbits << 9) | toff;
          254                                 twnbits += 9;
          255                         }else if(lithist & 1){
          256                                 toff = (toff + 64) & 0xff;
          257                                 if(toff < 96){
          258                                         twbits = (twbits << 10) | toff;
          259                                         twnbits += 10;
          260                                 }else{
          261                                         twbits = (twbits << 11) | toff;
          262                                         twnbits += 11;
          263                                 }
          264                         }else{
          265                                 twbits = (twbits << 8) | toff;
          266                                 twnbits += 8;
          267                         }
          268                         lits++;
          269                         blocks->maxoff++;
          270 
          271                         /*
          272                          * speed hack
          273                          * check for compression progress, bail if none achieved
          274                          */
          275                         if(s > half){
          276                                 if(4 * blocks->maxoff < 5 * lits)
          277                                         return -1;
          278                                 half = esrc;
          279                         }
          280 
          281                         if(s + MinMatch <= esrc){
          282                                 blocks->hash[(h ^ blocks->seq) & HashMask] = now;
          283                                 if(s + MinMatch < esrc)
          284                                         cont = (cont << 8) | s[MinMatch];
          285                         }
          286                         now++;
          287                         s++;
          288                         continue;
          289                 }
          290 
          291                 blocks->maxoff += len;
          292                 matches++;
          293 
          294                 /*
          295                  * length of match
          296                  */
          297                 len -= MinMatch;
          298                 if(len < MaxFastLen){
          299                         bits = lentab[len].bits;
          300                         twbits = (twbits << bits) | lentab[len].encode;
          301                         twnbits += bits;
          302                         lenbits += bits;
          303                 }else{
          304                         code = BigLenCode;
          305                         bits = BigLenBits;
          306                         use = BigLenBase;
          307                         len -= MaxFastLen;
          308                         while(len >= use){
          309                                 len -= use;
          310                                 code = (code + use) << 1;
          311                                 use <<= (bits & 1) ^ 1;
          312                                 bits++;
          313                         }
          314                         twbits = (twbits << bits) | (code + len);
          315                         twnbits += bits;
          316                         lenbits += bits;
          317 
          318                         for(; twnbits >= 8; twnbits -= 8){
          319                                 if(twdst >= twdmax)
          320                                         return -1;
          321                                 *twdst++ = twbits >> (twnbits - 8);
          322                         }
          323                 }
          324 
          325                 /*
          326                  * offset in history
          327                  */
          328                 toff--;
          329                 for(bits = OffBase; toff >= (1 << bits); bits++)
          330                         ;
          331                 if(bits < MaxOff+OffBase-1){
          332                         twbits = (twbits << 3) | (bits - OffBase);
          333                         if(bits != OffBase)
          334                                 bits--;
          335                         twnbits += bits + 3;
          336                         offbits += bits + 3;
          337                 }else{
          338                         twbits = (twbits << 4) | 0xe | (bits - (MaxOff+OffBase-1));
          339                         bits--;
          340                         twnbits += bits + 4;
          341                         offbits += bits + 4;
          342                 }
          343                 twbits = (twbits << bits) | (toff & ((1 << bits) - 1));
          344 
          345                 for(; s != ss; s++){
          346                         if(s + MinMatch <= esrc){
          347                                 h = hashit(cont);
          348                                 blocks->hash[(h ^ blocks->seq) & HashMask] = now;
          349                                 if(s + MinMatch < esrc)
          350                                         cont = (cont << 8) | s[MinMatch];
          351                         }
          352                         now++;
          353                 }
          354         }
          355 
          356 
          357         if(twnbits & 7){
          358                 twbits <<= 8 - (twnbits & 7);
          359                 twnbits += 8 - (twnbits & 7);
          360         }
          361         for(; twnbits >= 8; twnbits -= 8){
          362                 if(twdst >= twdmax)
          363                         return -1;
          364                 *twdst++ = twbits >> (twnbits - 8);
          365         }
          366 
          367         tw->slot++;
          368         if(tw->slot >= EWinBlocks)
          369                 tw->slot = 0;
          370 
          371         stats[StatBytes] += blocks->maxoff;
          372         stats[StatLits] += lits;
          373         stats[StatMatches] += matches;
          374         stats[StatOffBits] += offbits;
          375         stats[StatLenBits] += lenbits;
          376         stats[StatDelay] = stats[StatDelay]*7/8 + dst[0];
          377         stats[StatHist] = stats[StatHist]*7/8 + nhist;
          378         stats[StatOutBytes] += twdst - dst;
          379 
          380         return twdst - dst;
          381 }