xref: /plan9-contrib/sys/src/cmd/ip/ppp/unthwack.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <ip.h>
4*9a747e4fSDavid du Colombier #include <auth.h>
57dd7cddfSDavid du Colombier #include "ppp.h"
67dd7cddfSDavid du Colombier #include "thwack.h"
77dd7cddfSDavid du Colombier 
87dd7cddfSDavid du Colombier enum
97dd7cddfSDavid du Colombier {
107dd7cddfSDavid du Colombier 	DMaxFastLen	= 7,
117dd7cddfSDavid du Colombier 	DBigLenCode	= 0x3c,		/* minimum code for large lenth encoding */
127dd7cddfSDavid du Colombier 	DBigLenBits	= 6,
137dd7cddfSDavid du Colombier 	DBigLenBase	= 1		/* starting items to encode for big lens */
147dd7cddfSDavid du Colombier };
157dd7cddfSDavid du Colombier 
167dd7cddfSDavid du Colombier static uchar lenval[1 << (DBigLenBits - 1)] =
177dd7cddfSDavid du Colombier {
187dd7cddfSDavid du Colombier 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
197dd7cddfSDavid du Colombier 	3, 3, 3, 3, 3, 3, 3, 3,
207dd7cddfSDavid du Colombier 	4, 4, 4, 4,
217dd7cddfSDavid du Colombier 	5,
227dd7cddfSDavid du Colombier 	6,
237dd7cddfSDavid du Colombier 	255,
247dd7cddfSDavid du Colombier 	255
257dd7cddfSDavid du Colombier };
267dd7cddfSDavid du Colombier 
277dd7cddfSDavid du Colombier static uchar lenbits[] =
287dd7cddfSDavid du Colombier {
297dd7cddfSDavid du Colombier 	0, 0, 0,
307dd7cddfSDavid du Colombier 	2, 3, 5, 5,
317dd7cddfSDavid du Colombier };
327dd7cddfSDavid du Colombier 
337dd7cddfSDavid du Colombier static uchar offbits[16] =
347dd7cddfSDavid du Colombier {
357dd7cddfSDavid du Colombier 	5, 5, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 12, 13
367dd7cddfSDavid du Colombier };
377dd7cddfSDavid du Colombier 
387dd7cddfSDavid du Colombier static ushort offbase[16] =
397dd7cddfSDavid du Colombier {
407dd7cddfSDavid du Colombier 	0, 0x20,
417dd7cddfSDavid du Colombier 	0x40, 0x60,
427dd7cddfSDavid du Colombier 	0x80, 0xc0,
437dd7cddfSDavid du Colombier 	0x100, 0x180,
447dd7cddfSDavid du Colombier 	0x200, 0x300,
457dd7cddfSDavid du Colombier 	0x400, 0x600,
467dd7cddfSDavid du Colombier 	0x800, 0xc00,
477dd7cddfSDavid du Colombier 	0x1000,
487dd7cddfSDavid du Colombier 	0x2000
497dd7cddfSDavid du Colombier };
507dd7cddfSDavid du Colombier 
517dd7cddfSDavid du Colombier void
unthwackinit(Unthwack * ut)527dd7cddfSDavid du Colombier unthwackinit(Unthwack *ut)
537dd7cddfSDavid du Colombier {
547dd7cddfSDavid du Colombier 	int i;
557dd7cddfSDavid du Colombier 
567dd7cddfSDavid du Colombier 	memset(ut, 0, sizeof *ut);
577dd7cddfSDavid du Colombier 	for(i = 0; i < DWinBlocks; i++)
587dd7cddfSDavid du Colombier 		ut->blocks[i].data = ut->data[i];
597dd7cddfSDavid du Colombier }
607dd7cddfSDavid du Colombier 
617dd7cddfSDavid du Colombier ulong
unthwackstate(Unthwack * ut,uchar * mask)627dd7cddfSDavid du Colombier unthwackstate(Unthwack *ut, uchar *mask)
637dd7cddfSDavid du Colombier {
647dd7cddfSDavid du Colombier 	ulong bseq, seq;
657dd7cddfSDavid du Colombier 	int slot, m;
667dd7cddfSDavid du Colombier 
67*9a747e4fSDavid du Colombier 	seq = ~0UL;
687dd7cddfSDavid du Colombier 	m = 0;
697dd7cddfSDavid du Colombier 	slot = ut->slot;
707dd7cddfSDavid du Colombier 	for(;;){
717dd7cddfSDavid du Colombier 		slot--;
727dd7cddfSDavid du Colombier 		if(slot < 0)
737dd7cddfSDavid du Colombier 			slot += DWinBlocks;
747dd7cddfSDavid du Colombier 		if(slot == ut->slot)
757dd7cddfSDavid du Colombier 			break;
767dd7cddfSDavid du Colombier 		if(ut->blocks[slot].maxoff == 0)
77*9a747e4fSDavid du Colombier 			continue;
787dd7cddfSDavid du Colombier 		bseq = ut->blocks[slot].seq;
79*9a747e4fSDavid du Colombier 		if(seq == ~0UL)
807dd7cddfSDavid du Colombier 			seq = bseq;
817dd7cddfSDavid du Colombier 		else if(seq - bseq > MaxSeqMask)
827dd7cddfSDavid du Colombier 			break;
837dd7cddfSDavid du Colombier 		else
847dd7cddfSDavid du Colombier 			m |= 1 << (seq - bseq - 1);
857dd7cddfSDavid du Colombier 	}
867dd7cddfSDavid du Colombier 	*mask = m;
877dd7cddfSDavid du Colombier 	return seq;
887dd7cddfSDavid du Colombier }
897dd7cddfSDavid du Colombier 
90*9a747e4fSDavid du Colombier /*
91*9a747e4fSDavid du Colombier  * insert this block in it's correct sequence number order.
92*9a747e4fSDavid du Colombier  * replace the oldest block, which is always pointed to by ut->slot.
93*9a747e4fSDavid du Colombier  * the encoder doesn't use a history at wraparound,
94*9a747e4fSDavid du Colombier  * so don't worry about that case.
95*9a747e4fSDavid du Colombier  */
96*9a747e4fSDavid du Colombier static int
unthwackinsert(Unthwack * ut,int len,ulong seq)97*9a747e4fSDavid du Colombier unthwackinsert(Unthwack *ut, int len, ulong seq)
9859cc4ca5SDavid du Colombier {
9959cc4ca5SDavid du Colombier 	uchar *d;
10059cc4ca5SDavid du Colombier 	int slot, tslot;
10159cc4ca5SDavid du Colombier 
10259cc4ca5SDavid du Colombier 	tslot = ut->slot;
10359cc4ca5SDavid du Colombier 	for(;;){
10459cc4ca5SDavid du Colombier 		slot = tslot - 1;
10559cc4ca5SDavid du Colombier 		if(slot < 0)
10659cc4ca5SDavid du Colombier 			slot += DWinBlocks;
107*9a747e4fSDavid du Colombier 		if(ut->blocks[slot].seq <= seq || ut->blocks[slot].maxoff == 0)
10859cc4ca5SDavid du Colombier 			break;
10959cc4ca5SDavid du Colombier 		d = ut->blocks[tslot].data;
11059cc4ca5SDavid du Colombier 		ut->blocks[tslot] = ut->blocks[slot];
11159cc4ca5SDavid du Colombier 		ut->blocks[slot].data = d;
11259cc4ca5SDavid du Colombier 		tslot = slot;
11359cc4ca5SDavid du Colombier 	}
11459cc4ca5SDavid du Colombier 	ut->blocks[tslot].seq = seq;
115*9a747e4fSDavid du Colombier 	ut->blocks[tslot].maxoff = len;
11659cc4ca5SDavid du Colombier 
11759cc4ca5SDavid du Colombier 	ut->slot++;
11859cc4ca5SDavid du Colombier 	if(ut->slot >= DWinBlocks)
11959cc4ca5SDavid du Colombier 		ut->slot = 0;
12059cc4ca5SDavid du Colombier 
121*9a747e4fSDavid du Colombier 	ut->blocks[ut->slot].seq = ~0UL;
122*9a747e4fSDavid du Colombier 	ut->blocks[ut->slot].maxoff = 0;
123*9a747e4fSDavid du Colombier 
124*9a747e4fSDavid du Colombier 	return tslot;
125*9a747e4fSDavid du Colombier }
126*9a747e4fSDavid du Colombier 
127*9a747e4fSDavid du Colombier int
unthwackadd(Unthwack * ut,uchar * src,int nsrc,ulong seq)128*9a747e4fSDavid du Colombier unthwackadd(Unthwack *ut, uchar *src, int nsrc, ulong seq)
129*9a747e4fSDavid du Colombier {
130*9a747e4fSDavid du Colombier 	int tslot;
131*9a747e4fSDavid du Colombier 
132*9a747e4fSDavid du Colombier 	if(nsrc > ThwMaxBlock)
133*9a747e4fSDavid du Colombier 		return -1;
134*9a747e4fSDavid du Colombier 
135*9a747e4fSDavid du Colombier 	tslot = unthwackinsert(ut, nsrc, seq);
136*9a747e4fSDavid du Colombier 	if(tslot < 0)
137*9a747e4fSDavid du Colombier 		return -1;
138*9a747e4fSDavid du Colombier 
139*9a747e4fSDavid du Colombier 	memmove(ut->blocks[tslot].data, src, nsrc);
140*9a747e4fSDavid du Colombier 
14159cc4ca5SDavid du Colombier 	return nsrc;
14259cc4ca5SDavid du Colombier }
14359cc4ca5SDavid du Colombier 
14459cc4ca5SDavid du Colombier int
unthwack(Unthwack * ut,uchar * dst,int ndst,uchar * src,int nsrc,ulong seq)1457dd7cddfSDavid du Colombier unthwack(Unthwack *ut, uchar *dst, int ndst, uchar *src, int nsrc, ulong seq)
1467dd7cddfSDavid du Colombier {
1477dd7cddfSDavid du Colombier 	UnthwBlock blocks[CompBlocks], *b, *eblocks;
1487dd7cddfSDavid du Colombier 	uchar *s, *d, *dmax, *smax, lit;
149*9a747e4fSDavid du Colombier 	ulong cmask, cseq, bseq, utbits;
150*9a747e4fSDavid du Colombier 	int i, off, len, bits, slot, use, code, utnbits, overbits, lithist;
1517dd7cddfSDavid du Colombier 
1527dd7cddfSDavid du Colombier 	if(nsrc < 4 || nsrc > ThwMaxBlock){
1537dd7cddfSDavid du Colombier 		snprint(ut->err, ThwErrLen, "block too small or large");
1547dd7cddfSDavid du Colombier 		return -1;
1557dd7cddfSDavid du Colombier 	}
1567dd7cddfSDavid du Colombier 
157*9a747e4fSDavid du Colombier 	slot = ut->slot;
1587dd7cddfSDavid du Colombier 	b = blocks;
159*9a747e4fSDavid du Colombier 	*b = ut->blocks[slot];
1607dd7cddfSDavid du Colombier 	d = b->data;
1617dd7cddfSDavid du Colombier 	dmax = d + ndst;
1627dd7cddfSDavid du Colombier 
1637dd7cddfSDavid du Colombier 	/*
1647dd7cddfSDavid du Colombier 	 * set up the history blocks
1657dd7cddfSDavid du Colombier 	 */
1667dd7cddfSDavid du Colombier 	cseq = seq - src[0];
1677dd7cddfSDavid du Colombier 	cmask = src[1];
1687dd7cddfSDavid du Colombier 	b++;
1697dd7cddfSDavid du Colombier 	while(cseq != seq && b < blocks + CompBlocks){
1707dd7cddfSDavid du Colombier 		slot--;
1717dd7cddfSDavid du Colombier 		if(slot < 0)
1727dd7cddfSDavid du Colombier 			slot += DWinBlocks;
1737dd7cddfSDavid du Colombier 		if(slot == ut->slot)
1747dd7cddfSDavid du Colombier 			break;
1757dd7cddfSDavid du Colombier 		bseq = ut->blocks[slot].seq;
1767dd7cddfSDavid du Colombier 		if(bseq == cseq){
1777dd7cddfSDavid du Colombier 			*b = ut->blocks[slot];
1787dd7cddfSDavid du Colombier 			b++;
1797dd7cddfSDavid du Colombier 			if(cmask == 0){
1807dd7cddfSDavid du Colombier 				cseq = seq;
1817dd7cddfSDavid du Colombier 				break;
1827dd7cddfSDavid du Colombier 			}
1837dd7cddfSDavid du Colombier 			do{
1847dd7cddfSDavid du Colombier 				bits = cmask & 1;
1857dd7cddfSDavid du Colombier 				cseq--;
1867dd7cddfSDavid du Colombier 				cmask >>= 1;
1877dd7cddfSDavid du Colombier 			}while(!bits);
1887dd7cddfSDavid du Colombier 		}
1897dd7cddfSDavid du Colombier 	}
1907dd7cddfSDavid du Colombier 	eblocks = b;
1917dd7cddfSDavid du Colombier 	if(cseq != seq){
1927dd7cddfSDavid du Colombier 		snprint(ut->err, ThwErrLen, "blocks dropped: seq=%ld cseq=%ld %d cmask=%#lx %#x\n", seq, cseq, src[0], cmask, src[1]);
1937dd7cddfSDavid du Colombier 		return -2;
1947dd7cddfSDavid du Colombier 	}
1957dd7cddfSDavid du Colombier 
1967dd7cddfSDavid du Colombier 	smax = src + nsrc;
1977dd7cddfSDavid du Colombier 	src += 2;
1987dd7cddfSDavid du Colombier 	utnbits = 0;
1997dd7cddfSDavid du Colombier 	utbits = 0;
2007dd7cddfSDavid du Colombier 	overbits = 0;
2017dd7cddfSDavid du Colombier 	lithist = ~0;
2027dd7cddfSDavid du Colombier 	while(src < smax || utnbits - overbits >= MinDecode){
2037dd7cddfSDavid du Colombier 		while(utnbits <= 24){
2047dd7cddfSDavid du Colombier 			utbits <<= 8;
2057dd7cddfSDavid du Colombier 			if(src < smax)
2067dd7cddfSDavid du Colombier 				utbits |= *src++;
2077dd7cddfSDavid du Colombier 			else
2087dd7cddfSDavid du Colombier 				overbits += 8;
2097dd7cddfSDavid du Colombier 			utnbits += 8;
2107dd7cddfSDavid du Colombier 		}
2117dd7cddfSDavid du Colombier 
2127dd7cddfSDavid du Colombier 		/*
2137dd7cddfSDavid du Colombier 		 * literal
2147dd7cddfSDavid du Colombier 		 */
2157dd7cddfSDavid du Colombier 		len = lenval[(utbits >> (utnbits - 5)) & 0x1f];
2167dd7cddfSDavid du Colombier 		if(len == 0){
2177dd7cddfSDavid du Colombier 			if(lithist & 0xf){
2187dd7cddfSDavid du Colombier 				utnbits -= 9;
2197dd7cddfSDavid du Colombier 				lit = (utbits >> utnbits) & 0xff;
2207dd7cddfSDavid du Colombier 				lit &= 255;
2217dd7cddfSDavid du Colombier 			}else{
2227dd7cddfSDavid du Colombier 				utnbits -= 8;
2237dd7cddfSDavid du Colombier 				lit = (utbits >> utnbits) & 0x7f;
2247dd7cddfSDavid du Colombier 				if(lit < 32){
2257dd7cddfSDavid du Colombier 					if(lit < 24){
2267dd7cddfSDavid du Colombier 						utnbits -= 2;
2277dd7cddfSDavid du Colombier 						lit = (lit << 2) | ((utbits >> utnbits) & 3);
2287dd7cddfSDavid du Colombier 					}else{
2297dd7cddfSDavid du Colombier 						utnbits -= 3;
2307dd7cddfSDavid du Colombier 						lit = (lit << 3) | ((utbits >> utnbits) & 7);
2317dd7cddfSDavid du Colombier 					}
2327dd7cddfSDavid du Colombier 					lit = (lit - 64) & 0xff;
2337dd7cddfSDavid du Colombier 				}
2347dd7cddfSDavid du Colombier 			}
23580ee5cbfSDavid du Colombier 			if(d >= dmax){
23680ee5cbfSDavid du Colombier 				snprint(ut->err, ThwErrLen, "too much output");
23780ee5cbfSDavid du Colombier 				return -1;
23880ee5cbfSDavid du Colombier 			}
2397dd7cddfSDavid du Colombier 			*d++ = lit;
2407dd7cddfSDavid du Colombier 			lithist = (lithist << 1) | (lit < 32) | (lit > 127);
2417dd7cddfSDavid du Colombier 			blocks->maxoff++;
2427dd7cddfSDavid du Colombier 			continue;
2437dd7cddfSDavid du Colombier 		}
2447dd7cddfSDavid du Colombier 
2457dd7cddfSDavid du Colombier 		/*
2467dd7cddfSDavid du Colombier 		 * length
2477dd7cddfSDavid du Colombier 		 */
2487dd7cddfSDavid du Colombier 		if(len < 255)
2497dd7cddfSDavid du Colombier 			utnbits -= lenbits[len];
2507dd7cddfSDavid du Colombier 		else{
2517dd7cddfSDavid du Colombier 			utnbits -= DBigLenBits;
2527dd7cddfSDavid du Colombier 			code = ((utbits >> utnbits) & ((1 << DBigLenBits) - 1)) - DBigLenCode;
2537dd7cddfSDavid du Colombier 			len = DMaxFastLen;
2547dd7cddfSDavid du Colombier 			use = DBigLenBase;
2557dd7cddfSDavid du Colombier 			bits = (DBigLenBits & 1) ^ 1;
2567dd7cddfSDavid du Colombier 			while(code >= use){
2577dd7cddfSDavid du Colombier 				len += use;
2587dd7cddfSDavid du Colombier 				code -= use;
2597dd7cddfSDavid du Colombier 				code <<= 1;
2607dd7cddfSDavid du Colombier 				utnbits--;
26159cc4ca5SDavid du Colombier 				if(utnbits < 0){
26259cc4ca5SDavid du Colombier 					snprint(ut->err, ThwErrLen, "len out of range");
26359cc4ca5SDavid du Colombier 					return -1;
26459cc4ca5SDavid du Colombier 				}
2657dd7cddfSDavid du Colombier 				code |= (utbits >> utnbits) & 1;
2667dd7cddfSDavid du Colombier 				use <<= bits;
2677dd7cddfSDavid du Colombier 				bits ^= 1;
2687dd7cddfSDavid du Colombier 			}
2697dd7cddfSDavid du Colombier 			len += code;
2707dd7cddfSDavid du Colombier 
2717dd7cddfSDavid du Colombier 			while(utnbits <= 24){
2727dd7cddfSDavid du Colombier 				utbits <<= 8;
2737dd7cddfSDavid du Colombier 				if(src < smax)
2747dd7cddfSDavid du Colombier 					utbits |= *src++;
2757dd7cddfSDavid du Colombier 				else
2767dd7cddfSDavid du Colombier 					overbits += 8;
2777dd7cddfSDavid du Colombier 				utnbits += 8;
2787dd7cddfSDavid du Colombier 			}
2797dd7cddfSDavid du Colombier 		}
2807dd7cddfSDavid du Colombier 
2817dd7cddfSDavid du Colombier 		/*
2827dd7cddfSDavid du Colombier 		 * offset
2837dd7cddfSDavid du Colombier 		 */
2847dd7cddfSDavid du Colombier 		utnbits -= 4;
2857dd7cddfSDavid du Colombier 		bits = (utbits >> utnbits) & 0xf;
2867dd7cddfSDavid du Colombier 		off = offbase[bits];
2877dd7cddfSDavid du Colombier 		bits = offbits[bits];
2887dd7cddfSDavid du Colombier 
2897dd7cddfSDavid du Colombier 		utnbits -= bits;
2907dd7cddfSDavid du Colombier 		off |= (utbits >> utnbits) & ((1 << bits) - 1);
2917dd7cddfSDavid du Colombier 		off++;
2927dd7cddfSDavid du Colombier 
2937dd7cddfSDavid du Colombier 		b = blocks;
2947dd7cddfSDavid du Colombier 		while(off > b->maxoff){
2957dd7cddfSDavid du Colombier 			off -= b->maxoff;
2967dd7cddfSDavid du Colombier 			b++;
2977dd7cddfSDavid du Colombier 			if(b >= eblocks){
2987dd7cddfSDavid du Colombier 				snprint(ut->err, ThwErrLen, "offset out of range");
2997dd7cddfSDavid du Colombier 				return -1;
3007dd7cddfSDavid du Colombier 			}
3017dd7cddfSDavid du Colombier 		}
3027dd7cddfSDavid du Colombier 		if(d + len > dmax
3037dd7cddfSDavid du Colombier 		|| b != blocks && len > off){
3047dd7cddfSDavid du Colombier 			snprint(ut->err, ThwErrLen, "len out of range");
3057dd7cddfSDavid du Colombier 			return -1;
3067dd7cddfSDavid du Colombier 		}
3077dd7cddfSDavid du Colombier 		s = b->data + b->maxoff - off;
3087dd7cddfSDavid du Colombier 		blocks->maxoff += len;
3097dd7cddfSDavid du Colombier 
3107dd7cddfSDavid du Colombier 		for(i = 0; i < len; i++)
3117dd7cddfSDavid du Colombier 			d[i] = s[i];
3127dd7cddfSDavid du Colombier 		d += len;
3137dd7cddfSDavid du Colombier 	}
3147dd7cddfSDavid du Colombier 	if(utnbits < overbits){
3157dd7cddfSDavid du Colombier 		snprint(ut->err, ThwErrLen, "compressed data overrun");
3167dd7cddfSDavid du Colombier 		return -1;
3177dd7cddfSDavid du Colombier 	}
3187dd7cddfSDavid du Colombier 
3197dd7cddfSDavid du Colombier 	len = d - blocks->data;
3207dd7cddfSDavid du Colombier 	memmove(dst, blocks->data, len);
3217dd7cddfSDavid du Colombier 
322*9a747e4fSDavid du Colombier 	unthwackinsert(ut, len, seq);
3237dd7cddfSDavid du Colombier 
3247dd7cddfSDavid du Colombier 	return len;
3257dd7cddfSDavid du Colombier }
326