xref: /plan9/sys/src/cmd/gs/src/slzwe.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1*593dc095SDavid du Colombier /* Copyright (C) 1993 Aladdin Enterprises.  All rights reserved.
2*593dc095SDavid du Colombier 
3*593dc095SDavid du Colombier   This file is part of Aladdin Ghostscript.
4*593dc095SDavid du Colombier 
5*593dc095SDavid du Colombier   Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
6*593dc095SDavid du Colombier   or distributor accepts any responsibility for the consequences of using it,
7*593dc095SDavid du Colombier   or for whether it serves any particular purpose or works at all, unless he
8*593dc095SDavid du Colombier   or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
9*593dc095SDavid du Colombier   License (the "License") for full details.
10*593dc095SDavid du Colombier 
11*593dc095SDavid du Colombier   Every copy of Aladdin Ghostscript must include a copy of the License,
12*593dc095SDavid du Colombier   normally in a plain ASCII text file named PUBLIC.  The License grants you
13*593dc095SDavid du Colombier   the right to copy, modify and redistribute Aladdin Ghostscript, but only
14*593dc095SDavid du Colombier   under certain conditions described in the License.  Among other things, the
15*593dc095SDavid du Colombier   License requires that the copyright notice and this notice be preserved on
16*593dc095SDavid du Colombier   all copies.
17*593dc095SDavid du Colombier */
18*593dc095SDavid du Colombier 
19*593dc095SDavid du Colombier /* $Id: slzwe.c,v 1.4 2005/04/04 23:00:24 igor Exp $ */
20*593dc095SDavid du Colombier /* LZW encoding filter */
21*593dc095SDavid du Colombier #include "stdio_.h"	/* includes std.h */
22*593dc095SDavid du Colombier #include "gdebug.h"
23*593dc095SDavid du Colombier #include "strimpl.h"
24*593dc095SDavid du Colombier #include "slzwx.h"
25*593dc095SDavid du Colombier 
26*593dc095SDavid du Colombier /********************************************************/
27*593dc095SDavid du Colombier /* LZW routines are based on:				*/
28*593dc095SDavid du Colombier /* Dr. Dobbs Journal --- Oct. 1989. 			*/
29*593dc095SDavid du Colombier /* Article on LZW Data Compression by Mark R. Nelson 	*/
30*593dc095SDavid du Colombier /********************************************************/
31*593dc095SDavid du Colombier 
32*593dc095SDavid du Colombier /* Define the special codes */
33*593dc095SDavid du Colombier #define code_reset 256
34*593dc095SDavid du Colombier #define code_eod 257
35*593dc095SDavid du Colombier #define code_0 258			/* first assignable code */
36*593dc095SDavid du Colombier 
37*593dc095SDavid du Colombier /* Import the stream closing procedure */
38*593dc095SDavid du Colombier extern stream_proc_release(s_LZW_release);
39*593dc095SDavid du Colombier 
40*593dc095SDavid du Colombier typedef struct lzw_encode_s {
41*593dc095SDavid du Colombier 	byte datum;			/* last byte of this code */
42*593dc095SDavid du Colombier 	ushort prefix;			/* code for prefix of this code */
43*593dc095SDavid du Colombier } lzw_encode;
44*593dc095SDavid du Colombier 
45*593dc095SDavid du Colombier #define encode_max 4095		/* max # of codes, must be */
46*593dc095SDavid du Colombier 				/* > code_0 and <= 4095 */
47*593dc095SDavid du Colombier #define hash_size (encode_max + encode_max / 4)
48*593dc095SDavid du Colombier 
49*593dc095SDavid du Colombier struct lzw_encode_table_s {
50*593dc095SDavid du Colombier 	lzw_encode encode[encode_max];
51*593dc095SDavid du Colombier 	ushort hashed[hash_size];
52*593dc095SDavid du Colombier };
53*593dc095SDavid du Colombier gs_private_st_simple(st_lzwe_table, lzw_encode_table, "lzw_encode_table");
54*593dc095SDavid du Colombier 
55*593dc095SDavid du Colombier /* Hashing function */
56*593dc095SDavid du Colombier #define encode_hash(code, chr)\
57*593dc095SDavid du Colombier   ((uint)((code) * 59 + (chr) * ((hash_size / 256) | 1)) % hash_size)
58*593dc095SDavid du Colombier 
59*593dc095SDavid du Colombier /* Internal routine to put a code into the output buffer. */
60*593dc095SDavid du Colombier /* Let S = ss->code_size, M = ss->next_code, N = 1 << M. */
61*593dc095SDavid du Colombier /* Relevant invariants: 9 <= S <= 12; N / 2 <= M < N; 0 <= code < N; */
62*593dc095SDavid du Colombier /* 1 <= ss->bits_left <= 8; only the rightmost (8 - ss->bits_left) */
63*593dc095SDavid du Colombier /* bits of ss->bits contain valid data. */
64*593dc095SDavid du Colombier private byte *
lzw_put_code(register stream_LZW_state * ss,byte * q,uint code)65*593dc095SDavid du Colombier lzw_put_code(register stream_LZW_state *ss, byte *q, uint code)
66*593dc095SDavid du Colombier {	uint size = ss->code_size;
67*593dc095SDavid du Colombier 	byte cb = (ss->bits << ss->bits_left) +
68*593dc095SDavid du Colombier 		(code >> (size - ss->bits_left));
69*593dc095SDavid du Colombier 	if_debug2('W', "[w]writing 0x%x,%d\n", code, ss->code_size);
70*593dc095SDavid du Colombier 	*++q = cb;
71*593dc095SDavid du Colombier 	if ( (ss->bits_left += 8 - size) <= 0 )
72*593dc095SDavid du Colombier 	{	*++q = code >> -ss->bits_left;
73*593dc095SDavid du Colombier 		ss->bits_left += 8;
74*593dc095SDavid du Colombier 	}
75*593dc095SDavid du Colombier 	ss->bits = code;
76*593dc095SDavid du Colombier 	return q;
77*593dc095SDavid du Colombier }
78*593dc095SDavid du Colombier 
79*593dc095SDavid du Colombier /* Internal routine to reset the encoding table */
80*593dc095SDavid du Colombier private void
lzw_reset_encode(stream_LZW_state * ss)81*593dc095SDavid du Colombier lzw_reset_encode(stream_LZW_state *ss)
82*593dc095SDavid du Colombier {	register int c;
83*593dc095SDavid du Colombier 	lzw_encode_table *table = ss->table.encode;
84*593dc095SDavid du Colombier 	ss->next_code = code_0;
85*593dc095SDavid du Colombier 	ss->code_size = 9;
86*593dc095SDavid du Colombier 	ss->prev_code = code_eod;
87*593dc095SDavid du Colombier 	for ( c = 0; c < hash_size; c++ )
88*593dc095SDavid du Colombier 		table->hashed[c] = code_eod;
89*593dc095SDavid du Colombier 	for ( c = 0; c < 256; c++ )
90*593dc095SDavid du Colombier 	{	lzw_encode *ec = &table->encode[c];
91*593dc095SDavid du Colombier 		register ushort *tc = &table->hashed[encode_hash(code_eod, c)];
92*593dc095SDavid du Colombier 		while ( *tc != code_eod )
93*593dc095SDavid du Colombier 		  if ( ++tc == &table->hashed[hash_size] )
94*593dc095SDavid du Colombier 		    tc = &table->hashed[0];
95*593dc095SDavid du Colombier 		*tc = c;
96*593dc095SDavid du Colombier 		ec->datum = c, ec->prefix = code_eod;
97*593dc095SDavid du Colombier 	}
98*593dc095SDavid du Colombier 	table->encode[code_eod].prefix = code_reset;	/* guarantee no match */
99*593dc095SDavid du Colombier }
100*593dc095SDavid du Colombier 
101*593dc095SDavid du Colombier #define ss ((stream_LZW_state *)st)
102*593dc095SDavid du Colombier 
103*593dc095SDavid du Colombier /* Initialize LZWEncode filter */
104*593dc095SDavid du Colombier private int
s_LZWE_init(stream_state * st)105*593dc095SDavid du Colombier s_LZWE_init(stream_state *st)
106*593dc095SDavid du Colombier {	ss->bits_left = 8;
107*593dc095SDavid du Colombier 	ss->table.encode = gs_alloc_struct(st->memory,
108*593dc095SDavid du Colombier 			lzw_encode_table, &st_lzwe_table, "LZWEncode init");
109*593dc095SDavid du Colombier 	if ( ss->table.encode == 0 )
110*593dc095SDavid du Colombier 		return ERRC;		/****** WRONG ******/
111*593dc095SDavid du Colombier 	ss->first = true;
112*593dc095SDavid du Colombier 	lzw_reset_encode(ss);
113*593dc095SDavid du Colombier 	return 0;
114*593dc095SDavid du Colombier }
115*593dc095SDavid du Colombier 
116*593dc095SDavid du Colombier /* Process a buffer */
117*593dc095SDavid du Colombier private int
s_LZWE_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * pw,bool last)118*593dc095SDavid du Colombier s_LZWE_process(stream_state *st, stream_cursor_read *pr,
119*593dc095SDavid du Colombier   stream_cursor_write *pw, bool last)
120*593dc095SDavid du Colombier {	register const byte *p = pr->ptr;
121*593dc095SDavid du Colombier 	const byte *rlimit = pr->limit;
122*593dc095SDavid du Colombier 	register byte *q = pw->ptr;
123*593dc095SDavid du Colombier 	byte *wlimit = pw->limit;
124*593dc095SDavid du Colombier 	int code = ss->prev_code;
125*593dc095SDavid du Colombier 	lzw_encode_table *table = ss->table.encode;
126*593dc095SDavid du Colombier 	ushort *table_end = &table->hashed[hash_size];
127*593dc095SDavid du Colombier 	int status = 0;
128*593dc095SDavid du Colombier 	int limit_code;
129*593dc095SDavid du Colombier #define set_limit_code()\
130*593dc095SDavid du Colombier   limit_code = (1 << ss->code_size) - ss->EarlyChange;\
131*593dc095SDavid du Colombier   if ( limit_code > encode_max ) limit_code = encode_max
132*593dc095SDavid du Colombier 	set_limit_code();
133*593dc095SDavid du Colombier 	if ( ss->first )
134*593dc095SDavid du Colombier 	{	/* Emit the initial reset code. */
135*593dc095SDavid du Colombier 		if ( wlimit - q < 2 )
136*593dc095SDavid du Colombier 			return 1;
137*593dc095SDavid du Colombier 		q = lzw_put_code(ss, q, code_reset);
138*593dc095SDavid du Colombier 		ss->first = false;
139*593dc095SDavid du Colombier 	}
140*593dc095SDavid du Colombier 	while ( p < rlimit )
141*593dc095SDavid du Colombier 	{	byte c = p[1];
142*593dc095SDavid du Colombier 		ushort *tp;
143*593dc095SDavid du Colombier 		for ( tp = &table->hashed[encode_hash(code, c)]; ; )
144*593dc095SDavid du Colombier 		{	lzw_encode *ep = &table->encode[*tp];
145*593dc095SDavid du Colombier 			if ( ep->prefix == code && ep->datum == c )
146*593dc095SDavid du Colombier 			{	code = *tp;
147*593dc095SDavid du Colombier 				p++;
148*593dc095SDavid du Colombier 				break;
149*593dc095SDavid du Colombier 			}
150*593dc095SDavid du Colombier 			else if ( *tp != code_eod )
151*593dc095SDavid du Colombier 			{	if ( ++tp == table_end )
152*593dc095SDavid du Colombier 				  tp = &table->hashed[0]; /* wrap around */
153*593dc095SDavid du Colombier 			}
154*593dc095SDavid du Colombier 			else
155*593dc095SDavid du Colombier 			{	/* end of recognized sequence */
156*593dc095SDavid du Colombier 				if ( wlimit - q <= 4 )
157*593dc095SDavid du Colombier 				{	status = 1;
158*593dc095SDavid du Colombier 					goto out;
159*593dc095SDavid du Colombier 				}
160*593dc095SDavid du Colombier 				q = lzw_put_code(ss, q, code);
161*593dc095SDavid du Colombier 				if ( ss->next_code == limit_code )
162*593dc095SDavid du Colombier 				{	/* Reached power of 2 or limit. */
163*593dc095SDavid du Colombier 					/* Determine which. */
164*593dc095SDavid du Colombier 					if ( ss->next_code == encode_max )
165*593dc095SDavid du Colombier 					{	q = lzw_put_code(ss, q, code_reset);
166*593dc095SDavid du Colombier 						lzw_reset_encode(ss);
167*593dc095SDavid du Colombier 						set_limit_code();
168*593dc095SDavid du Colombier 						goto cx;
169*593dc095SDavid du Colombier 					}
170*593dc095SDavid du Colombier 					ss->code_size++;
171*593dc095SDavid du Colombier 					set_limit_code();
172*593dc095SDavid du Colombier 				}
173*593dc095SDavid du Colombier 				if_debug3('W', "[W]encoding 0x%x=0x%x+%c\n",
174*593dc095SDavid du Colombier 					  ss->next_code, code, c);
175*593dc095SDavid du Colombier 				*tp = ss->next_code++;
176*593dc095SDavid du Colombier 				ep = &table->encode[*tp];
177*593dc095SDavid du Colombier 				ep->datum = c;
178*593dc095SDavid du Colombier 				ep->prefix = code;
179*593dc095SDavid du Colombier cx:				code = code_eod;
180*593dc095SDavid du Colombier 				break;
181*593dc095SDavid du Colombier 			}
182*593dc095SDavid du Colombier 		}
183*593dc095SDavid du Colombier 	}
184*593dc095SDavid du Colombier 	if ( last && status == 0 )
185*593dc095SDavid du Colombier 	{	if ( wlimit - q < 4 )
186*593dc095SDavid du Colombier 			status = 1;
187*593dc095SDavid du Colombier 		else
188*593dc095SDavid du Colombier 		  {	if ( code != code_eod )
189*593dc095SDavid du Colombier 			  {	q = lzw_put_code(ss, q, code);	/* put out final code */
190*593dc095SDavid du Colombier 			  }
191*593dc095SDavid du Colombier 			q = lzw_put_code(ss, q, code_eod);
192*593dc095SDavid du Colombier 			if ( ss->bits_left < 8 )
193*593dc095SDavid du Colombier 			  *++q = ss->bits << ss->bits_left;  /* final byte */
194*593dc095SDavid du Colombier 		  }
195*593dc095SDavid du Colombier 	}
196*593dc095SDavid du Colombier out:	ss->prev_code = code;
197*593dc095SDavid du Colombier 	pr->ptr = p;
198*593dc095SDavid du Colombier 	pw->ptr = q;
199*593dc095SDavid du Colombier 	return status;
200*593dc095SDavid du Colombier }
201*593dc095SDavid du Colombier 
202*593dc095SDavid du Colombier #undef ss
203*593dc095SDavid du Colombier 
204*593dc095SDavid du Colombier /* Stream template */
205*593dc095SDavid du Colombier const stream_template s_LZWE_template =
206*593dc095SDavid du Colombier {	&st_LZW_state, s_LZWE_init, s_LZWE_process, 1, 4, s_LZW_release,
207*593dc095SDavid du Colombier 	s_LZW_set_defaults
208*593dc095SDavid du Colombier };
209