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"
17 #include <cstdio>
18 #include <cstring>
19 #include <string>
21 namespace Inkscape
22 {
23 namespace IO
24 {
26 //#########################################################################
27 //# G Z I P I N P U T S T R E A M
28 //#########################################################################
30 #define OUT_SIZE 4000
32 /**
33 *
34 */
35 GzipInputStream::GzipInputStream(InputStream &sourceStream)
36 : BasicInputStream(sourceStream),
37 loaded(false),
38 totalIn(0),
39 totalOut(0),
40 outputBuf(NULL),
41 crc(0),
42 srcCrc(0),
43 srcSiz(0),
44 srcConsumed(0),
45 srcLen(0),
46 outputBufPos(0),
47 outputBufLen(0)
48 {
49 memset( &d_stream, 0, sizeof(d_stream) );
50 }
52 /**
53 *
54 */
55 GzipInputStream::~GzipInputStream()
56 {
57 close();
58 if ( srcBuf ) {
59 free(srcBuf);
60 srcBuf = 0;
61 }
62 if ( outputBuf ) {
63 free(outputBuf);
64 outputBuf = 0;
65 }
66 }
68 /**
69 * Returns the number of bytes that can be read (or skipped over) from
70 * this input stream without blocking by the next caller of a method for
71 * this input stream.
72 */
73 int GzipInputStream::available()
74 {
75 if (closed || !outputBuf)
76 return 0;
77 return outputBufLen - outputBufPos;
78 }
81 /**
82 * Closes this input stream and releases any system resources
83 * associated with the stream.
84 */
85 void GzipInputStream::close()
86 {
87 if (closed)
88 return;
90 int zerr = inflateEnd(&d_stream);
91 if (zerr != Z_OK) {
92 printf("inflateEnd: Some kind of problem: %d\n", zerr);
93 }
95 if ( srcBuf ) {
96 free(srcBuf);
97 srcBuf = 0;
98 }
99 if ( outputBuf ) {
100 free(outputBuf);
101 outputBuf = 0;
102 }
103 closed = true;
104 }
106 /**
107 * Reads the next byte of data from the input stream. -1 if EOF
108 */
109 int GzipInputStream::get()
110 {
111 int ch = -1;
112 if (closed) {
113 // leave return value -1
114 }
115 else if (!loaded && !load()) {
116 closed=true;
117 } else {
118 loaded = true;
120 int zerr = Z_OK;
121 if ( outputBufPos >= outputBufLen ) {
122 // time to read more, if we can
123 zerr = fetchMore();
124 }
126 if ( outputBufPos < outputBufLen ) {
127 ch = (int)outputBuf[outputBufPos++];
128 }
129 }
131 return ch;
132 }
134 #define FTEXT 0x01
135 #define FHCRC 0x02
136 #define FEXTRA 0x04
137 #define FNAME 0x08
138 #define FCOMMENT 0x10
140 bool GzipInputStream::load()
141 {
142 crc = crc32(0L, Z_NULL, 0);
144 std::vector<Byte> inputBuf;
145 while (true)
146 {
147 int ch = source.get();
148 if (ch<0)
149 break;
150 inputBuf.push_back((Byte)(ch & 0xff));
151 }
152 long inputBufLen = inputBuf.size();
154 if (inputBufLen < 19) //header + tail + 1
155 {
156 return false;
157 }
159 srcLen = inputBuf.size();
160 srcBuf = (Bytef *)malloc(srcLen * sizeof(Byte));
161 if (!srcBuf) {
162 return false;
163 }
165 outputBuf = (unsigned char *)malloc(OUT_SIZE);
166 if ( !outputBuf ) {
167 free(srcBuf);
168 srcBuf = 0;
169 return false;
170 }
171 outputBufLen = 0; // Not filled in yet
173 std::vector<unsigned char>::iterator iter;
174 Bytef *p = srcBuf;
175 for (iter=inputBuf.begin() ; iter != inputBuf.end() ; iter++)
176 *p++ = *iter;
178 int headerLen = 10;
180 //Magic
181 int val = (int)srcBuf[0];
182 //printf("val:%x\n", val);
183 val = (int)srcBuf[1];
184 //printf("val:%x\n", val);
186 //Method
187 val = (int)srcBuf[2];
188 //printf("val:%x\n", val);
190 //flags
191 int flags = (int)srcBuf[3];
193 //time
194 val = (int)srcBuf[4];
195 val = (int)srcBuf[5];
196 val = (int)srcBuf[6];
197 val = (int)srcBuf[7];
199 //xflags
200 val = (int)srcBuf[8];
201 //OS
202 val = (int)srcBuf[9];
204 int cur = 10;
205 // if ( flags & FEXTRA ) {
206 // headerLen += 2;
207 // int xlen =
208 // TODO deal with optional header parts
209 // }
210 if ( flags & FNAME ) {
211 while ( srcBuf[cur] )
212 {
213 cur++;
214 headerLen++;
215 }
216 headerLen++;
217 }
220 srcCrc = ((0x0ff & srcBuf[srcLen - 5]) << 24)
221 | ((0x0ff & srcBuf[srcLen - 6]) << 16)
222 | ((0x0ff & srcBuf[srcLen - 7]) << 8)
223 | ((0x0ff & srcBuf[srcLen - 8]) << 0);
224 //printf("srcCrc:%lx\n", srcCrc);
226 srcSiz = ((0x0ff & srcBuf[srcLen - 1]) << 24)
227 | ((0x0ff & srcBuf[srcLen - 2]) << 16)
228 | ((0x0ff & srcBuf[srcLen - 3]) << 8)
229 | ((0x0ff & srcBuf[srcLen - 4]) << 0);
230 //printf("srcSiz:%lx/%ld\n", srcSiz, srcSiz);
232 if ( srcSiz <= 0 ) {
233 return false;
234 }
236 //outputBufLen = srcSiz + srcSiz/100 + 14;
238 unsigned char *data = srcBuf + headerLen;
239 unsigned long dataLen = srcLen - (headerLen + 8);
240 //printf("%x %x\n", data[0], data[dataLen-1]);
242 d_stream.zalloc = (alloc_func)0;
243 d_stream.zfree = (free_func)0;
244 d_stream.opaque = (voidpf)0;
245 d_stream.next_in = data;
246 d_stream.avail_in = dataLen;
247 d_stream.next_out = outputBuf;
248 d_stream.avail_out = OUT_SIZE;
250 int zerr = inflateInit2(&d_stream, -MAX_WBITS);
251 if ( zerr == Z_OK )
252 {
253 zerr = fetchMore();
254 } else {
255 printf("inflateInit2: Some kind of problem: %d\n", zerr);
256 }
259 return (zerr == Z_OK) || (zerr == Z_STREAM_END);
260 }
263 int GzipInputStream::fetchMore()
264 {
265 int zerr = Z_OK;
267 // TODO assumes we aren't called till the buffer is empty
268 d_stream.next_out = outputBuf;
269 d_stream.avail_out = OUT_SIZE;
270 outputBufLen = 0;
271 outputBufPos = 0;
273 zerr = inflate( &d_stream, Z_SYNC_FLUSH );
274 if ( zerr == Z_OK || zerr == Z_STREAM_END ) {
275 outputBufLen = OUT_SIZE - d_stream.avail_out;
276 if ( outputBufLen ) {
277 crc = crc32(crc, (const Bytef *)outputBuf, outputBufLen);
278 }
279 //printf("crc:%lx\n", crc);
280 // } else if ( zerr != Z_STREAM_END ) {
281 // // TODO check to be sure this won't happen for partial end reads
282 // printf("inflate: Some kind of problem: %d\n", zerr);
283 }
285 return zerr;
286 }
288 //#########################################################################
289 //# G Z I P O U T P U T S T R E A M
290 //#########################################################################
292 /**
293 *
294 */
295 GzipOutputStream::GzipOutputStream(OutputStream &destinationStream)
296 : BasicOutputStream(destinationStream)
297 {
299 totalIn = 0;
300 totalOut = 0;
301 crc = crc32(0L, Z_NULL, 0);
303 //Gzip header
304 destination.put(0x1f);
305 destination.put(0x8b);
307 //Say it is compressed
308 destination.put(Z_DEFLATED);
310 //flags
311 destination.put(0);
313 //time
314 destination.put(0);
315 destination.put(0);
316 destination.put(0);
317 destination.put(0);
319 //xflags
320 destination.put(0);
322 //OS code - from zutil.h
323 //destination.put(OS_CODE);
324 //apparently, we should not explicitly include zutil.h
325 destination.put(0);
327 }
329 /**
330 *
331 */
332 GzipOutputStream::~GzipOutputStream()
333 {
334 close();
335 }
337 /**
338 * Closes this output stream and releases any system resources
339 * associated with this stream.
340 */
341 void GzipOutputStream::close()
342 {
343 if (closed)
344 return;
346 flush();
348 //# Send the CRC
349 uLong outlong = crc;
350 for (int n = 0; n < 4; n++)
351 {
352 destination.put((int)(outlong & 0xff));
353 outlong >>= 8;
354 }
355 //# send the file length
356 outlong = totalIn & 0xffffffffL;
357 for (int n = 0; n < 4; n++)
358 {
359 destination.put((int)(outlong & 0xff));
360 outlong >>= 8;
361 }
363 destination.close();
364 closed = true;
365 }
367 /**
368 * Flushes this output stream and forces any buffered output
369 * bytes to be written out.
370 */
371 void GzipOutputStream::flush()
372 {
373 if (closed || inputBuf.size()<1)
374 return;
376 uLong srclen = inputBuf.size();
377 Bytef *srcbuf = (Bytef *)malloc(srclen * sizeof(Byte));
378 if (!srcbuf)
379 {
380 return;
381 }
383 uLong destlen = srclen;
384 Bytef *destbuf = (Bytef *)malloc((destlen + (srclen/100) + 13) * sizeof(Byte));
385 if (!destbuf)
386 {
387 free(srcbuf);
388 return;
389 }
391 std::vector<unsigned char>::iterator iter;
392 Bytef *p = srcbuf;
393 for (iter=inputBuf.begin() ; iter != inputBuf.end() ; iter++)
394 *p++ = *iter;
396 crc = crc32(crc, (const Bytef *)srcbuf, srclen);
398 int zerr = compress(destbuf, (uLongf *)&destlen, srcbuf, srclen);
399 if (zerr != Z_OK)
400 {
401 printf("Some kind of problem\n");
402 }
404 totalOut += destlen;
405 //skip the redundant zlib header and checksum
406 for (uLong i=2; i<destlen-4 ; i++)
407 {
408 destination.put((int)destbuf[i]);
409 }
411 destination.flush();
413 inputBuf.clear();
414 free(srcbuf);
415 free(destbuf);
417 //printf("done\n");
419 }
423 /**
424 * Writes the specified byte to this output stream.
425 */
426 void GzipOutputStream::put(int ch)
427 {
428 if (closed)
429 {
430 //probably throw an exception here
431 return;
432 }
435 //Add char to buffer
436 inputBuf.push_back(ch);
437 totalIn++;
439 }
443 } // namespace IO
444 } // namespace Inkscape
447 //#########################################################################
448 //# E N D O F F I L E
449 //#########################################################################
451 /*
452 Local Variables:
453 mode:c++
454 c-file-style:"stroustrup"
455 c-file-offsets:((innamespace . 0)(inline-open . 0))
456 indent-tabs-mode:nil
457 fill-column:99
458 End:
459 */
460 // vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :