/*	
 *	cliptlz.c
 *
 *	簡易LZ圧縮
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2008 Naoyuki Sawa
 *
 *	* Sun Feb 17 22:17:30 JST 2008 Naoyuki Sawa
 *	- 1st リリース。
 */
#include "clip.h"

/****************************************************************************
 *	
 ****************************************************************************/

#ifndef PIECE
void
TinyLZ_init(TinyLZ* tlz, const void* p)
{
	tlz->p = p;
	memset(&tlz->len_pos, 0, sizeof(TinyLZ) - sizeof tlz->p);
}
#else /*PIECE*/
void TinyLZ_init(TinyLZ* tlz, const void* p);
asm("
	.code
	.align	1
	.global	TinyLZ_init
TinyLZ_init:
	;// %r12 := tlz
	;// %r13 := p
	ld.w	[%r12]+, %r13		; tlz->p = p
	ld.w	%r13, 0			; memset(&tlz->len_pos, 0, sizeof(TinyLZ) - sizeof tlz->p)
	xjp.d	memset
	ld.w	%r14, 16		;						*delay*
");
#endif /*PIECE*/

#ifndef PIECE
int
TinyLZ_get(TinyLZ* tlz)
{
	const unsigned char* p = tlz->p;
	int len_pos = tlz->len_pos;
	//
	int c;

	if((len_pos -= 16) < 0) {
		len_pos = *p++;
	}
	if(len_pos & 15) {
		c = tlz->dic[15 - (len_pos & 15)];
	} else {
		c = *p++;
	}
	tlz->p = p;
	tlz->len_pos = len_pos;
	memcpy(&tlz->dic[0], &tlz->dic[1], 14); /* 前方コピーなので、memmove()でなくて大丈夫 */
	tlz->dic[14] = c;

	return c;
}
#else /*PIECE*/
int TinyLZ_get(TinyLZ* tlz);
asm("
	.code
	.align	1
	.global	TinyLZ_get
TinyLZ_get:
	pushn	%r0
	ld.w	%r4, [%r12]+		; %r4  := p = tlz->p
	ld.ub	%r5, [%r12]+		; %r5  := len_pos = tlz->len_pos
	;// %r4  := p
	;// %r5  := len_pos
	;// %r12 := &tlz->dic[0]
	sub	%r5, 16			; if((len_pos -= 16) < 0)
	jrge.d	3
	 ld.w	%r13, 1			; %r13 := 1					*delay*
	 ld.ub	%r5, [%r4]+		;   %r5  := len_pos = *p++
	;// %r4  := p
	;// %r5  := len_pos
	;// %r12 := &tlz->dic[0]
	;// %r13 := 1
	xand	%r9, %r5, 15		; %r9  := len_pos & 15
	jreq.d	6			; if(len_pos & 15) ---------------------------+
	 ld.w	%r14, 14		; %r14 := 14                                  |	*delay*
	 xor	%r9, 15			;   %r9  := 15 - (len_pos & 15)               |
	 add	%r9, %r12		;   %r9  := &tlz->dic[15 - (len_pos & 15)]    |
	 ld.ub	%r0, [%r9]		;   %r0  := c = tlz->dic[15 - (len_pos & 15)] |
	 jp.d	3			; else                                        |
	  sub	%r12, 5			; %r12 := tlz <-------------------------------+	*delay*
	  ld.ub	%r0, [%r4]+		;   %r0  := c = *p++
	;// %r0  := c
	;// %r4  := p
	;// %r5  := len_pos
	;// %r12 := tlz
	;// %r13 := 1
	;// %r14 := 14
	ld.w	[%r12]+, %r4		; tlz->p = p
	ld.b	[%r12]+, %r5		; tlz->len = len_pos
	;// %r0  := c
	;// %r12 := &tlz->dic[0]
	;// %r13 := 1
	;// %r14 := 14
	xcall.d	memcpy			; memcpy(&tlz->dic[0], &tlz->dic[1], 14)
	add	%r13, %r12		; %r13 := &tlz->dic[1]				*delay*
	;// %r0  := c
	;// %r10 := &tlz->dic[0]
	xld.b	[%r10+14], %r0		; tlz->dic[14] = c
	ld.w	%r10, %r0		; return c
	popn	%r0
	ret
");
#endif /*PIECE*/

int
TinyLZ_uncompress(void* _dst, int dst_max, const void* _src, int src_len)
{
	const unsigned char* src = _src;
	      unsigned char* dst = _dst;
	int dst_len = 0;
	//
	TinyLZ tlz;
	int c;

	TinyLZ_init(&tlz, src);
	src += src_len;
	while((tlz.p < src) || (tlz.len_pos > 15)) {
		c = TinyLZ_get(&tlz);
		if(dst_len++ < dst_max) {
			*dst++ = c;
		}
	}

	return dst_len;
}

#ifndef PIECE
int
TinyLZ_compress(void* _dst, int dst_max, const void* _src, int src_len)
{
	      unsigned char* tmp = calloc(src_len + 15, 1);
	const unsigned char* src = memcpy(&tmp[15], _src, src_len);
	const unsigned char* end = src + src_len;
	      unsigned char* dst = _dst;
	int dst_len = 0;
	int imm_len = 0;
	//
	int c;
	int len;
	int pos;
	int dic_len;
	int dic_pos;

	while(src < end) {
		dic_len = 0;
		dic_pos = 0; /* 警告抑制 */
		for(pos = 1; pos <= 15; pos++) {
			for(len = 0; (src < end) && (len < 16); src++, len++) {
				if(*src != *(src - pos)) {
					break;
				}
			}
			src -= len;
			if(len > dic_len) {
				dic_len = len;
				dic_pos = pos;
			}
		}
		//if(dic_len >= 1) {
		//↓ひんぱんにimm/dicが切り替わって圧縮率が低下するのを防ぐため
		if((imm_len == 0 && dic_len >= 1) ||
		   (imm_len != 0 && dic_len >= 2)) {
			/*{{imm*/
			if(imm_len) {
				if(dst_len++ < dst_max) {
					*dst++ = (imm_len - 1) << 4;
				}
				src -= imm_len;
				do {
					c = *src++;
					if(dst_len++ < dst_max) {
						*dst++ = c;
					}
				} while(--imm_len);
			}
			/*}}imm*/
			/*{{dic*/
			if(dst_len++ < dst_max) {
				*dst++ = (dic_len - 1) << 4 | dic_pos;
			}
			/*}}dic*/
			src += dic_len;
		} else {
			src++;
			/*{{imm*/
			if(++imm_len == 16) {
				if(dst_len++ < dst_max) {
					*dst++ = (imm_len - 1) << 4;
				}
				src -= imm_len;
				do {
					c = *src++;
					if(dst_len++ < dst_max) {
						*dst++ = c;
					}
				} while(--imm_len);
			}
			/*}}imm*/
		}
	}
	/*{{imm*/
	if(imm_len) {
		if(dst_len++ < dst_max) {
			*dst++ = (imm_len - 1) << 4;
		}
		src -= imm_len;
		do {
			c = *src++;
			if(dst_len++ < dst_max) {
				*dst++ = c;
			}
		} while(--imm_len);
	}
	/*}}imm*/

	free(tmp);
	return dst_len;
}
#endif /*PIECE*/

