KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > HTTPClient > UncompressInputStream


1 /*
2  * @(#)UncompressInputStream.java 0.3-2 18/06/1999
3  *
4  * This file is part of the HTTPClient package
5  * Copyright (C) 1996-1999 Ronald Tschalär
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free
19  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  * MA 02111-1307, USA
21  *
22  * For questions, suggestions, bug-reports, enhancement-requests etc.
23  * I may be contacted at:
24  *
25  * ronald@innovation.ch
26  *
27  */

28
29 package HTTPClient;
30
31 import java.io.IOException JavaDoc;
32 import java.io.EOFException JavaDoc;
33 import java.io.InputStream JavaDoc;
34 import java.io.FileInputStream JavaDoc;
35 import java.io.FilterInputStream JavaDoc;
36
37
38 /**
39  * This class decompresses an input stream containing data compressed with
40  * the unix "compress" utility (LZC, a LZW variant). This code is based
41  * heavily on the <var>unlzw.c</var> code in <var>gzip-1.2.4</var> (written
42  * by Peter Jannesen) and the original compress code.
43  *
44  * @version 0.3-2 18/06/1999
45  * @author Ronald Tschalär
46  */

47 class UncompressInputStream extends FilterInputStream JavaDoc
48 {
49     /**
50      * @param is the input stream to decompress
51      * @exception IOException if the header is malformed
52      */

53     public UncompressInputStream(InputStream JavaDoc is) throws IOException JavaDoc
54     {
55     super(is);
56     parse_header();
57     }
58
59
60     byte[] one = new byte[1];
61     public synchronized int read() throws IOException JavaDoc
62     {
63     int b = in.read(one, 0, 1);
64     if (b == 1)
65         return (one[0] & 0xff);
66     else
67         return -1;
68     }
69
70
71     // string table stuff
72
private static final int TBL_CLEAR = 0x100;
73     private static final int TBL_FIRST = TBL_CLEAR + 1;
74
75     private int[] tab_prefix;
76     private byte[] tab_suffix;
77     private int[] zeros = new int[256];
78     private byte[] stack;
79
80     // various state
81
private boolean block_mode;
82     private int n_bits;
83     private int maxbits;
84     private int maxmaxcode;
85     private int maxcode;
86     private int bitmask;
87     private int oldcode;
88     private byte finchar;
89     private int stackp;
90     private int free_ent;
91
92     // input buffer
93
private byte[] data = new byte[10000];
94     private int bit_pos = 0, end = 0, got = 0;
95     private boolean eof = false;
96     private static final int EXTRA = 64;
97
98
99     public synchronized int read(byte[] buf, int off, int len)
100         throws IOException JavaDoc
101     {
102     if (eof) return -1;
103     int start = off;
104
105     /* Using local copies of various variables speeds things up by as
106      * much as 30% !
107      */

108     int[] l_tab_prefix = tab_prefix;
109     byte[] l_tab_suffix = tab_suffix;
110     byte[] l_stack = stack;
111     int l_n_bits = n_bits;
112     int l_maxcode = maxcode;
113     int l_maxmaxcode = maxmaxcode;
114     int l_bitmask = bitmask;
115     int l_oldcode = oldcode;
116     byte l_finchar = finchar;
117     int l_stackp = stackp;
118     int l_free_ent = free_ent;
119     byte[] l_data = data;
120     int l_bit_pos = bit_pos;
121
122
123     // empty stack if stuff still left
124

125     int s_size = l_stack.length - l_stackp;
126     if (s_size > 0)
127     {
128         int num = (s_size >= len) ? len : s_size ;
129         System.arraycopy(l_stack, l_stackp, buf, off, num);
130         off += num;
131         len -= num;
132         l_stackp += num;
133     }
134
135     if (len == 0)
136     {
137         stackp = l_stackp;
138         return off-start;
139     }
140
141
142     // loop, filling local buffer until enough data has been decompressed
143

144     main_loop: do
145     {
146         if (end < EXTRA) fill();
147
148         int bit_in = (got > 0) ? (end - end%l_n_bits)<<3 :
149                      (end<<3)-(l_n_bits-1);
150
151         while (l_bit_pos < bit_in)
152         {
153         // check for code-width expansion
154

155         if (l_free_ent > l_maxcode)
156         {
157             int n_bytes = l_n_bits << 3;
158             l_bit_pos = (l_bit_pos-1) +
159                 n_bytes - (l_bit_pos-1+n_bytes) % n_bytes;
160
161             l_n_bits++;
162             l_maxcode = (l_n_bits==maxbits) ? l_maxmaxcode :
163                               (1<<l_n_bits) - 1;
164
165             if (debug)
166             System.err.println("Code-width expanded to " + l_n_bits);
167
168             l_bitmask = (1<<l_n_bits)-1;
169             l_bit_pos = resetbuf(l_bit_pos);
170             continue main_loop;
171         }
172
173
174         // read next code
175

176         int pos = l_bit_pos>>3;
177         int code = (((l_data[pos]&0xFF) | ((l_data[pos+1]&0xFF)<<8) |
178                 ((l_data[pos+2]&0xFF)<<16))
179                 >> (l_bit_pos & 0x7)) & l_bitmask;
180         l_bit_pos += l_n_bits;
181
182
183         // handle first iteration
184

185         if (l_oldcode == -1)
186         {
187             if (code >= 256)
188             throw new IOException JavaDoc("corrupt input: " + code +
189                           " > 255");
190             l_finchar = (byte) (l_oldcode = code);
191             buf[off++] = l_finchar;
192             len--;
193             continue;
194         }
195
196
197         // handle CLEAR code
198

199         if (code == TBL_CLEAR && block_mode)
200         {
201             System.arraycopy(zeros, 0, l_tab_prefix, 0, zeros.length);
202             l_free_ent = TBL_FIRST - 1;
203
204             int n_bytes = l_n_bits << 3;
205             l_bit_pos = (l_bit_pos-1) +
206                 n_bytes - (l_bit_pos-1+n_bytes) % n_bytes;
207             l_n_bits = INIT_BITS;
208             l_maxcode = (1 << l_n_bits) - 1;
209             l_bitmask = l_maxcode;
210
211             if (debug) System.err.println("Code tables reset");
212
213             l_bit_pos = resetbuf(l_bit_pos);
214             continue main_loop;
215         }
216
217
218         // setup
219

220         int incode = code;
221         l_stackp = l_stack.length;
222
223
224         // Handle KwK case
225

226         if (code >= l_free_ent)
227         {
228             if (code > l_free_ent)
229             throw new IOException JavaDoc("corrupt input: code=" + code +
230                           ", free_ent=" + l_free_ent);
231             
232             l_stack[--l_stackp] = l_finchar;
233             code = l_oldcode;
234         }
235
236
237         // Generate output characters in reverse order
238

239         while (code >= 256)
240         {
241             l_stack[--l_stackp] = l_tab_suffix[code];
242             code = l_tab_prefix[code];
243         }
244         l_finchar = l_tab_suffix[code];
245         buf[off++] = l_finchar;
246         len--;
247
248
249         // And put them out in forward order
250

251         s_size = l_stack.length - l_stackp;
252         int num = (s_size >= len) ? len : s_size ;
253         System.arraycopy(l_stack, l_stackp, buf, off, num);
254         off += num;
255         len -= num;
256         l_stackp += num;
257
258
259         // generate new entry in table
260

261         if (l_free_ent < l_maxmaxcode)
262         {
263             l_tab_prefix[l_free_ent] = l_oldcode;
264             l_tab_suffix[l_free_ent] = l_finchar;
265             l_free_ent++;
266         }
267
268
269         // Remember previous code
270

271         l_oldcode = incode;
272
273
274         // if output buffer full, then return
275

276         if (len == 0)
277         {
278             n_bits = l_n_bits;
279             maxcode = l_maxcode;
280             bitmask = l_bitmask;
281             oldcode = l_oldcode;
282             finchar = l_finchar;
283             stackp = l_stackp;
284             free_ent = l_free_ent;
285             bit_pos = l_bit_pos;
286
287             return off-start;
288         }
289         }
290
291         l_bit_pos = resetbuf(l_bit_pos);
292     } while (got > 0);
293
294     n_bits = l_n_bits;
295     maxcode = l_maxcode;
296     bitmask = l_bitmask;
297     oldcode = l_oldcode;
298     finchar = l_finchar;
299     stackp = l_stackp;
300     free_ent = l_free_ent;
301     bit_pos = l_bit_pos;
302
303     eof = true;
304     return off-start;
305     }
306
307
308     /**
309      * Moves the unread data in the buffer to the beginning and resets
310      * the pointers.
311      */

312     private final int resetbuf(int bit_pos)
313     {
314     int pos = bit_pos >> 3;
315     System.arraycopy(data, pos, data, 0, end-pos);
316     end -= pos;
317     return 0;
318     }
319
320
321     private final void fill() throws IOException JavaDoc
322     {
323     got = in.read(data, end, data.length-1-end);
324     if (got > 0) end += got;
325     }
326
327
328     public synchronized long skip(long num) throws IOException JavaDoc
329     {
330     byte[] tmp = new byte[(int) num];
331     int got = read(tmp, 0, (int) num);
332
333     if (got > 0)
334         return (long) got;
335     else
336         return 0L;
337     }
338
339
340     public synchronized int available() throws IOException JavaDoc
341     {
342     if (eof) return 0;
343
344     return in.available();
345     }
346
347
348     private static final int LZW_MAGIC = 0x1f9d;
349     private static final int MAX_BITS = 16;
350     private static final int INIT_BITS = 9;
351     private static final int HDR_MAXBITS = 0x1f;
352     private static final int HDR_EXTENDED = 0x20;
353     private static final int HDR_FREE = 0x40;
354     private static final int HDR_BLOCK_MODE = 0x80;
355
356     private void parse_header() throws IOException JavaDoc
357     {
358     // read in and check magic number
359

360     int t = in.read();
361     if (t < 0) throw new EOFException JavaDoc("Failed to read magic number");
362     int magic = (t & 0xff) << 8;
363     t = in.read();
364     if (t < 0) throw new EOFException JavaDoc("Failed to read magic number");
365     magic += t & 0xff;
366     if (magic != LZW_MAGIC)
367         throw new IOException JavaDoc("Input not in compress format (read " +
368                   "magic number 0x" +
369                   Integer.toHexString(magic) + ")");
370
371
372     // read in header byte
373

374     int header = in.read();
375     if (header < 0) throw new EOFException JavaDoc("Failed to read header");
376
377     block_mode = (header & HDR_BLOCK_MODE) > 0;
378     maxbits = header & HDR_MAXBITS;
379
380     if (maxbits > MAX_BITS)
381         throw new IOException JavaDoc("Stream compressed with " + maxbits +
382                   " bits, but can only handle " + MAX_BITS +
383                   " bits");
384
385     if ((header & HDR_EXTENDED) > 0)
386         throw new IOException JavaDoc("Header extension bit set");
387
388     if ((header & HDR_FREE) > 0)
389         throw new IOException JavaDoc("Header bit 6 set");
390
391     if (debug)
392     {
393         System.err.println("block mode: " + block_mode);
394         System.err.println("max bits: " + maxbits);
395     }
396
397
398     // initialize stuff
399

400     maxmaxcode = 1 << maxbits;
401     n_bits = INIT_BITS;
402     maxcode = (1 << n_bits) - 1;
403     bitmask = maxcode;
404     oldcode = -1;
405     finchar = 0;
406     free_ent = block_mode ? TBL_FIRST : 256;
407
408     tab_prefix = new int[1 << maxbits];
409     tab_suffix = new byte[1 << maxbits];
410     stack = new byte[1 << maxbits];
411     stackp = stack.length;
412
413     for (int idx=255; idx>=0; idx--)
414         tab_suffix[idx] = (byte) idx;
415     }
416
417
418     private static final boolean debug = false;
419
420     public static void main (String JavaDoc args[]) throws Exception JavaDoc
421     {
422     if (args.length != 1)
423     {
424         System.err.println("Usage: UncompressInputStream <file>");
425         System.exit(1);
426     }
427
428     InputStream JavaDoc in =
429             new UncompressInputStream(new FileInputStream JavaDoc(args[0]));
430
431     byte[] buf = new byte[100000];
432     int tot = 0;
433     long beg = System.currentTimeMillis();
434
435     while (true)
436     {
437         int got = in.read(buf);
438         if (got < 0) break;
439         System.out.write(buf, 0, got);
440         tot += got;
441     }
442
443     long end = System.currentTimeMillis();
444     System.err.println("Decompressed " + tot + " bytes");
445     System.err.println("Time: " + (end-beg)/1000. + " seconds");
446     }
447 }
448
449
Popular Tags