]> git.saurik.com Git - wxWidgets.git/blob - src/common/zstream.cpp
Allow retrieving the descent and external leading of empty strings.
[wxWidgets.git] / src / common / zstream.cpp
1 //////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/zstream.cpp
3 // Purpose: Compressed stream classes
4 // Author: Guilhem Lavaux
5 // Modified by: Mike Wetherell
6 // Created: 11/07/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Guilhem Lavaux
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_ZLIB && wxUSE_STREAMS
20
21 #include "wx/zstream.h"
22 #include "wx/versioninfo.h"
23
24 #ifndef WX_PRECOMP
25 #include "wx/intl.h"
26 #include "wx/log.h"
27 #include "wx/utils.h"
28 #endif
29
30
31 // normally, the compiler options should contain -I../zlib, but it is
32 // apparently not the case for all MSW makefiles and so, unless we use
33 // configure (which defines __WX_SETUP_H__) or it is explicitly overridden by
34 // the user (who can define wxUSE_ZLIB_H_IN_PATH), we hardcode the path here
35 #if defined(__WINDOWS__) && !defined(__WX_SETUP_H__) && !defined(wxUSE_ZLIB_H_IN_PATH)
36 #include "../zlib/zlib.h"
37 #else
38 #include "zlib.h"
39 #endif
40
41 enum {
42 ZSTREAM_BUFFER_SIZE = 16384,
43 ZSTREAM_GZIP = 0x10, // gzip header
44 ZSTREAM_AUTO = 0x20 // auto detect between gzip and zlib
45 };
46
47
48 wxVersionInfo wxGetZlibVersionInfo()
49 {
50 int major,
51 minor,
52 build;
53
54 if ( sscanf(zlibVersion(), "%d.%d.%d", &major, &minor, &build) != 3 )
55 {
56 major =
57 minor =
58 build = 0;
59 }
60
61 return wxVersionInfo("zlib", major, minor, build);
62 }
63
64 /////////////////////////////////////////////////////////////////////////////
65 // Zlib Class factory
66
67 IMPLEMENT_DYNAMIC_CLASS(wxZlibClassFactory, wxFilterClassFactory)
68
69 static wxZlibClassFactory g_wxZlibClassFactory;
70
71 wxZlibClassFactory::wxZlibClassFactory()
72 {
73 if (this == &g_wxZlibClassFactory)
74 PushFront();
75 }
76
77 const wxChar * const *
78 wxZlibClassFactory::GetProtocols(wxStreamProtocolType type) const
79 {
80 static const wxChar *mimes[] = { wxT("application/x-deflate"), NULL };
81 static const wxChar *encs[] = { wxT("deflate"), NULL };
82 static const wxChar *empty[] = { NULL };
83
84 switch (type) {
85 case wxSTREAM_MIMETYPE: return mimes;
86 case wxSTREAM_ENCODING: return encs;
87 default: return empty;
88 }
89 }
90
91
92 /////////////////////////////////////////////////////////////////////////////
93 // Gzip Class factory
94
95 IMPLEMENT_DYNAMIC_CLASS(wxGzipClassFactory, wxFilterClassFactory)
96
97 static wxGzipClassFactory g_wxGzipClassFactory;
98
99 wxGzipClassFactory::wxGzipClassFactory()
100 {
101 if (this == &g_wxGzipClassFactory && wxZlibInputStream::CanHandleGZip())
102 PushFront();
103 }
104
105 const wxChar * const *
106 wxGzipClassFactory::GetProtocols(wxStreamProtocolType type) const
107 {
108 static const wxChar *protos[] =
109 { wxT("gzip"), NULL };
110 static const wxChar *mimes[] =
111 { wxT("application/gzip"), wxT("application/x-gzip"), NULL };
112 static const wxChar *encs[] =
113 { wxT("gzip"), NULL };
114 static const wxChar *exts[] =
115 { wxT(".gz"), wxT(".gzip"), NULL };
116 static const wxChar *empty[] =
117 { NULL };
118
119 switch (type) {
120 case wxSTREAM_PROTOCOL: return protos;
121 case wxSTREAM_MIMETYPE: return mimes;
122 case wxSTREAM_ENCODING: return encs;
123 case wxSTREAM_FILEEXT: return exts;
124 default: return empty;
125 }
126 }
127
128
129 //////////////////////
130 // wxZlibInputStream
131 //////////////////////
132
133 wxZlibInputStream::wxZlibInputStream(wxInputStream& stream, int flags)
134 : wxFilterInputStream(stream)
135 {
136 Init(flags);
137 }
138
139 wxZlibInputStream::wxZlibInputStream(wxInputStream *stream, int flags)
140 : wxFilterInputStream(stream)
141 {
142 Init(flags);
143 }
144
145 void wxZlibInputStream::Init(int flags)
146 {
147 m_inflate = NULL;
148 m_z_buffer = new unsigned char[ZSTREAM_BUFFER_SIZE];
149 m_z_size = ZSTREAM_BUFFER_SIZE;
150 m_pos = 0;
151
152 // if gzip is asked for but not supported...
153 if ((flags == wxZLIB_GZIP || flags == wxZLIB_AUTO) && !CanHandleGZip()) {
154 if (flags == wxZLIB_AUTO) {
155 // an error will come later if the input turns out not to be a zlib
156 flags = wxZLIB_ZLIB;
157 }
158 else {
159 wxLogError(_("Gzip not supported by this version of zlib"));
160 m_lasterror = wxSTREAM_READ_ERROR;
161 return;
162 }
163 }
164
165 if (m_z_buffer) {
166 m_inflate = new z_stream_s;
167
168 if (m_inflate) {
169 memset(m_inflate, 0, sizeof(z_stream_s));
170
171 // see zlib.h for documentation on windowBits
172 int windowBits = MAX_WBITS;
173 switch (flags) {
174 case wxZLIB_NO_HEADER: windowBits = -MAX_WBITS; break;
175 case wxZLIB_ZLIB: windowBits = MAX_WBITS; break;
176 case wxZLIB_GZIP: windowBits = MAX_WBITS | ZSTREAM_GZIP; break;
177 case wxZLIB_AUTO: windowBits = MAX_WBITS | ZSTREAM_AUTO; break;
178 default: wxFAIL_MSG(wxT("Invalid zlib flag"));
179 }
180
181 if (inflateInit2(m_inflate, windowBits) == Z_OK)
182 return;
183 }
184 }
185
186 wxLogError(_("Can't initialize zlib inflate stream."));
187 m_lasterror = wxSTREAM_READ_ERROR;
188 }
189
190 wxZlibInputStream::~wxZlibInputStream()
191 {
192 inflateEnd(m_inflate);
193 delete m_inflate;
194
195 delete [] m_z_buffer;
196 }
197
198 size_t wxZlibInputStream::OnSysRead(void *buffer, size_t size)
199 {
200 wxASSERT_MSG(m_inflate && m_z_buffer, wxT("Inflate stream not open"));
201
202 if (!m_inflate || !m_z_buffer)
203 m_lasterror = wxSTREAM_READ_ERROR;
204 if (!IsOk() || !size)
205 return 0;
206
207 int err = Z_OK;
208 m_inflate->next_out = (unsigned char *)buffer;
209 m_inflate->avail_out = size;
210
211 while (err == Z_OK && m_inflate->avail_out > 0) {
212 if (m_inflate->avail_in == 0 && m_parent_i_stream->IsOk()) {
213 m_parent_i_stream->Read(m_z_buffer, m_z_size);
214 m_inflate->next_in = m_z_buffer;
215 m_inflate->avail_in = m_parent_i_stream->LastRead();
216 }
217 err = inflate(m_inflate, Z_SYNC_FLUSH);
218 }
219
220 switch (err) {
221 case Z_OK:
222 break;
223
224 case Z_STREAM_END:
225 if (m_inflate->avail_out) {
226 // Unread any data taken from past the end of the deflate stream, so that
227 // any additional data can be read from the underlying stream (the crc
228 // in a gzip for example)
229 if (m_inflate->avail_in) {
230 m_parent_i_stream->Reset();
231 m_parent_i_stream->Ungetch(m_inflate->next_in, m_inflate->avail_in);
232 m_inflate->avail_in = 0;
233 }
234 m_lasterror = wxSTREAM_EOF;
235 }
236 break;
237
238 case Z_BUF_ERROR:
239 // Indicates that zlib was expecting more data, but the parent stream
240 // has none. Other than Eof the error will have been already reported
241 // by the parent strean,
242 m_lasterror = wxSTREAM_READ_ERROR;
243 if (m_parent_i_stream->Eof())
244 {
245 wxLogError(_("Can't read inflate stream: unexpected EOF in underlying stream."));
246 }
247 break;
248
249 default:
250 wxString msg(m_inflate->msg, *wxConvCurrent);
251 if (!msg)
252 msg = wxString::Format(_("zlib error %d"), err);
253 wxLogError(_("Can't read from inflate stream: %s"), msg.c_str());
254 m_lasterror = wxSTREAM_READ_ERROR;
255 }
256
257 size -= m_inflate->avail_out;
258 m_pos += size;
259 return size;
260 }
261
262 /* static */ bool wxZlibInputStream::CanHandleGZip()
263 {
264 const char *dot = strchr(zlibVersion(), '.');
265 int major = atoi(zlibVersion());
266 int minor = dot ? atoi(dot + 1) : 0;
267 return major > 1 || (major == 1 && minor >= 2);
268 }
269
270 bool wxZlibInputStream::SetDictionary(const char *data, const size_t datalen)
271 {
272 return (inflateSetDictionary(m_inflate, (Bytef*)data, datalen) == Z_OK);
273 }
274
275 bool wxZlibInputStream::SetDictionary(const wxMemoryBuffer &buf)
276 {
277 return SetDictionary((char*)buf.GetData(), buf.GetDataLen());
278 }
279
280
281 //////////////////////
282 // wxZlibOutputStream
283 //////////////////////
284
285 wxZlibOutputStream::wxZlibOutputStream(wxOutputStream& stream,
286 int level,
287 int flags)
288 : wxFilterOutputStream(stream)
289 {
290 Init(level, flags);
291 }
292
293 wxZlibOutputStream::wxZlibOutputStream(wxOutputStream *stream,
294 int level,
295 int flags)
296 : wxFilterOutputStream(stream)
297 {
298 Init(level, flags);
299 }
300
301 void wxZlibOutputStream::Init(int level, int flags)
302 {
303 m_deflate = NULL;
304 m_z_buffer = new unsigned char[ZSTREAM_BUFFER_SIZE];
305 m_z_size = ZSTREAM_BUFFER_SIZE;
306 m_pos = 0;
307
308 if ( level == -1 )
309 {
310 level = Z_DEFAULT_COMPRESSION;
311 }
312 else
313 {
314 wxASSERT_MSG(level >= 0 && level <= 9, wxT("wxZlibOutputStream compression level must be between 0 and 9!"));
315 }
316
317 // if gzip is asked for but not supported...
318 if (flags == wxZLIB_GZIP && !CanHandleGZip()) {
319 wxLogError(_("Gzip not supported by this version of zlib"));
320 m_lasterror = wxSTREAM_WRITE_ERROR;
321 return;
322 }
323
324 if (m_z_buffer) {
325 m_deflate = new z_stream_s;
326
327 if (m_deflate) {
328 memset(m_deflate, 0, sizeof(z_stream_s));
329 m_deflate->next_out = m_z_buffer;
330 m_deflate->avail_out = m_z_size;
331
332 // see zlib.h for documentation on windowBits
333 int windowBits = MAX_WBITS;
334 switch (flags) {
335 case wxZLIB_NO_HEADER: windowBits = -MAX_WBITS; break;
336 case wxZLIB_ZLIB: windowBits = MAX_WBITS; break;
337 case wxZLIB_GZIP: windowBits = MAX_WBITS | ZSTREAM_GZIP; break;
338 default: wxFAIL_MSG(wxT("Invalid zlib flag"));
339 }
340
341 if (deflateInit2(m_deflate, level, Z_DEFLATED, windowBits,
342 8, Z_DEFAULT_STRATEGY) == Z_OK)
343 return;
344 }
345 }
346
347 wxLogError(_("Can't initialize zlib deflate stream."));
348 m_lasterror = wxSTREAM_WRITE_ERROR;
349 }
350
351 bool wxZlibOutputStream::Close()
352 {
353 DoFlush(true);
354 deflateEnd(m_deflate);
355 wxDELETE(m_deflate);
356 wxDELETEA(m_z_buffer);
357
358 return wxFilterOutputStream::Close() && IsOk();
359 }
360
361 void wxZlibOutputStream::DoFlush(bool final)
362 {
363 if (!m_deflate || !m_z_buffer)
364 m_lasterror = wxSTREAM_WRITE_ERROR;
365 if (!IsOk())
366 return;
367
368 int err = Z_OK;
369 bool done = false;
370
371 while (err == Z_OK || err == Z_STREAM_END) {
372 size_t len = m_z_size - m_deflate->avail_out;
373 if (len) {
374 if (m_parent_o_stream->Write(m_z_buffer, len).LastWrite() != len) {
375 m_lasterror = wxSTREAM_WRITE_ERROR;
376 wxLogDebug(wxT("wxZlibOutputStream: Error writing to underlying stream"));
377 break;
378 }
379 m_deflate->next_out = m_z_buffer;
380 m_deflate->avail_out = m_z_size;
381 }
382
383 if (done)
384 break;
385 err = deflate(m_deflate, final ? Z_FINISH : Z_FULL_FLUSH);
386 done = m_deflate->avail_out != 0 || err == Z_STREAM_END;
387 }
388 }
389
390 size_t wxZlibOutputStream::OnSysWrite(const void *buffer, size_t size)
391 {
392 wxASSERT_MSG(m_deflate && m_z_buffer, wxT("Deflate stream not open"));
393
394 if (!m_deflate || !m_z_buffer)
395 {
396 // notice that this will make IsOk() test just below return false
397 m_lasterror = wxSTREAM_WRITE_ERROR;
398 }
399
400 if (!IsOk() || !size)
401 return 0;
402
403 int err = Z_OK;
404 m_deflate->next_in = (unsigned char *)buffer;
405 m_deflate->avail_in = size;
406
407 while (err == Z_OK && m_deflate->avail_in > 0) {
408 if (m_deflate->avail_out == 0) {
409 m_parent_o_stream->Write(m_z_buffer, m_z_size);
410 if (m_parent_o_stream->LastWrite() != m_z_size) {
411 m_lasterror = wxSTREAM_WRITE_ERROR;
412 wxLogDebug(wxT("wxZlibOutputStream: Error writing to underlying stream"));
413 break;
414 }
415
416 m_deflate->next_out = m_z_buffer;
417 m_deflate->avail_out = m_z_size;
418 }
419
420 err = deflate(m_deflate, Z_NO_FLUSH);
421 }
422
423 if (err != Z_OK) {
424 m_lasterror = wxSTREAM_WRITE_ERROR;
425 wxString msg(m_deflate->msg, *wxConvCurrent);
426 if (!msg)
427 msg = wxString::Format(_("zlib error %d"), err);
428 wxLogError(_("Can't write to deflate stream: %s"), msg.c_str());
429 }
430
431 size -= m_deflate->avail_in;
432 m_pos += size;
433 return size;
434 }
435
436 /* static */ bool wxZlibOutputStream::CanHandleGZip()
437 {
438 return wxZlibInputStream::CanHandleGZip();
439 }
440
441 bool wxZlibOutputStream::SetDictionary(const char *data, const size_t datalen)
442 {
443 return (deflateSetDictionary(m_deflate, (Bytef*)data, datalen) == Z_OK);
444 }
445
446 bool wxZlibOutputStream::SetDictionary(const wxMemoryBuffer &buf)
447 {
448 return SetDictionary((char*)buf.GetData(), buf.GetDataLen());
449 }
450
451 #endif
452 // wxUSE_ZLIB && wxUSE_STREAMS