File: gunzip_mod.c

package info (click to toggle)
m68k-vme-tftplilo 1.1.2-2
  • links: PTS
  • area: main
  • in suites: woody
  • size: 332 kB
  • ctags: 674
  • sloc: ansic: 5,858; makefile: 81
file content (306 lines) | stat: -rw-r--r-- 8,355 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/*
 * gunzip_mod.c -- Decompressing module for VME bootstrap
 *
 * Copyright (c) 1997 by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
 *
 * Modified by Nick Holgate to suit VME Linux/m68k TFTP Loader
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of this archive
 * for more details.
 */

#include "defs.h"

/*
 * gzip declarations
 */

#define OF(args)  args

#define memzero(s, n)     mem_clear ((s), (n))

typedef unsigned char  uch;
typedef unsigned short ush;
typedef unsigned long  ulg;

#define INBUFSIZ	(32*1024)	/* input buffer size; for optimum performance*/
								/* the same or a multiple of other buffer	*/
								/* sizes, and memory_mod uses 32k			*/
#define WSIZE		(32*1024)	/* window size--must be a power of two, and */
								/* at least 32K for zip's deflate method	*/
#define CHANGING_WINDOW			/* this tells inflate.c that 'window' can	*/
								/* change in flush_window(), and that the	*/
								/* previous window (needed to copy data) is	*/
								/* preserved in 'previous_window'			*/

/***************************** Prototypes *****************************/

static int gunzip_open( void *data, long count );
static long gunzip_fillbuf( void *buf );
static int gunzip_close( void );
static int call_gunzip( void );
static void gzip_mark( void **ptr );
static void gzip_release( void **ptr );
static int fill_inbuf( void );
static void flush_window( void );
static void error( char *x );

/************************* End of Prototypes **************************/

MODULE gunzip_mod = {
	"gunzip",					/* name */
	WSIZE,						/* max. 512 bytes per TFTP packet */
	gunzip_open,
	gunzip_fillbuf,
	NULL,						/* cannot skip */
	gunzip_close,
	MOD_REST_INIT
};

/* special stack for gunzip() function */
static char *gunzip_stack;
/* some globals for storing values between stack switches */
static long gunzip_sp, gunzip_jumpback, main_sp, main_fp;
/* size of stack for gunzip(); 4k should be more than enough. (I guess 2k
 * would suffice also, but I don't want to track down stack overflows...) */
#define GUNZIP_STACK_SIZE	(4*1024)

static uch *inbuf;
static uch *window;
static uch *previous_window;

static unsigned insize = 0;  /* valid bytes in inbuf */
static unsigned inptr = 0;   /* index of next byte to be processed in inbuf */
static unsigned outcnt = 0;  /* bytes in output buffer */
static int exit_code = 0;
static long bytes_out = 0;
static int made_crc_table = 0;

#define get_byte()  (inptr < insize ? inbuf[inptr++] : fill_inbuf())

/* Diagnostic functions (stubbed out) */
#define Assert(cond,msg)
#define Trace(x)
#define Tracev(x)
#define Tracevv(x)
#define Tracec(c,x)
#define Tracecv(c,x)

#define STATIC static

#ifdef DEBUG_GUNZIP
FILE *xfile;
#endif

#include "inflate.c"

static int gunzip_open( void *data, long count )
{
	int rv;
	unsigned char buf[2];
	
	insize    = 0;  /* valid bytes in inbuf */
	inptr     = 0;  /* index of next byte to be processed in inbuf */
	outcnt    = 0;  /* bytes in output buffer */
	exit_code = 0;
	bytes_out = 0;
	crc       = (ulg)0xffffffffL; /* shift register contents */
	if (!made_crc_table)
	{
		++made_crc_table;
		makecrc();
	}

	/* try opening downstream channel */
	if (sopen( data, count ) < 0)
		return( -1 );

	/* check if data is gzipped */
	rv = sread( buf, 2 );
	sseek( 0, SEEK_SET );
	if (rv < 2) {
		printf( "File shorter than 2 bytes, can't test for gzip\n" );
		return( -1 );
	}
    if (buf[0] != 037 || (buf[1] != 0213 && buf[1] != 0236))
		/* not compressed, remove this module from the stream */
		return( 1 );

	if (!(gunzip_stack = malloc( GUNZIP_STACK_SIZE ))) {
		printf( "Out of memory for gunzip stack!\n" );
		return( -1 );
	}
	gunzip_sp = 0;

	if (!(inbuf = malloc( INBUFSIZ ))) {
		printf( "Out of memory for gunzip input buffer!\n" );
		return( -1 );
	}

#ifdef DEBUG_GUNZIP
	xfile = fopen( "xx", "wb" );
#endif
	return( 0 );
}

static __inline__ unsigned long getsp( void )
{
	ulg ret;
	__asm__ __volatile__
		( "movl	%%sp,%0" : "=r" (ret) : );
	return( ret );
}

static long gunzip_fillbuf( void *buf )
{
	previous_window = window;
	window = buf;
	return( call_gunzip() );
}

static int gunzip_close( void )
{
	free( gunzip_stack );
	free( inbuf );
	sclose();
#ifdef DEBUG_GUNZIP
	fclose( xfile );
#endif
	return( 0 );
}

static void gzip_mark( void **ptr )
{
}

static void gzip_release( void **ptr )
{
}

/* This function does most of the magic for implementing a second stack for
 * gunzip(). This is necessary because lib/inflate.c only provides a callback
 * (flush_window()) that can store data away. But we need to actually return
 * from out fillbuf method to implement the streaming. (Otherwise, the whole
 * file would have to be uncompressed and buffered as a whole.)
 *
 * The solution to this problem is a second stack on which gunzip() runs. If
 * flush_window() is called, it saves state and then switches back to the main
 * stack, from which we can return to our caller. For resuming at the point
 * where it left off, we simply restore the gunzip stack again. A second
 * return value in d1 distinguishes returns from inside flush_window() and
 * "normal" returns from gunzip() itself. The latter either indicate EOF or
 * error.
 */
static int call_gunzip( void )
{
	int rv = 0;
	
	/* avoid warnings about unused variables (appear only in asm) */
	(void)&main_sp;
	(void)&main_fp;
	(void)&gunzip_jumpback;
	(void)gunzip;
	
	if (!gunzip_sp) {
		register int asm_rv __asm__ ("d0");
		/* gunzip() wasn't called before: set up its stack and call the main
		 * function */
		__asm__ __volatile__ (
			"movl	%%sp,main_sp\n\t"	/* save current sp */
			"movl	%%a6,main_fp\n\t"	/* and fp */
			"movl	%1,%%sp\n\t"		/* move to new stack */
			"jbsr	gunzip\n\t"			/* call the function */
		"_return_from_flush:\n\t" 		/* point of coming back */
			"movl	main_sp,%%sp\n\t"	/* restore main sp */
			"movl	main_fp,%%a6" 		/* and fp */
			: "=d" (asm_rv)
			: "g" (gunzip_stack+GUNZIP_STACK_SIZE-sizeof(int))
			: "d1", "d2", "d3", "d4", "d5", "d6", "d7", "a0", "a1", "a2", "a3",
			  "a4", "a5", "memory" /* all regs possibly clobbered */
			);
		rv = asm_rv;
	}
	else {
		/* gunzip() is already active, jump to where it left off */
		__asm__ __volatile__ (
			"movl	%%sp,main_sp\n\t"	/* save current sp */
			"movl	%%a6,main_fp\n\t"	/* and fp */
			"movl	gunzip_jumpback,%%a0\n\t"
			"jmp	%%a0@"
			: /* no outputs */
			: /* no inputs */
			: "d0", "d1", "a0", "a1", "memory" /* clobbered regs */
			);
	}
	return( rv );		/* 0 for EOF, -1 for error, > 0 if from flush_window */
}

/* This is used in flush_window() to return into call_gunzip() */
#define RETURN_TO_MAIN_STACK()												\
	__asm__ __volatile__ (													\
		"movml	%%d2-%%d7/%%a2-%%a6,%%sp@-\n\t"	/* save call-saved regs */	\
		"movl	%%sp,gunzip_sp\n\t" 			/* save current sp */		\
		"movl	#1f,gunzip_jumpback\n\t" 		/* save return address */	\
		"movl	outcnt,%%d0\n\t"				/* return value */			\
		"jmp	_return_from_flush\n" 			/* and return... */			\
	"1:\tmovl	gunzip_sp,%%sp\n\t" 			/* restore sp */			\
		"movml	%%sp@+,%%d2-%%d7/%%a2-%%a6"		/* restore registers */		\
		: /* no outputs */													\
		: /* no inputs */													\
		: "d0", "d1", "a0", "a1", "memory" /* clobbered regs */				\
		)

/*
 * Fill the input buffer. This is called only when the buffer is empty
 * and at least one byte is really needed.
 */
static int fill_inbuf( void )
{
    if (exit_code)
		return( -1 );

    insize = sread( inbuf, INBUFSIZ );
    if (insize <= 0)
		return( -1 );

    inptr = 1;
    return( inbuf[0] );
}

/*
 * Write the output window window[0..outcnt-1] and update crc and bytes_out.
 * (Used for the decompressed data only.)
 */
static void flush_window( void )
{
    ulg c = crc;         /* temporary variable */
    unsigned n;
    uch *in, ch;
    
    in = window;
    for( n = 0; n < outcnt; n++ ) {
	    ch = *in++;
	    c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
    }
    crc = c;
	bytes_out += (ulg)outcnt;
#ifdef DEBUG_GUNZIP
	fwrite( window, outcnt, 1, xfile );
#endif

	/* return to call_gunzip(), next call to it resumes here */
	RETURN_TO_MAIN_STACK();
	
	outcnt = 0;
}

static void error( char *x )
{
    printf( "\n%s\n", x);
    exit_code = 1;
}

/* Local Variables: */
/* tab-width: 4     */
/* End:             */