/*
small_decompress.c
by David Cary

heavily copied from old_lzrw1-a.c by Ross Williams
goal:
decompression code that can handle LZRW1-a text,
but has very small *source* code.

FIXME: untested
FIXME: need to have a test harness.

FIXME:
later make *compression* code that has very small *source* code.

FIXME: replace fast_copy() with memcpy().

FIXME: consider using ``const'' appropriately (unfortuanately, makes it longer).

2003-01-14:DAV: converted from K&R to ANSI function declarations (actually makes it shorter);
	deleted ``register'' keywords (ignored by all modern C compilers)
2003-01-14:DAV: copied compress_decompress() subroutine from old_lzrw1-a.c by Ross Williams.

*/

/*
DAV:
simplified interface;
decompresses lzrw1-a text;
assumes input is ``padded'' so that control bytes padded with 0 (``literal'') flags,
assumes OK to write a little past end of dest (worst-case 15 bytes, I think).
*/
LOCAL void compress_decompress(
	UBYTE *p_src_first,
	ULONG src_len,
	UBYTE *p_dst_first,
	ULONG &p_dst_len
)
/* Input  : Specify input block using p_src_first and src_len.          */
/* Input  : Point p_dst_first to the start of the output zone.          */
/* Input  : Point p_dst_len to a ULONG to receive the output length.    */
/* Input  : Input block and output zone must not overlap. User knows    */
/* Input  : upperbound on output block length from earlier compression. */
/* Input  : In any case, maximum expansion possible is nine times.      */
/* Output : Length of output block written to *p_dst_len.               */
/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
/* Output : Writes only  in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
{
	UBYTE *p_src = p_src_first+FLAG_BYTES;
	UBYTE *p_dst = p_dst_first;
	UBYTE *p_src_post = p_src_first+src_len;
	UBYTE *p_src_max16 = p_src_first+src_len-(16*2);
	ULONG control = 1;

	if (*p_src_first==FLAG_COPY){
		fast_copy(p_src_first+FLAG_BYTES, p_dst_first, src_len-FLAG_BYTES);
		*p_dst_len = src_len-FLAG_BYTES;
		return;
	}
	while (p_src != p_src_post){
		if (control==1) {
			control = 0x10000 | *p_src++;
			control |= (*p_src++)<<8;
		}
		if (control&1){
			UWORD lenmt;
			UBYTE *p;

			lenmt = *p_src++;
			p = p_dst-(((lenmt&0xF0)<<4) | *p_src++);
			*p_dst++ = *p++; *p_dst++ = *p++; *p_dst++ = *p++;
			lenmt &= 0xF;
			while (lenmt--){
				*p_dst++ = *p++;
			}
		}
		else{
			*p_dst++ = *p_src++;
		};
	  control >>= 1;
	}
	*p_dst_len=p_dst-p_dst_first;
}


/*
same interface as LZRW1-a, slightly shorter code
*/
LOCAL void compress_decompress(
	UBYTE *wrk_mem,
	UBYTE *p_src_first,
	ULONG src_len,
	UBYTE *p_dst_first,
	ULONG &p_dst_len
)
/* Input  : Specify input block using p_src_first and src_len.          */
/* Input  : Point p_dst_first to the start of the output zone.          */
/* Input  : Point p_dst_len to a ULONG to receive the output length.    */
/* Input  : Input block and output zone must not overlap. User knows    */
/* Input  : upperbound on output block length from earlier compression. */
/* Input  : In any case, maximum expansion possible is nine times.      */
/* Output : Length of output block written to *p_dst_len.               */
/* Output : Output block in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
/* Output : Writes only  in Mem[p_dst_first..p_dst_first+*p_dst_len-1]. */
{
	UBYTE *p_src=p_src_first+FLAG_BYTES, *p_dst=p_dst_first;
	UBYTE *p_src_post=p_src_first+src_len;
	UBYTE *p_src_max16=p_src_first+src_len-(16*2);

	ULONG control=1;
	if (*p_src_first==FLAG_COPY){
		fast_copy(p_src_first+FLAG_BYTES,p_dst_first,src_len-FLAG_BYTES);
		*p_dst_len=src_len-FLAG_BYTES;
		return;
	}
	while (p_src!=p_src_post){
		if (control==1) {
			control=0x10000|*p_src++; control|=(*p_src++)<<8;
		}
		if (control&1){
			UWORD lenmt;
			UBYTE *p;

			lenmt=*p_src++; p=p_dst-(((lenmt&0xF0)<<4)|*p_src++);
			*p_dst++=*p++; *p_dst++=*p++; *p_dst++=*p++;
			lenmt&=0xF;
			while (lenmt--){
				*p_dst++=*p++;
			}
		}
		else{
			*p_dst++=*p_src++;
		};
	  control>>=1;
	}
	*p_dst_len=p_dst-p_dst_first;
}


