1 /**
2 * Zlib-enabled input and output streams
3 *
4 * This is a thin wrapper of libz calls, in order
5 * to provide a simple interface to our developers
6 * for gzip input and output.
7 *
8 * Authors:
9 * Bob Jamison <rjamison@titan.com>
10 *
11 * Copyright (C) 2004 Inkscape.org
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #include "gzipstream.h"
19 namespace Inkscape
20 {
21 namespace IO
22 {
24 //#########################################################################
25 //# G Z I P I N P U T S T R E A M
26 //#########################################################################
28 #define OUT_SIZE 4000
30 /**
31 *
32 */
33 GzipInputStream::GzipInputStream(InputStream &sourceStream)
34 : BasicInputStream(sourceStream),
35 loaded(false),
36 totalIn(0),
37 totalOut(0),
38 outputBuf(NULL),
39 crc(0),
40 srcCrc(0),
41 srcSiz(0),
42 srcConsumed(0),
43 srcLen(0),
44 outputBufPos(0),
45 outputBufLen(0)
46 {
47 memset( &d_stream, 0, sizeof(d_stream) );
48 }
50 /**
51 *
52 */
53 GzipInputStream::~GzipInputStream()
54 {
55 close();
56 if ( srcBuf ) {
57 free(srcBuf);
58 srcBuf = 0;
59 }
60 if ( outputBuf ) {
61 free(outputBuf);
62 outputBuf = 0;
63 }
64 }
66 /**
67 * Returns the number of bytes that can be read (or skipped over) from
68 * this input stream without blocking by the next caller of a method for
69 * this input stream.
70 */
71 int GzipInputStream::available()
72 {
73 if (closed || !outputBuf)
74 return 0;
75 return outputBufLen - outputBufPos;
76 }
79 /**
80 * Closes this input stream and releases any system resources
81 * associated with the stream.
82 */
83 void GzipInputStream::close()
84 {
85 if (closed)
86 return;
88 int zerr = inflateEnd(&d_stream);
89 if (zerr != Z_OK) {
90 printf("inflateEnd: Some kind of problem: %d\n", zerr);
91 }
93 if ( srcBuf ) {
94 free(srcBuf);
95 srcBuf = 0;
96 }
97 if ( outputBuf ) {
98 free(outputBuf);
99 outputBuf = 0;
100 }
101 closed = true;
102 }
104 /**
105 * Reads the next byte of data from the input stream. -1 if EOF
106 */
107 int GzipInputStream::get()
108 {
109 int ch = -1;
110 if (closed) {
111 // leave return value -1
112 }
113 else if (!loaded && !load()) {
114 closed=true;
115 } else {
116 loaded = true;
118 int zerr = Z_OK;
119 if ( outputBufPos >= outputBufLen ) {
120 // time to read more, if we can
121 zerr = fetchMore();
122 }
124 if ( outputBufPos < outputBufLen ) {
125 ch = (int)outputBuf[outputBufPos++];
126 }
127 }
129 return ch;
130 }
132 #define FTEXT 0x01
133 #define FHCRC 0x02
134 #define FEXTRA 0x04
135 #define FNAME 0x08
136 #define FCOMMENT 0x10
138 bool GzipInputStream::load()
139 {
140 crc = crc32(0L, Z_NULL, 0);
142 std::vector<Byte> inputBuf;
143 while (true)
144 {
145 int ch = source.get();
146 if (ch<0)
147 break;
148 inputBuf.push_back((Byte)(ch & 0xff));
149 }
150 long inputBufLen = inputBuf.size();
152 if (inputBufLen < 19) //header + tail + 1
153 {
154 return false;
155 }
157 srcLen = inputBuf.size();
158 srcBuf = (Bytef *)malloc(srcLen * sizeof(Byte));
159 if (!srcBuf) {
160 return false;
161 }
163 outputBuf = (unsigned char *)malloc(OUT_SIZE);
164 if ( !outputBuf ) {
165 free(srcBuf);
166 srcBuf = 0;
167 return false;
168 }
169 outputBufLen = 0; // Not filled in yet
171 std::vector<unsigned char>::iterator iter;
172 Bytef *p = srcBuf;
173 for (iter=inputBuf.begin() ; iter != inputBuf.end() ; iter++)
174 *p++ = *iter;
176 int headerLen = 10;
178 //Magic
179 int val = (int)srcBuf[0];
180 //printf("val:%x\n", val);
181 val = (int)srcBuf[1];
182 //printf("val:%x\n", val);
184 //Method
185 val = (int)srcBuf[2];
186 //printf("val:%x\n", val);
188 //flags
189 int flags = (int)srcBuf[3];
191 //time
192 val = (int)srcBuf[4];
193 val = (int)srcBuf[5];
194 val = (int)srcBuf[6];
195 val = (int)srcBuf[7];
197 //xflags
198 val = (int)srcBuf[8];
199 //OS
200 val = (int)srcBuf[9];
202 int cur = 10;
203 // if ( flags & FEXTRA ) {
204 // headerLen += 2;
205 // int xlen =
206 // TODO deal with optional header parts
207 // }
208 if ( flags & FNAME ) {
209 while ( srcBuf[cur] )
210 {
211 cur++;
212 headerLen++;
213 }
214 headerLen++;
215 }
218 srcCrc = ((0x0ff & srcBuf[srcLen - 5]) << 24)
219 | ((0x0ff & srcBuf[srcLen - 6]) << 16)
220 | ((0x0ff & srcBuf[srcLen - 7]) << 8)
221 | ((0x0ff & srcBuf[srcLen - 8]) << 0);
222 //printf("srcCrc:%lx\n", srcCrc);
224 srcSiz = ((0x0ff & srcBuf[srcLen - 1]) << 24)
225 | ((0x0ff & srcBuf[srcLen - 2]) << 16)
226 | ((0x0ff & srcBuf[srcLen - 3]) << 8)
227 | ((0x0ff & srcBuf[srcLen - 4]) << 0);
228 //printf("srcSiz:%lx/%ld\n", srcSiz, srcSiz);
230 if ( srcSiz <= 0 ) {
231 return false;
232 }
234 //outputBufLen = srcSiz + srcSiz/100 + 14;
236 unsigned char *data = srcBuf + headerLen;
237 unsigned long dataLen = srcLen - (headerLen + 8);
238 //printf("%x %x\n", data[0], data[dataLen-1]);
240 d_stream.zalloc = (alloc_func)0;
241 d_stream.zfree = (free_func)0;
242 d_stream.opaque = (voidpf)0;
243 d_stream.next_in = data;
244 d_stream.avail_in = dataLen;
245 d_stream.next_out = outputBuf;
246 d_stream.avail_out = OUT_SIZE;
248 int zerr = inflateInit2(&d_stream, -MAX_WBITS);
249 if ( zerr == Z_OK )
250 {
251 zerr = fetchMore();
252 } else {
253 printf("inflateInit2: Some kind of problem: %d\n", zerr);
254 }
257 return (zerr == Z_OK) || (zerr == Z_STREAM_END);
258 }
261 int GzipInputStream::fetchMore()
262 {
263 int zerr = Z_OK;
265 // TODO assumes we aren't called till the buffer is empty
266 d_stream.next_out = outputBuf;
267 d_stream.avail_out = OUT_SIZE;
268 outputBufLen = 0;
269 outputBufPos = 0;
271 zerr = inflate( &d_stream, Z_SYNC_FLUSH );
272 if ( zerr == Z_OK || zerr == Z_STREAM_END ) {
273 outputBufLen = OUT_SIZE - d_stream.avail_out;
274 if ( outputBufLen ) {
275 crc = crc32(crc, (const Bytef *)outputBuf, outputBufLen);
276 }
277 //printf("crc:%lx\n", crc);
278 // } else if ( zerr != Z_STREAM_END ) {
279 // // TODO check to be sure this won't happen for partial end reads
280 // printf("inflate: Some kind of problem: %d\n", zerr);
281 }
283 return zerr;
284 }
286 //#########################################################################
287 //# G Z I P O U T P U T S T R E A M
288 //#########################################################################
290 /**
291 *
292 */
293 GzipOutputStream::GzipOutputStream(OutputStream &destinationStream)
294 : BasicOutputStream(destinationStream)
295 {
297 totalIn = 0;
298 totalOut = 0;
299 crc = crc32(0L, Z_NULL, 0);
301 //Gzip header
302 destination.put(0x1f);
303 destination.put(0x8b);
305 //Say it is compressed
306 destination.put(Z_DEFLATED);
308 //flags
309 destination.put(0);
311 //time
312 destination.put(0);
313 destination.put(0);
314 destination.put(0);
315 destination.put(0);
317 //xflags
318 destination.put(0);
320 //OS code - from zutil.h
321 //destination.put(OS_CODE);
322 //apparently, we should not explicitly include zutil.h
323 destination.put(0);
325 }
327 /**
328 *
329 */
330 GzipOutputStream::~GzipOutputStream()
331 {
332 close();
333 }
335 /**
336 * Closes this output stream and releases any system resources
337 * associated with this stream.
338 */
339 void GzipOutputStream::close()
340 {
341 if (closed)
342 return;
344 flush();
346 //# Send the CRC
347 uLong outlong = crc;
348 for (int n = 0; n < 4; n++)
349 {
350 destination.put((int)(outlong & 0xff));
351 outlong >>= 8;
352 }
353 //# send the file length
354 outlong = totalIn & 0xffffffffL;
355 for (int n = 0; n < 4; n++)
356 {
357 destination.put((int)(outlong & 0xff));
358 outlong >>= 8;
359 }
361 destination.close();
362 closed = true;
363 }
365 /**
366 * Flushes this output stream and forces any buffered output
367 * bytes to be written out.
368 */
369 void GzipOutputStream::flush()
370 {
371 if (closed || inputBuf.size()<1)
372 return;
374 uLong srclen = inputBuf.size();
375 Bytef *srcbuf = (Bytef *)malloc(srclen * sizeof(Byte));
376 if (!srcbuf)
377 {
378 return;
379 }
381 uLong destlen = srclen;
382 Bytef *destbuf = (Bytef *)malloc((destlen + (srclen/100) + 13) * sizeof(Byte));
383 if (!destbuf)
384 {
385 free(srcbuf);
386 return;
387 }
389 std::vector<unsigned char>::iterator iter;
390 Bytef *p = srcbuf;
391 for (iter=inputBuf.begin() ; iter != inputBuf.end() ; iter++)
392 *p++ = *iter;
394 crc = crc32(crc, (const Bytef *)srcbuf, srclen);
396 int zerr = compress(destbuf, (uLongf *)&destlen, srcbuf, srclen);
397 if (zerr != Z_OK)
398 {
399 printf("Some kind of problem\n");
400 }
402 totalOut += destlen;
403 //skip the redundant zlib header and checksum
404 for (uLong i=2; i<destlen-4 ; i++)
405 {
406 destination.put((int)destbuf[i]);
407 }
409 destination.flush();
411 inputBuf.clear();
412 free(srcbuf);
413 free(destbuf);
415 //printf("done\n");
417 }
421 /**
422 * Writes the specified byte to this output stream.
423 */
424 void GzipOutputStream::put(int ch)
425 {
426 if (closed)
427 {
428 //probably throw an exception here
429 return;
430 }
433 //Add char to buffer
434 inputBuf.push_back(ch);
435 totalIn++;
437 }
441 } // namespace IO
442 } // namespace Inkscape
445 //#########################################################################
446 //# E N D O F F I L E
447 //#########################################################################
449 /*
450 Local Variables:
451 mode:c++
452 c-file-style:"stroustrup"
453 c-file-offsets:((innamespace . 0)(inline-open . 0))
454 indent-tabs-mode:nil
455 fill-column:99
456 End:
457 */
458 // vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :