]> git.saurik.com Git - wxWidgets.git/blame - src/common/zstream.cpp
Add wxInputStream::ReadAll() and wxOutputStream::WriteAll().
[wxWidgets.git] / src / common / zstream.cpp
CommitLineData
0915d0b2 1//////////////////////////////////////////////////////////////////////////////
88a7a4e1 2// Name: src/common/zstream.cpp
79c3e0e1
GL
3// Purpose: Compressed stream classes
4// Author: Guilhem Lavaux
0915d0b2 5// Modified by: Mike Wetherell
79c3e0e1
GL
6// Created: 11/07/98
7// RCS-ID: $Id$
8// Copyright: (c) Guilhem Lavaux
65571936 9// Licence: wxWindows licence
79c3e0e1 10/////////////////////////////////////////////////////////////////////////////
ce4169a4 11
79c3e0e1
GL
12// For compilers that support precompilation, includes "wx.h".
13#include "wx/wxprec.h"
ac57418f 14
ce4169a4 15#ifdef __BORLANDC__
88a7a4e1 16 #pragma hdrstop
ce4169a4
RR
17#endif
18
ce4169a4 19#if wxUSE_ZLIB && wxUSE_STREAMS
ac57418f 20
ce4169a4 21#include "wx/zstream.h"
ccec9093 22#include "wx/versioninfo.h"
88a7a4e1
WS
23
24#ifndef WX_PRECOMP
25 #include "wx/intl.h"
e4db172a 26 #include "wx/log.h"
de6185e2 27 #include "wx/utils.h"
88a7a4e1
WS
28#endif
29
d1af991f 30
f6bcfd97
BP
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
d98a58c5 35#if defined(__WINDOWS__) && !defined(__WX_SETUP_H__) && !defined(wxUSE_ZLIB_H_IN_PATH)
de6185e2 36 #include "../zlib/zlib.h"
d1af991f 37#else
de6185e2 38 #include "zlib.h"
d1af991f 39#endif
79c3e0e1 40
0915d0b2 41enum {
4c68a102
VS
42 ZSTREAM_BUFFER_SIZE = 16384,
43 ZSTREAM_GZIP = 0x10, // gzip header
44 ZSTREAM_AUTO = 0x20 // auto detect between gzip and zlib
0915d0b2 45};
6d44bf31 46
55420742 47
ccec9093
VZ
48wxVersionInfo wxGetZlibVersionInfo()
49{
d02a916b
VZ
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);
ccec9093
VZ
62}
63
55420742
MW
64/////////////////////////////////////////////////////////////////////////////
65// Zlib Class factory
66
67IMPLEMENT_DYNAMIC_CLASS(wxZlibClassFactory, wxFilterClassFactory)
68
69static wxZlibClassFactory g_wxZlibClassFactory;
70
71wxZlibClassFactory::wxZlibClassFactory()
72{
73 if (this == &g_wxZlibClassFactory)
74 PushFront();
75}
76
77const wxChar * const *
78wxZlibClassFactory::GetProtocols(wxStreamProtocolType type) const
79{
9a83f860
VZ
80 static const wxChar *mimes[] = { wxT("application/x-deflate"), NULL };
81 static const wxChar *encs[] = { wxT("deflate"), NULL };
55420742
MW
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
95IMPLEMENT_DYNAMIC_CLASS(wxGzipClassFactory, wxFilterClassFactory)
96
97static wxGzipClassFactory g_wxGzipClassFactory;
98
99wxGzipClassFactory::wxGzipClassFactory()
100{
101 if (this == &g_wxGzipClassFactory && wxZlibInputStream::CanHandleGZip())
102 PushFront();
103}
104
105const wxChar * const *
106wxGzipClassFactory::GetProtocols(wxStreamProtocolType type) const
107{
03647350 108 static const wxChar *protos[] =
9a83f860 109 { wxT("gzip"), NULL };
03647350 110 static const wxChar *mimes[] =
9a83f860 111 { wxT("application/gzip"), wxT("application/x-gzip"), NULL };
03647350 112 static const wxChar *encs[] =
9a83f860 113 { wxT("gzip"), NULL };
03647350 114 static const wxChar *exts[] =
9a83f860 115 { wxT(".gz"), wxT(".gzip"), NULL };
55420742
MW
116 static const wxChar *empty[] =
117 { NULL };
118
119 switch (type) {
489a164c
MW
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;
55420742
MW
125 }
126}
127
128
79c3e0e1
GL
129//////////////////////
130// wxZlibInputStream
131//////////////////////
132
0915d0b2 133wxZlibInputStream::wxZlibInputStream(wxInputStream& stream, int flags)
79c3e0e1 134 : wxFilterInputStream(stream)
55420742
MW
135{
136 Init(flags);
137}
138
139wxZlibInputStream::wxZlibInputStream(wxInputStream *stream, int flags)
140 : wxFilterInputStream(stream)
141{
142 Init(flags);
143}
144
145void wxZlibInputStream::Init(int flags)
79c3e0e1 146{
0915d0b2
VZ
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;
6d44bf31 151
4c68a102
VS
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
0915d0b2
VZ
165 if (m_z_buffer) {
166 m_inflate = new z_stream_s;
79c3e0e1 167
0915d0b2 168 if (m_inflate) {
5fc01d13 169 memset(m_inflate, 0, sizeof(z_stream_s));
79c3e0e1 170
4c68a102
VS
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 }
6d44bf31 180
4c68a102 181 if (inflateInit2(m_inflate, windowBits) == Z_OK)
0915d0b2
VZ
182 return;
183 }
184 }
185
186 wxLogError(_("Can't initialize zlib inflate stream."));
187 m_lasterror = wxSTREAM_READ_ERROR;
79c3e0e1
GL
188}
189
190wxZlibInputStream::~wxZlibInputStream()
191{
856d2e52
GL
192 inflateEnd(m_inflate);
193 delete m_inflate;
45805ba3
VZ
194
195 delete [] m_z_buffer;
79c3e0e1
GL
196}
197
75ed1d15 198size_t wxZlibInputStream::OnSysRead(void *buffer, size_t size)
79c3e0e1 199{
0915d0b2
VZ
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;
79c3e0e1 206
0915d0b2 207 int err = Z_OK;
856d2e52
GL
208 m_inflate->next_out = (unsigned char *)buffer;
209 m_inflate->avail_out = size;
79c3e0e1 210
0915d0b2 211 while (err == Z_OK && m_inflate->avail_out > 0) {
4c68a102 212 if (m_inflate->avail_in == 0 && m_parent_i_stream->IsOk()) {
0915d0b2 213 m_parent_i_stream->Read(m_z_buffer, m_z_size);
856d2e52
GL
214 m_inflate->next_in = m_z_buffer;
215 m_inflate->avail_in = m_parent_i_stream->LastRead();
4c68a102
VS
216 }
217 err = inflate(m_inflate, Z_SYNC_FLUSH);
218 }
6d44bf31 219
4c68a102
VS
220 switch (err) {
221 case Z_OK:
0915d0b2 222 break;
4c68a102
VS
223
224 case Z_STREAM_END:
8e50d1ad
MW
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) {
abffc1ff 230 m_parent_i_stream->Reset();
8e50d1ad
MW
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;
777fd647 235 }
4c68a102 236 break;
f6bcfd97 237
4c68a102
VS
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())
af588446 244 {
4c68a102 245 wxLogError(_("Can't read inflate stream: unexpected EOF in underlying stream."));
af588446 246 }
4c68a102
VS
247 break;
248
249 default:
250 wxString msg(m_inflate->msg, *wxConvCurrent);
251 if (!msg)
2a1f999f 252 msg = wxString::Format(_("zlib error %d"), err);
4c68a102
VS
253 wxLogError(_("Can't read from inflate stream: %s"), msg.c_str());
254 m_lasterror = wxSTREAM_READ_ERROR;
79c3e0e1
GL
255 }
256
0915d0b2
VZ
257 size -= m_inflate->avail_out;
258 m_pos += size;
259 return size;
79c3e0e1
GL
260}
261
4c68a102
VS
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
51acf83b
VZ
270bool wxZlibInputStream::SetDictionary(const char *data, const size_t datalen)
271{
272 return (inflateSetDictionary(m_inflate, (Bytef*)data, datalen) == Z_OK);
273}
274
275bool wxZlibInputStream::SetDictionary(const wxMemoryBuffer &buf)
276{
277 return SetDictionary((char*)buf.GetData(), buf.GetDataLen());
278}
279
4c68a102 280
79c3e0e1
GL
281//////////////////////
282// wxZlibOutputStream
283//////////////////////
284
0915d0b2
VZ
285wxZlibOutputStream::wxZlibOutputStream(wxOutputStream& stream,
286 int level,
287 int flags)
79c3e0e1 288 : wxFilterOutputStream(stream)
55420742
MW
289{
290 Init(level, flags);
291}
292
293wxZlibOutputStream::wxZlibOutputStream(wxOutputStream *stream,
294 int level,
295 int flags)
296 : wxFilterOutputStream(stream)
297{
298 Init(level, flags);
299}
300
301void wxZlibOutputStream::Init(int level, int flags)
79c3e0e1 302{
0915d0b2
VZ
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;
79c3e0e1 307
af850422
VZ
308 if ( level == -1 )
309 {
0915d0b2 310 level = Z_DEFAULT_COMPRESSION;
af850422
VZ
311 }
312 else
313 {
314 wxASSERT_MSG(level >= 0 && level <= 9, wxT("wxZlibOutputStream compression level must be between 0 and 9!"));
315 }
5824f314 316
4c68a102
VS
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 }
6d44bf31 323
4c68a102
VS
324 if (m_z_buffer) {
325 m_deflate = new z_stream_s;
0915d0b2 326
4c68a102
VS
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;
cab1a605 331
4c68a102
VS
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
cab1a605 341 if (deflateInit2(m_deflate, level, Z_DEFLATED, windowBits,
4c68a102
VS
342 8, Z_DEFAULT_STRATEGY) == Z_OK)
343 return;
344 }
0915d0b2
VZ
345 }
346
347 wxLogError(_("Can't initialize zlib deflate stream."));
348 m_lasterror = wxSTREAM_WRITE_ERROR;
79c3e0e1
GL
349}
350
8f0ff178
RN
351bool wxZlibOutputStream::Close()
352 {
353 DoFlush(true);
354 deflateEnd(m_deflate);
5276b0a5
VZ
355 wxDELETE(m_deflate);
356 wxDELETEA(m_z_buffer);
55420742
MW
357
358 return wxFilterOutputStream::Close() && IsOk();
8f0ff178 359 }
79c3e0e1 360
0915d0b2 361void wxZlibOutputStream::DoFlush(bool final)
6d44bf31 362{
0915d0b2
VZ
363 if (!m_deflate || !m_z_buffer)
364 m_lasterror = wxSTREAM_WRITE_ERROR;
365 if (!IsOk())
6d44bf31 366 return;
6d44bf31 367
0915d0b2
VZ
368 int err = Z_OK;
369 bool done = false;
babd4308 370
0915d0b2
VZ
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 }
6d44bf31
GL
388}
389
75ed1d15 390size_t wxZlibOutputStream::OnSysWrite(const void *buffer, size_t size)
79c3e0e1 391{
0915d0b2 392 wxASSERT_MSG(m_deflate && m_z_buffer, wxT("Deflate stream not open"));
79c3e0e1 393
0915d0b2 394 if (!m_deflate || !m_z_buffer)
10793ebf 395 {
517cce71 396 // notice that this will make IsOk() test just below return false
0915d0b2 397 m_lasterror = wxSTREAM_WRITE_ERROR;
10793ebf
VZ
398 }
399
0915d0b2
VZ
400 if (!IsOk() || !size)
401 return 0;
402
403 int err = Z_OK;
856d2e52
GL
404 m_deflate->next_in = (unsigned char *)buffer;
405 m_deflate->avail_in = size;
79c3e0e1 406
0915d0b2 407 while (err == Z_OK && m_deflate->avail_in > 0) {
856d2e52 408 if (m_deflate->avail_out == 0) {
6d44bf31 409 m_parent_o_stream->Write(m_z_buffer, m_z_size);
0915d0b2
VZ
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 }
6d44bf31 415
856d2e52
GL
416 m_deflate->next_out = m_z_buffer;
417 m_deflate->avail_out = m_z_size;
79c3e0e1 418 }
6d44bf31 419
856d2e52 420 err = deflate(m_deflate, Z_NO_FLUSH);
79c3e0e1 421 }
0915d0b2
VZ
422
423 if (err != Z_OK) {
424 m_lasterror = wxSTREAM_WRITE_ERROR;
301deecc
RN
425 wxString msg(m_deflate->msg, *wxConvCurrent);
426 if (!msg)
2a1f999f 427 msg = wxString::Format(_("zlib error %d"), err);
4c68a102 428 wxLogError(_("Can't write to deflate stream: %s"), msg.c_str());
0915d0b2
VZ
429 }
430
431 size -= m_deflate->avail_in;
432 m_pos += size;
6d44bf31 433 return size;
79c3e0e1 434}
ac57418f 435
4c68a102 436/* static */ bool wxZlibOutputStream::CanHandleGZip()
cab1a605 437{
4c68a102
VS
438 return wxZlibInputStream::CanHandleGZip();
439}
440
51acf83b
VZ
441bool wxZlibOutputStream::SetDictionary(const char *data, const size_t datalen)
442{
443 return (deflateSetDictionary(m_deflate, (Bytef*)data, datalen) == Z_OK);
444}
445
446bool wxZlibOutputStream::SetDictionary(const wxMemoryBuffer &buf)
447{
448 return SetDictionary((char*)buf.GetData(), buf.GetDataLen());
449}
450
ac57418f 451#endif
ce4169a4 452 // wxUSE_ZLIB && wxUSE_STREAMS