| /* | 
 |  * JFFS2 -- Journalling Flash File System, Version 2. | 
 |  * | 
 |  * Copyright (C) 2004 Patrik Kluba, | 
 |  *                    University of Szeged, Hungary | 
 |  * | 
 |  * For licensing information, see the file 'LICENCE' in the | 
 |  * jffs2 directory. | 
 |  * | 
 |  * $Id: compr_lzari.c,v 1.3 2004/06/23 16:34:39 havasi Exp $ | 
 |  * | 
 |  */ | 
 |  | 
 | /* | 
 |    Lempel-Ziv-Arithmetic coding compression module for jffs2 | 
 |    Based on the LZARI source included in LDS (lossless datacompression sources) | 
 | */ | 
 |  | 
 | /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ | 
 |  | 
 | /* | 
 | Original copyright follows: | 
 |  | 
 | ************************************************************** | 
 | 	LZARI.C -- A Data Compression Program | 
 | 	(tab = 4 spaces) | 
 | ************************************************************** | 
 | 	4/7/1989 Haruhiko Okumura | 
 | 	Use, distribute, and modify this program freely. | 
 | 	Please send me your improved versions. | 
 | 		PC-VAN		SCIENCE | 
 | 		NIFTY-Serve	PAF01022 | 
 | 		CompuServe	74050,1022 | 
 | ************************************************************** | 
 |  | 
 | LZARI.C (c)1989 by Haruyasu Yoshizaki, Haruhiko Okumura, and Kenji Rikitake. | 
 | All rights reserved. Permission granted for non-commercial use. | 
 |  | 
 | */ | 
 |  | 
 | /* | 
 |  | 
 | 	2004-02-18  pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu> | 
 | 				Removed unused variables and fixed no return value | 
 |  | 
 | 	2004-02-16  pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu> | 
 | 				Initial release | 
 |  | 
 | */ | 
 |  | 
 |  | 
 | #include <config.h> | 
 | #if defined(CONFIG_CMD_JFFS2) && defined(CONFIG_JFFS2_LZO_LZARI) | 
 |  | 
 | #include <linux/stddef.h> | 
 | #include <jffs2/jffs2.h> | 
 |  | 
 |  | 
 | #define N			4096	/* size of ring buffer */ | 
 | #define F			60	/* upper limit for match_length */ | 
 | #define THRESHOLD		2	/* encode string into position and length | 
 | 					   if match_length is greater than this */ | 
 | #define NIL			N	/* index for root of binary search trees */ | 
 |  | 
 | static unsigned char | 
 | 		text_buf[N + F - 1];	/* ring buffer of size N, | 
 | 			with extra F-1 bytes to facilitate string comparison */ | 
 |  | 
 | /********** Arithmetic Compression **********/ | 
 |  | 
 | /*  If you are not familiar with arithmetic compression, you should read | 
 | 		I. E. Witten, R. M. Neal, and J. G. Cleary, | 
 | 			Communications of the ACM, Vol. 30, pp. 520-540 (1987), | 
 | 	from which much have been borrowed.  */ | 
 |  | 
 | #define M   15 | 
 |  | 
 | /*	Q1 (= 2 to the M) must be sufficiently large, but not so | 
 | 	large as the unsigned long 4 * Q1 * (Q1 - 1) overflows.  */ | 
 |  | 
 | #define Q1  (1UL << M) | 
 | #define Q2  (2 * Q1) | 
 | #define Q3  (3 * Q1) | 
 | #define Q4  (4 * Q1) | 
 | #define MAX_CUM (Q1 - 1) | 
 |  | 
 | #define N_CHAR  (256 - THRESHOLD + F) | 
 | 	/* character code = 0, 1, ..., N_CHAR - 1 */ | 
 |  | 
 | static unsigned long char_to_sym[N_CHAR], sym_to_char[N_CHAR + 1]; | 
 | static unsigned long | 
 | 	sym_freq[N_CHAR + 1],  /* frequency for symbols */ | 
 | 	sym_cum[N_CHAR + 1],   /* cumulative freq for symbols */ | 
 | 	position_cum[N + 1];   /* cumulative freq for positions */ | 
 |  | 
 | static void StartModel(void)  /* Initialize model */ | 
 | { | 
 | 	unsigned long ch, sym, i; | 
 |  | 
 | 	sym_cum[N_CHAR] = 0; | 
 | 	for (sym = N_CHAR; sym >= 1; sym--) { | 
 | 		ch = sym - 1; | 
 | 		char_to_sym[ch] = sym;  sym_to_char[sym] = ch; | 
 | 		sym_freq[sym] = 1; | 
 | 		sym_cum[sym - 1] = sym_cum[sym] + sym_freq[sym]; | 
 | 	} | 
 | 	sym_freq[0] = 0;  /* sentinel (!= sym_freq[1]) */ | 
 | 	position_cum[N] = 0; | 
 | 	for (i = N; i >= 1; i--) | 
 | 		position_cum[i - 1] = position_cum[i] + 10000 / (i + 200); | 
 | 			/* empirical distribution function (quite tentative) */ | 
 | 			/* Please devise a better mechanism! */ | 
 | } | 
 |  | 
 | static void UpdateModel(unsigned long sym) | 
 | { | 
 | 	unsigned long c, ch_i, ch_sym; | 
 | 	unsigned long i; | 
 | 	if (sym_cum[0] >= MAX_CUM) { | 
 | 		c = 0; | 
 | 		for (i = N_CHAR; i > 0; i--) { | 
 | 			sym_cum[i] = c; | 
 | 			c += (sym_freq[i] = (sym_freq[i] + 1) >> 1); | 
 | 		} | 
 | 		sym_cum[0] = c; | 
 | 	} | 
 | 	for (i = sym; sym_freq[i] == sym_freq[i - 1]; i--) ; | 
 | 	if (i < sym) { | 
 | 		ch_i = sym_to_char[i];    ch_sym = sym_to_char[sym]; | 
 | 		sym_to_char[i] = ch_sym;  sym_to_char[sym] = ch_i; | 
 | 		char_to_sym[ch_i] = sym;  char_to_sym[ch_sym] = i; | 
 | 	} | 
 | 	sym_freq[i]++; | 
 | 	while (--i > 0) sym_cum[i]++; | 
 | 	sym_cum[0]++; | 
 | } | 
 |  | 
 | static unsigned long BinarySearchSym(unsigned long x) | 
 | 	/* 1      if x >= sym_cum[1], | 
 | 	   N_CHAR if sym_cum[N_CHAR] > x, | 
 | 	   i such that sym_cum[i - 1] > x >= sym_cum[i] otherwise */ | 
 | { | 
 | 	unsigned long i, j, k; | 
 |  | 
 | 	i = 1;  j = N_CHAR; | 
 | 	while (i < j) { | 
 | 		k = (i + j) / 2; | 
 | 		if (sym_cum[k] > x) i = k + 1;  else j = k; | 
 | 	} | 
 | 	return i; | 
 | } | 
 |  | 
 | unsigned long BinarySearchPos(unsigned long x) | 
 | 	/* 0 if x >= position_cum[1], | 
 | 	   N - 1 if position_cum[N] > x, | 
 | 	   i such that position_cum[i] > x >= position_cum[i + 1] otherwise */ | 
 | { | 
 | 	unsigned long i, j, k; | 
 |  | 
 | 	i = 1;  j = N; | 
 | 	while (i < j) { | 
 | 		k = (i + j) / 2; | 
 | 		if (position_cum[k] > x) i = k + 1;  else j = k; | 
 | 	} | 
 | 	return i - 1; | 
 | } | 
 |  | 
 | static int Decode(unsigned char *srcbuf, unsigned char *dstbuf, unsigned long srclen, | 
 | 					unsigned long dstlen)	/* Just the reverse of Encode(). */ | 
 | { | 
 | 	unsigned long i, r, j, k, c, range, sym; | 
 | 	unsigned char *ip, *op; | 
 | 	unsigned char *srcend = srcbuf + srclen; | 
 | 	unsigned char *dstend = dstbuf + dstlen; | 
 | 	unsigned char buffer = 0; | 
 | 	unsigned char mask = 0; | 
 | 	unsigned long low = 0; | 
 | 	unsigned long high = Q4; | 
 | 	unsigned long value = 0; | 
 |  | 
 | 	ip = srcbuf; | 
 | 	op = dstbuf; | 
 | 	for (i = 0; i < M + 2; i++) { | 
 | 		value *= 2; | 
 | 		if ((mask >>= 1) == 0) { | 
 | 			buffer = (ip >= srcend) ? 0 : *(ip++); | 
 | 			mask = 128; | 
 | 		} | 
 | 		value += ((buffer & mask) != 0); | 
 | 	} | 
 |  | 
 | 	StartModel(); | 
 | 	for (i = 0; i < N - F; i++) text_buf[i] = ' '; | 
 | 	r = N - F; | 
 |  | 
 | 	while (op < dstend) { | 
 | 		range = high - low; | 
 | 		sym = BinarySearchSym((unsigned long) | 
 | 				(((value - low + 1) * sym_cum[0] - 1) / range)); | 
 | 		high = low + (range * sym_cum[sym - 1]) / sym_cum[0]; | 
 | 		low +=       (range * sym_cum[sym    ]) / sym_cum[0]; | 
 | 		for ( ; ; ) { | 
 | 			if (low >= Q2) { | 
 | 				value -= Q2;  low -= Q2;  high -= Q2; | 
 | 			} else if (low >= Q1 && high <= Q3) { | 
 | 				value -= Q1;  low -= Q1;  high -= Q1; | 
 | 			} else if (high > Q2) break; | 
 | 			low += low;  high += high; | 
 | 			value *= 2; | 
 | 			if ((mask >>= 1) == 0) { | 
 | 				buffer = (ip >= srcend) ? 0 : *(ip++); | 
 | 				mask = 128; | 
 | 			} | 
 | 			value += ((buffer & mask) != 0); | 
 | 		} | 
 | 		c = sym_to_char[sym]; | 
 | 		UpdateModel(sym); | 
 | 		if (c < 256) { | 
 | 			if (op >= dstend) return -1; | 
 | 			*(op++) = c; | 
 | 			text_buf[r++] = c; | 
 | 			r &= (N - 1); | 
 | 		} else { | 
 | 			j = c - 255 + THRESHOLD; | 
 | 			range = high - low; | 
 | 			i = BinarySearchPos((unsigned long) | 
 | 				(((value - low + 1) * position_cum[0] - 1) / range)); | 
 | 			high = low + (range * position_cum[i    ]) / position_cum[0]; | 
 | 			low +=       (range * position_cum[i + 1]) / position_cum[0]; | 
 | 			for ( ; ; ) { | 
 | 				if (low >= Q2) { | 
 | 					value -= Q2;  low -= Q2;  high -= Q2; | 
 | 				} else if (low >= Q1 && high <= Q3) { | 
 | 					value -= Q1;  low -= Q1;  high -= Q1; | 
 | 				} else if (high > Q2) break; | 
 | 				low += low;  high += high; | 
 | 				value *= 2; | 
 | 				if ((mask >>= 1) == 0) { | 
 | 					buffer = (ip >= srcend) ? 0 : *(ip++); | 
 | 					mask = 128; | 
 | 				} | 
 | 				value += ((buffer & mask) != 0); | 
 | 			} | 
 | 			i = (r - i - 1) & (N - 1); | 
 | 			for (k = 0; k < j; k++) { | 
 | 				c = text_buf[(i + k) & (N - 1)]; | 
 | 				if (op >= dstend) return -1; | 
 | 				*(op++) = c; | 
 | 				text_buf[r++] = c; | 
 | 				r &= (N - 1); | 
 | 			} | 
 | 		} | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | int lzari_decompress(unsigned char *data_in, unsigned char *cpage_out, | 
 | 		      u32 srclen, u32 destlen) | 
 | { | 
 |     return Decode(data_in, cpage_out, srclen, destlen); | 
 | } | 
 | #endif |