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