/*
* lifted from /sys/src/cmd/ip/ppp/mppc.c (RFC 2118)
* plus the RDP 5.0 64K history (3.1.8.4.2 RDP 5.0)
*/
#include <u.h>
#include <libc.h>
#include "dat.h"
#include "fns.h"
#define DBG if(0)
//#define DBG
enum
{
MaxHistorySize= 64*1024,
Preset= 0x80, /* reset history */
Pfront= 0x40, /* move packet to front of history */
Pcompress= 0x20, /* packet is compressed */
Pbulk64= 0x01, /* RPD5 bulk compression (64K history) */
};
enum
{
Bits4= 0xf,
Bits5= 0x1f,
Bits6= 0x3f,
Bits7= 0x7f,
Bits8= 0xff,
Bits11= 0x7ff,
Bits13= 0x1fff,
Bits16= 0xffff,
};
enum
{
Lit7, /* seven bit literal */
Lit8, /* eight bit literal */
Off6, /* six bit offset */
Off8, /* eight bit offset */
Off11, /* eleven bit offset (64K history) */
Off13, /* thirteen bit offset (8K history) */
Off16, /* sixteen bit offset (64K history) */
};
/* decode first four bits (8K history) */
static int decode8[16]=
{
Lit7, Lit7, Lit7, Lit7,
Lit7, Lit7, Lit7, Lit7,
Lit8, Lit8, Lit8, Lit8,
Off13, Off13, Off8, Off6,
};
/* decode first five bits (64K history) */
static int decode64[32]=
{
Lit7, Lit7, Lit7, Lit7,
Lit7, Lit7, Lit7, Lit7,
Lit7, Lit7, Lit7, Lit7,
Lit7, Lit7, Lit7, Lit7,
Lit8, Lit8, Lit8, Lit8,
Lit8, Lit8, Lit8, Lit8,
Off16, Off16, Off16, Off16,
Off11, Off11, Off8, Off6,
};
typedef struct Uncstate Uncstate;
struct Uncstate
{
uchar his[MaxHistorySize];
int indx; /* current indx in history */
int size; /* current history size */
};
static Uncstate uncstate;
#define NEXTBYTE sreg = (sreg<<8) | *p++; n--; bits += 8
uchar*
uncomp(uchar* buf, int nbytes, int flags, int* psize)
{
int n, bits, off, len, ones, t;
int *decode, lookbits, lookmask, maxhis, maxones;
ulong sreg;
uchar *p, c, *hp, *hs, *he, *hq;
Uncstate *s;
s = &uncstate;
p = buf;
n = nbytes;
if(flags&Pbulk64){
maxhis = 64*1024;
maxones = 14;
decode = decode64;
lookbits = 5;
lookmask = Bits5;
}else{
maxhis = 8*1024;
maxones = 11;
decode = decode8;
lookbits = 4;
lookmask = Bits4;
}
if(flags&Preset){
s->indx = 0;
s->size = 0;
memset(s->his, maxhis, 0);
}
if(flags&Pfront){
s->indx = 0;
DBG fprint(2, "mppc: front flag set\n");
}
if(!(flags&Pcompress)){
*psize = n;
return buf;
}
bits = 0;
sreg = 0;
hs = s->his; /* history start */
hp = hs+s->indx; /* write pointer in history */
he = hs+maxhis; /* history end */
for(;;){
if(bits<lookbits){
if(n==0) goto Done;
NEXTBYTE;
}
t = decode[(sreg>>(bits-lookbits))&lookmask];
switch(t){
default:
sysfatal("mppc: bad decode %d!", t);
case Lit7:
bits -= 1;
if(bits<7){
if(n==0) goto Done;
NEXTBYTE;
}
c = (sreg>>(bits-7))&Bits7;
bits -= 7;
if(hp >= he) goto His;
*hp++ = c;
continue;
case Lit8:
bits -= 2;
if(bits<7) {
if(n==0) goto Eof;
NEXTBYTE;
}
c = 0x80 | ((sreg>>(bits-7))&Bits7);
bits -= 7;
if(hp >= he) goto His;
*hp++ = c;
continue;
case Off6:
bits -= lookbits;
if(bits<6){
if(n==0) goto Eof;
NEXTBYTE;
}
off = (sreg>>(bits-6))&Bits6;
bits -= 6;
break;
case Off8:
bits -= lookbits;
if(bits<8){
if(n==0) goto Eof;
NEXTBYTE;
}
off = ((sreg>>(bits-8))&Bits8)+64;
bits -= 8;
break;
case Off13: /* (8K history) */
bits -= 3;
while(bits<13){
if(n==0) goto Eof;
NEXTBYTE;
}
off = ((sreg>>(bits-13))&Bits13)+320;
bits -= 13;
break;
case Off11: /* (64K history) */
bits -= 4;
while(bits<11){
if(n==0) goto Eof;
NEXTBYTE;
}
off = ((sreg>>(bits-11))&Bits11)+320;
bits -= 11;
break;
case Off16: /* (64K history) */
bits -= 3;
while(bits<16){
if(n==0) goto Eof;
NEXTBYTE;
}
off = ((sreg>>(bits-16))&Bits16)+2368;
bits -= 16;
break;
}
for(ones=0;;ones++) {
if(bits == 0) {
if(n==0) goto Eof;
NEXTBYTE;
}
bits--;
if(!(sreg&(1<<bits)))
break;
}
if(ones>maxones){
werrstr("bad length %d\n", ones);
return nil;
}
if(ones == 0) {
len = 3;
} else {
ones++;
while(bits<ones) {
if(n==0) goto Eof;
NEXTBYTE;
}
len = (1<<ones) | ((sreg>>(bits-ones))&((1<<ones)-1));
bits -= ones;
}
hq = hp-off;
if(hq < hs) {
hq += maxhis;
if(hq-hs+len > s->size){
// goto His;
fprint(2, "mppc: reference past valid history\n");
}
}
if(hp+len > he)
goto His;
while(len) {
*hp++ = *hq++;
len--;
}
}
Done:
hq = hs+s->indx;
len = hp-hq;
DBG fprint(2, "mppc: len %d bits = %d n=%d\n", len, bits, n);
s->indx += len;
if(s->indx > s->size)
s->size = s->indx;
*psize = len;
return hq;
Eof:
werrstr("unexpected end of data");
return nil;
His:
werrstr("bad history reference");
return nil;
}
|