ia64/xen-unstable

view tools/xfrd/lzi_stream.c @ 1921:24ecc060e9d7

bitkeeper revision 1.1108.21.1 (41062740xHG36OEbpVAmVX5N9WCaNw)

make vmlinuz really stripped
author cl349@freefall.cl.cam.ac.uk
date Tue Jul 27 09:58:24 2004 +0000 (2004-07-27)
parents 0e23f01219c6
children 59a924789d34 a01199a95070
line source
1 /*
2 * Copyright (C) 2003 Hewlett-Packard Company.
3 *
4 * This library is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation; either version 2.1 of the License, or
7 * (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
19 /** @file
20 * An IOStream implementation using LZI to provide compression and decompression.
21 * This is designed to provide compression without output latency.
22 * Flushing an LZI stream flushes all pending data to the underlying stream.
23 * This is essential for stream-based (e.g. networked) applications.
24 *
25 * A compressed data stream is a sequence of blocks.
26 * Each block is the block size followed by the compressed data.
27 * The last block has size zero.
28 * Sizes are 4-byte unsigned in network order.
29 *
30 * This format allows compressed data to be read from a stream without reading
31 * past the logical end of compressed data.
32 *
33 * @author Mike Wray <mike.wray@hpl.hp.com>
34 */
35 #ifndef __KERNEL__
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <errno.h>
40 #include <string.h>
42 #include "zlib.h"
44 #include "allocate.h"
45 #include "lzi_stream.h"
46 #include "file_stream.h"
47 #include "marshal.h"
49 #define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZI>%s" fmt, __FUNCTION__, ##args)
50 #define wprintf(fmt, args...) fprintf(stderr, "[WARN] LZI>%s" fmt, __FUNCTION__, ##args)
51 #define iprintf(fmt, args...) fprintf(stdout, "[INFO] LZI>%s" fmt, __FUNCTION__, ##args)
52 #define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZI>%s" fmt, __FUNCTION__, ##args)
54 static int lzi_read(IOStream *s, void *buf, size_t n);
55 static int lzi_write(IOStream *s, const void *buf, size_t n);
56 static int lzi_error(IOStream *s);
57 static int lzi_close(IOStream *s);
58 static void lzi_free(IOStream *s);
59 static int lzi_flush(IOStream *s);
61 enum {
62 LZI_WRITE = 1,
63 LZI_READ = 2,
64 };
66 /** Methods used by a gzFile* IOStream. */
67 static const IOMethods lzi_methods = {
68 read: lzi_read,
69 write: lzi_write,
70 error: lzi_error,
71 close: lzi_close,
72 free: lzi_free,
73 flush: lzi_flush,
74 };
76 #define BUFFER_SIZE (512 * 1024)
78 typedef struct LZIState {
79 z_stream zstream;
80 void *inbuf;
81 uint32_t inbuf_size;
82 void *outbuf;
83 uint32_t outbuf_size;
84 /** Underlying stream for I/O. */
85 IOStream *io;
86 /** Flags. */
87 int flags;
88 /** Error indicator. */
89 int error;
90 int eof;
91 int plain_bytes;
92 int comp_bytes;
93 int zstream_initialized;
94 int flushed;
95 } LZIState;
97 static inline int LZIState_writeable(LZIState *s){
98 return (s->flags & LZI_WRITE) != 0;
99 }
101 static inline int LZIState_readable(LZIState *s){
102 return (s->flags & LZI_READ) != 0;
103 }
105 void LZIState_free(LZIState *z){
106 if(!z) return;
107 if(z->zstream_initialized){
108 if(LZIState_writeable(z)){
109 deflateEnd(&z->zstream);
110 } else if(LZIState_readable(z)){
111 inflateEnd(&z->zstream);
112 }
113 }
114 deallocate(z->inbuf);
115 deallocate(z->outbuf);
116 deallocate(z);
117 }
119 static int mode_flags(const char *mode, int *flags){
120 int err = 0;
121 int r=0, w=0;
122 if(!mode){
123 err = -EINVAL;
124 goto exit;
125 }
126 for(; *mode; mode++){
127 if(*mode == 'w') w = 1;
128 if(*mode == 'r') r = 1;
129 }
130 if(r + w != 1){
131 err = -EINVAL;
132 goto exit;
133 }
134 if(r) *flags |= LZI_READ;
135 if(w) *flags |= LZI_WRITE;
136 exit:
137 return err;
138 }
140 /** Get the stream state.
141 *
142 * @param s lzi stream
143 * @return stream state.
144 */
145 static inline LZIState * lzi_state(IOStream *io){
146 return (LZIState*)io->data;
147 }
149 IOStream *lzi_stream_io(IOStream *io){
150 LZIState *s = lzi_state(io);
151 return s->io;
152 }
154 static inline void set_error(LZIState *s, int err){
155 if(err < 0 && !s->error){
156 s->error = err;
157 }
158 }
160 static int zerror(LZIState *s, int err){
161 if(err){
162 //dprintf("> err=%d\n", err);
163 if(err < 0) set_error(s, -EIO);
164 }
165 return s->error;
166 }
168 int lzi_stream_plain_bytes(IOStream *io){
169 LZIState *s = lzi_state(io);
170 return s->plain_bytes;
171 }
173 int lzi_stream_comp_bytes(IOStream *io){
174 LZIState *s = lzi_state(io);
175 return s->comp_bytes;
176 }
178 float lzi_stream_ratio(IOStream *io){
179 LZIState *s = lzi_state(io);
180 float ratio = 0.0;
181 if(s->comp_bytes){
182 ratio = ((float) s->comp_bytes)/((float) s->plain_bytes);
183 }
184 return ratio;
185 }
187 static int alloc(void **p, int n){
188 *p = allocate(n);
189 return (p ? 0 : -ENOMEM);
190 }
192 LZIState * LZIState_new(IOStream *io, int flags){
193 int err = -ENOMEM;
194 int zlevel = Z_BEST_SPEED; // Level 1 compression - fastest.
195 int zstrategy = Z_DEFAULT_STRATEGY;
196 int zwindow = MAX_WBITS;
197 int zmemory = 8;
198 LZIState *z = ALLOCATE(LZIState);
200 //dprintf(">\n");
201 if(!z) goto exit;
202 z->io = io;
203 z->flags = flags;
205 if(LZIState_writeable(z)){
206 z->outbuf_size = BUFFER_SIZE;
207 /* windowBits is passed < 0 to suppress zlib header */
208 err = deflateInit2(&z->zstream, zlevel, Z_DEFLATED, -zwindow, zmemory, zstrategy);
209 if (err != Z_OK) goto exit;
210 z->zstream_initialized = 1;
211 err = alloc(&z->outbuf, z->outbuf_size);
212 if(err) goto exit;
213 z->zstream.next_out = z->outbuf;
214 z->zstream.avail_out = z->outbuf_size;
215 } else {
216 z->inbuf_size = BUFFER_SIZE;
217 err = alloc(&z->inbuf, z->inbuf_size);
218 if(err) goto exit;
219 ///z->zstream.next_in = z->inbuf;
221 /* windowBits is passed < 0 to tell that there is no zlib header.
222 * Note that in this case inflate *requires* an extra "dummy" byte
223 * after the compressed stream in order to complete decompression and
224 * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
225 * present after the compressed stream.
226 */
227 err = inflateInit2(&z->zstream, -zwindow);
228 if(err != Z_OK) goto exit;
229 z->zstream_initialized = 1;
230 }
232 exit:
233 if(err){
234 LZIState_free(z);
235 z = NULL;
236 }
237 //dprintf("< z=%p\n", z);
238 return z;
239 }
241 int read_block(LZIState *s){
242 int err = 0, k = 0;
243 //dprintf(">\n");
244 if(s->eof) goto exit;
245 err = unmarshal_uint32(s->io, &k);
246 if(err) goto exit;
247 if(k > s->inbuf_size){
248 err = -EINVAL;
249 goto exit;
250 }
251 if(k){
252 err = unmarshal_bytes(s->io, s->inbuf, k);
253 if(err) goto exit;
254 } else {
255 s->eof = 1;
256 }
257 s->zstream.avail_in = k;
258 s->zstream.next_in = s->inbuf;
259 s->comp_bytes += 4;
260 s->comp_bytes += k;
261 exit:
262 //dprintf("< err=%d\n", err);
263 return err;
264 }
266 int write_block(LZIState *s){
267 int err = 0;
268 int k = ((char*)s->zstream.next_out) - ((char*)s->outbuf);
269 //int k2 = s->outbuf_size - s->zstream.avail_out;
270 //dprintf("> k=%d k2=%d\n", k, k2);
271 if(!k) goto exit;
272 err = marshal_uint32(s->io, k);
273 if(err) goto exit;
274 err = marshal_bytes(s->io, s->outbuf, k);
275 if(err) goto exit;
276 s->zstream.next_out = s->outbuf;
277 s->zstream.avail_out = s->outbuf_size;
278 s->comp_bytes += 4;
279 s->comp_bytes += k;
280 exit:
281 //dprintf("< err=%d\n", err);
282 return err;
283 }
285 int write_terminator(LZIState *s){
286 int err = 0;
287 char c = 0;
288 err = marshal_uint32(s->io, 1);
289 if(err) goto exit;
290 err = marshal_bytes(s->io, &c, 1);
291 if(err) goto exit;
292 err = marshal_uint32(s->io, 0);
293 if(err) goto exit;
294 s->comp_bytes += 9;
295 exit:
296 return err;
297 }
299 /** Write to the underlying stream using fwrite();
300 *
301 * @param io destination
302 * @param buf data
303 * @param n number of bytes to write
304 * @return number of bytes written
305 */
306 static int lzi_write(IOStream *io, const void *buf, size_t n){
307 int err = 0;
308 LZIState *s = lzi_state(io);
310 //dprintf("> buf=%p n=%d\n", buf, n);
311 if(!LZIState_writeable(s)){
312 err = -EINVAL;
313 goto exit;
314 }
315 s->flushed = 0;
316 s->zstream.next_in = (void*)buf;
317 s->zstream.avail_in = n;
318 while(s->zstream.avail_in){
319 if(s->zstream.avail_out == 0){
320 err = write_block(s);
321 if(err) goto exit;
322 }
323 //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
324 //dprintf("> 1 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out);
325 err = zerror(s, deflate(&s->zstream, Z_NO_FLUSH));
326 //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
327 //dprintf("> 2 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out);
328 if(err) goto exit;
329 }
330 err = n;
331 s->plain_bytes += n;
332 exit:
333 //dprintf("< err=%d\n", err);
334 return err;
335 }
338 /** Read from the underlying stream.
339 *
340 * @param io input
341 * @param buf where to put input
342 * @param n number of bytes to read
343 * @return number of bytes read
344 */
345 static int lzi_read(IOStream *io, void *buf, size_t n){
346 int err, zerr;
347 LZIState *s = lzi_state(io);
349 //dprintf("> n=%d\n", n);
350 if(!LZIState_readable(s)){
351 err = -EINVAL;
352 goto exit;
353 }
354 s->zstream.next_out = buf;
355 s->zstream.avail_out = n;
356 while(s->zstream.avail_out){
357 if(s->zstream.avail_in == 0){
358 err = read_block(s);
359 }
360 //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
361 zerr = inflate(&s->zstream, Z_NO_FLUSH);
362 //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
363 if(zerr == Z_STREAM_END) break;
364 //dprintf("> zerr=%d\n", zerr);
365 err = zerror(s, zerr);
366 if(err) goto exit;
367 }
368 err = n - s->zstream.avail_out;
369 s->plain_bytes += err;
370 exit:
371 set_error(s, err);
372 //dprintf("< err=%d\n", err);
373 return err;
374 }
376 static int flush_output(LZIState *s, int mode){
377 int err = 0, zerr;
378 int done = 0;
379 int avail_out_old;
381 //dprintf("> avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
382 if(s->flushed == 1 + mode) goto exit;
383 //s->zstream.avail_in = 0; /* should be zero already anyway */
384 for(;;){
385 // Write any available output.
386 if(done || s->zstream.avail_out == 0){
387 err = write_block(s);
388 if(err) goto exit;
389 if(done) break;
390 }
391 //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
392 avail_out_old = s->zstream.avail_out;
393 zerr = deflate(&s->zstream, mode);
394 err = zerror(s, zerr);
395 //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out);
396 //dprintf("> deflate=%d\n", err);
397 //done = (s->zstream.avail_out != 0);
398 //done = (s->zstream.avail_in == 0) && (s->zstream.avail_out == avail_out_old);
399 if(0 && mode == Z_FINISH){
400 done = (zerr == Z_STREAM_END);
401 } else {
402 done = (s->zstream.avail_in == 0)
403 //&& (s->zstream.avail_out == avail_out_old)
404 && (s->zstream.avail_out != 0);
405 }
406 }
407 s->flushed = 1 + mode;
408 exit:
409 //dprintf("< err=%d\n", err);
410 return err;
411 }
413 /** Flush any pending input to the underlying stream.
414 *
415 * @param s lzi stream
416 * @return 0 on success, error code otherwise
417 */
418 static int lzi_flush(IOStream *io){
419 int err = 0;
420 LZIState *s = lzi_state(io);
421 //dprintf(">\n");
422 if(!LZIState_writeable(s)){
423 err = -EINVAL;
424 goto exit;
425 }
426 err = flush_output(s, Z_SYNC_FLUSH);
427 if(err) goto exit;
428 err = IOStream_flush(s->io);
429 exit:
430 set_error(s, err);
431 //dprintf("< err=%d\n", err);
432 return (err < 0 ? err : 0);
433 }
435 /** Check if a stream has an error.
436 *
437 * @param s lzi stream
438 * @return code if has an error, 0 otherwise
439 */
440 static int lzi_error(IOStream *s){
441 int err = 0;
442 LZIState *state = lzi_state(s);
443 err = state->error;
444 if(err) goto exit;
445 err = IOStream_error(state->io);
446 exit:
447 return err;
448 }
450 /** Close an lzi stream.
451 *
452 * @param s lzi stream to close
453 * @return result of the close
454 */
455 static int lzi_close(IOStream *io){
456 int err = 0;
457 LZIState *s = lzi_state(io);
458 if(LZIState_writeable(s)){
459 err = flush_output(s, Z_FINISH);
460 if(err) goto exit;
461 err = write_terminator(s);
462 if(err) goto exit;
463 err = IOStream_flush(s->io);
464 }
465 exit:
466 err = IOStream_close(s->io);
467 set_error(s, err);
468 return err;
469 }
471 /** Free an lzi stream.
472 *
473 * @param s lzi stream
474 */
475 static void lzi_free(IOStream *s){
476 LZIState *state = lzi_state(s);
477 IOStream_free(state->io);
478 LZIState_free(state);
479 s->data = NULL;
480 }
482 /** Create an lzi stream for an IOStream.
483 *
484 * @param io stream to wrap
485 * @return new IOStream using f for i/o
486 */
487 IOStream *lzi_stream_new(IOStream *io, const char *mode){
488 int err = -ENOMEM;
489 int flags = 0;
490 IOStream *zio = NULL;
491 LZIState *state = NULL;
493 zio = ALLOCATE(IOStream);
494 if(!zio) goto exit;
495 err = mode_flags(mode, &flags);
496 if(err) goto exit;
497 state = LZIState_new(io, flags);
498 if(!state) goto exit;
499 err = 0;
500 zio->data = state;
501 zio->methods = &lzi_methods;
502 exit:
503 if(err){
504 if(state) LZIState_free(state);
505 if(zio) deallocate(zio);
506 zio = NULL;
507 }
508 return zio;
509 }
511 /** IOStream version of fdopen().
512 *
513 * @param fd file descriptor
514 * @param flags giving the mode to open in (as for fdopen())
515 * @return new stream for the open file, or NULL if failed
516 */
517 IOStream *lzi_stream_fdopen(int fd, const char *mode){
518 int err = -ENOMEM;
519 IOStream *io = NULL, *zio = NULL;
520 io = file_stream_fdopen(fd, mode);
521 if(!io) goto exit;
522 zio = lzi_stream_new(io, mode);
523 if(!io) goto exit;
524 err = 0;
525 exit:
526 if(err){
527 IOStream_free(io);
528 IOStream_free(zio);
529 zio = NULL;
530 }
531 return zio;
532 }
533 #endif