]> git.saurik.com Git - wxWidgets.git/commitdiff
improvements to zlib streams (patch 929416):
authorVáclav Slavík <vslavik@fastmail.fm>
Sun, 11 Apr 2004 19:34:56 +0000 (19:34 +0000)
committerVáclav Slavík <vslavik@fastmail.fm>
Sun, 11 Apr 2004 19:34:56 +0000 (19:34 +0000)
1. CanHandleGZip static method
2. code cleanup
3. 2.4 compatibility mode

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@26713 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
docs/latex/wx/strmzlib.tex
include/wx/zstream.h
src/common/zstream.cpp

index 6dd0708c9ba3577c5c2a5563b6408451052ab577..1ab41e7a4f375d1e4da7b5059ad6422c10e2fe7b 100644 (file)
@@ -42,6 +42,9 @@ INCOMPATIBLE CHANGES SINCE 2.4.x
 - wxTaskBarIcon objects must now be destroyed before the application can exit.
   Previously, the application terminated if there were no top level windows;
   now it terminates if there are no top level windows or taskbar icons left.
 - wxTaskBarIcon objects must now be destroyed before the application can exit.
   Previously, the application terminated if there were no top level windows;
   now it terminates if there are no top level windows or taskbar icons left.
+- wxZlibInputStream is not by default compatible with the output of the
+  2.4.x version of wxZlibOutputStream. However, there is a compatibilty mode,
+  switched on by passing wxZLIB_24COMPATIBLE to the constructor.
 
 wxTaskBarIcon must be explicitly destroyed now, otherwise the application
   won't exit even though there are no top level windows
 
 wxTaskBarIcon must be explicitly destroyed now, otherwise the application
   won't exit even though there are no top level windows
index d563a5064064f5587f369f57804089b683ba1429..46945e16adea4b0efa7dbe4c7a182a899e67d17c 100644 (file)
@@ -4,7 +4,8 @@
 \section{\class{wxZlibInputStream}}\label{wxzlibinputstream}
 
 This filter stream decompresses a stream that is in zlib or gzip format.
 \section{\class{wxZlibInputStream}}\label{wxzlibinputstream}
 
 This filter stream decompresses a stream that is in zlib or gzip format.
-Note that reading the gzip format requires zlib version 1.2.0 greater.
+Note that reading the gzip format requires zlib version 1.2.1 or greater,
+(the builtin version does support gzips).
 
 The stream is not seekable, \helpref{SeekI()}{wxinputstreamseeki} returns
  {\it wxInvalidOffset}. Also \helpref{GetSize()}{wxstreambasegetsize} is
 
 The stream is not seekable, \helpref{SeekI()}{wxinputstreamseeki} returns
  {\it wxInvalidOffset}. Also \helpref{GetSize()}{wxstreambasegetsize} is
@@ -27,25 +28,45 @@ not supported, it always returns $0$.
 
 \membersection{wxZlibInputStream::wxZlibInputStream}
 
 
 \membersection{wxZlibInputStream::wxZlibInputStream}
 
-\func{}{wxZlibInputStream}{\param{wxInputStream\&}{ stream}, \param{int}{ flags = wxZLIB\_ZLIB | wxZLIB\_GZIP}}
+\func{}{wxZlibInputStream}{\param{wxInputStream\&}{ stream}, \param{int}{ flags = wxZLIB\_AUTO}}
 
 The {\it flags} wxZLIB\_ZLIB and wxZLIB\_GZIP specify whether the input data
 
 The {\it flags} wxZLIB\_ZLIB and wxZLIB\_GZIP specify whether the input data
-is in zlib or gzip format. If both are used, bitwise ored, then zlib will
+is in zlib or gzip format. If wxZLIB\_AUTO is used, then zlib will
 autodetect the stream type, this is the default.
 autodetect the stream type, this is the default.
-If {\it flags} is zero, then the data is assumed to be a raw deflate stream
-without either zlib or gzip headers.
+
+If {\it flags} is wxZLIB\_NO\_HEADER, then the data is assumed to be a raw
+deflate stream without either zlib or gzip headers. This is a lower level
+mode, which is not usually used directly. It can be used to read a raw
+deflate stream embedded in a higher level protocol.
+
+%if WXWIN_COMPATIBILITY_2_4
+This version is not by default compatible with the output produced by
+the version of {\it wxZlibOutputStream} in wxWidgets 2.4.x. However,
+there is a compatibilty mode, which is switched on by passing
+wxZLIB\_24COMPATIBLE for flags. Note that in when operating in compatibilty
+mode error checking is very much reduced.
+%endif
 
 The following symbols can be use for the flags:
 
 \begin{verbatim}
 // Flags
 enum {
 
 The following symbols can be use for the flags:
 
 \begin{verbatim}
 // Flags
 enum {
-    wxZLIB_NO_HEADER = 0,   // raw deflate stream, no header or checksum
-    wxZLIB_ZLIB = 1,        // zlib header and checksum
-    wxZLIB_GZIP = 2         // gzip header and checksum, requires zlib 1.2+
+#if WXWIN_COMPATIBILITY_2_4
+    wxZLIB_24COMPATIBLE = 4, // read v2.4.x data without error
+#endif
+    wxZLIB_NO_HEADER = 0,    // raw deflate stream, no header or checksum
+    wxZLIB_ZLIB = 1,         // zlib header and checksum
+    wxZLIB_GZIP = 2,         // gzip header and checksum, requires zlib 1.2.1+
+    wxZLIB_AUTO = 3          // autodetect header zlib or gzip
 };
 \end{verbatim}
 
 };
 \end{verbatim}
 
+\membersection{wxZlibInputStream::CanHandleGZip}\label{wxzlibinputstreamcanhandlegzip}
+
+\func{static bool}{CanHandleGZip}{\void}
+
+Returns true if zlib library in use can handle gzip compressed data.
 
 % -----------------------------------------------------------------------------
 % wxZlibOutputStream
 
 % -----------------------------------------------------------------------------
 % wxZlibOutputStream
@@ -54,7 +75,8 @@ enum {
 
 This stream compresses all data written to it. The compressed output can be
 in zlib or gzip format.
 
 This stream compresses all data written to it. The compressed output can be
 in zlib or gzip format.
-Note that writing the gzip format requires zlib version 1.2.0 greater.
+Note that writing the gzip format requires zlib version 1.2.1 or greater
+(the builtin version does support gzips).
 
 The stream is not seekable, \helpref{SeekO()}{wxoutputstreamseeko} returns
  {\it wxInvalidOffset}.
 
 The stream is not seekable, \helpref{SeekO()}{wxoutputstreamseeko} returns
  {\it wxInvalidOffset}.
@@ -86,8 +108,11 @@ value (currently equivalent to 6).
 
 The {\it flags} wxZLIB\_ZLIB and wxZLIB\_GZIP specify whether the output data
 will be in zlib or gzip format. wxZLIB\_ZLIB is the default.
 
 The {\it flags} wxZLIB\_ZLIB and wxZLIB\_GZIP specify whether the output data
 will be in zlib or gzip format. wxZLIB\_ZLIB is the default.
-If {\it flags} is zero, then a raw deflate stream is output without either
-zlib or gzip headers.
+
+If {\it flags} is wxZLIB\_NO\_HEADER, then a raw deflate stream is output
+without either zlib or gzip headers. This is a lower level
+mode, which is not usually used directly. It can be used to embed a raw
+deflate stream in a higher level protocol.
 
 The following symbols can be use for the compression level and flags:
 
 
 The following symbols can be use for the compression level and flags:
 
@@ -104,7 +129,13 @@ enum {
 enum {
     wxZLIB_NO_HEADER = 0,   // raw deflate stream, no header or checksum
     wxZLIB_ZLIB = 1,        // zlib header and checksum
 enum {
     wxZLIB_NO_HEADER = 0,   // raw deflate stream, no header or checksum
     wxZLIB_ZLIB = 1,        // zlib header and checksum
-    wxZLIB_GZIP = 2         // gzip header and checksum, requires zlib 1.2+
+    wxZLIB_GZIP = 2         // gzip header and checksum, requires zlib 1.2.1+
 };
 \end{verbatim}
 
 };
 \end{verbatim}
 
+\membersection{wxZlibOutputStream::CanHandleGZip}\label{wxoutputstreamcanhandlegzip}
+
+\func{static bool}{CanHandleGZip}{\void}
+
+Returns true if zlib library in use can handle gzip compressed data.
+
index de88e07b56895d65ce6284e98e11d51d6cc24442..748abd7b47ea0b78be58d5908adb7e93bd6ef62c 100644 (file)
@@ -31,19 +31,25 @@ enum {
 
 // Flags
 enum {
 
 // Flags
 enum {
-    wxZLIB_NO_HEADER = 0,   // raw deflate stream, no header or checksum
-    wxZLIB_ZLIB = 1,        // zlib header and checksum
-    wxZLIB_GZIP = 2         // gzip header and checksum, requires zlib 1.2+
+#if WXWIN_COMPATIBILITY_2_4
+    wxZLIB_24COMPATIBLE = 4, // read v2.4.x data without error
+#endif
+    wxZLIB_NO_HEADER = 0,    // raw deflate stream, no header or checksum
+    wxZLIB_ZLIB = 1,         // zlib header and checksum
+    wxZLIB_GZIP = 2,         // gzip header and checksum, requires zlib 1.2.1+
+    wxZLIB_AUTO = 3          // autodetect header zlib or gzip
 };
 
 class WXDLLIMPEXP_BASE wxZlibInputStream: public wxFilterInputStream {
  public:
 };
 
 class WXDLLIMPEXP_BASE wxZlibInputStream: public wxFilterInputStream {
  public:
-  wxZlibInputStream(wxInputStream& stream, int flags = wxZLIB_ZLIB | wxZLIB_GZIP);
+  wxZlibInputStream(wxInputStream& stream, int flags = wxZLIB_AUTO);
   virtual ~wxZlibInputStream();
 
   char Peek() { return wxInputStream::Peek(); }
   size_t GetSize() const { return wxInputStream::GetSize(); }
 
   virtual ~wxZlibInputStream();
 
   char Peek() { return wxInputStream::Peek(); }
   size_t GetSize() const { return wxInputStream::GetSize(); }
 
+  static bool CanHandleGZip();
+
  protected:
   size_t OnSysRead(void *buffer, size_t size);
   off_t OnSysTell() const { return m_pos; }
  protected:
   size_t OnSysRead(void *buffer, size_t size);
   off_t OnSysTell() const { return m_pos; }
@@ -53,8 +59,11 @@ class WXDLLIMPEXP_BASE wxZlibInputStream: public wxFilterInputStream {
   unsigned char *m_z_buffer;
   struct z_stream_s *m_inflate;
   off_t m_pos;
   unsigned char *m_z_buffer;
   struct z_stream_s *m_inflate;
   off_t m_pos;
+#if WXWIN_COMPATIBILITY_2_4
+  bool m_24compatibilty;
+#endif
 
 
-    DECLARE_NO_COPY_CLASS(wxZlibInputStream)
+  DECLARE_NO_COPY_CLASS(wxZlibInputStream)
 };
 
 class WXDLLIMPEXP_BASE wxZlibOutputStream: public wxFilterOutputStream {
 };
 
 class WXDLLIMPEXP_BASE wxZlibOutputStream: public wxFilterOutputStream {
@@ -65,6 +74,8 @@ class WXDLLIMPEXP_BASE wxZlibOutputStream: public wxFilterOutputStream {
   void Sync() { DoFlush(false); }
   size_t GetSize() const { return (size_t)m_pos; }
 
   void Sync() { DoFlush(false); }
   size_t GetSize() const { return (size_t)m_pos; }
 
+  static bool CanHandleGZip();
+
  protected:
   size_t OnSysWrite(const void *buffer, size_t size);
   off_t OnSysTell() const { return m_pos; }
  protected:
   size_t OnSysWrite(const void *buffer, size_t size);
   off_t OnSysTell() const { return m_pos; }
@@ -77,7 +88,7 @@ class WXDLLIMPEXP_BASE wxZlibOutputStream: public wxFilterOutputStream {
   struct z_stream_s *m_deflate;
   off_t m_pos;
 
   struct z_stream_s *m_deflate;
   off_t m_pos;
 
-    DECLARE_NO_COPY_CLASS(wxZlibOutputStream)
+  DECLARE_NO_COPY_CLASS(wxZlibOutputStream)
 };
 
 #endif
 };
 
 #endif
index 5372d214811f04a391c2971d45bd6b6ef6da4b29..09d616bab2cbe04cdf4f987fd637e2809f9689b9 100644 (file)
@@ -38,7 +38,9 @@
 #endif
 
 enum {
 #endif
 
 enum {
-    ZSTREAM_BUFFER_SIZE = 16384
+    ZSTREAM_BUFFER_SIZE = 16384,
+    ZSTREAM_GZIP        = 0x10,     // gzip header
+    ZSTREAM_AUTO        = 0x20      // auto detect between gzip and zlib
 };
 
 //////////////////////
 };
 
 //////////////////////
@@ -53,25 +55,43 @@ wxZlibInputStream::wxZlibInputStream(wxInputStream& stream, int flags)
   m_z_size = ZSTREAM_BUFFER_SIZE;
   m_pos = 0;
 
   m_z_size = ZSTREAM_BUFFER_SIZE;
   m_pos = 0;
 
+#if WXWIN_COMPATIBILITY_2_4
+  // treat compatibilty mode as auto
+  m_24compatibilty = flags == wxZLIB_24COMPATIBLE;
+  if (m_24compatibilty)
+    flags = wxZLIB_AUTO;
+#endif
+
+  // if gzip is asked for but not supported...
+  if ((flags == wxZLIB_GZIP || flags == wxZLIB_AUTO) && !CanHandleGZip()) {
+    if (flags == wxZLIB_AUTO) {
+      // an error will come later if the input turns out not to be a zlib
+      flags = wxZLIB_ZLIB;
+    }
+    else {
+      wxLogError(_("Gzip not supported by this version of zlib"));
+      m_lasterror = wxSTREAM_READ_ERROR;
+      return;
+    }
+  }
+
   if (m_z_buffer) {
     m_inflate = new z_stream_s;
 
     if (m_inflate) {
       memset(m_inflate, 0, sizeof(z_stream_s));
 
   if (m_z_buffer) {
     m_inflate = new z_stream_s;
 
     if (m_inflate) {
       memset(m_inflate, 0, sizeof(z_stream_s));
 
-      wxASSERT((flags & ~(wxZLIB_ZLIB | wxZLIB_GZIP)) == 0);
-      // when autodetecting between gzip & zlib, silently drop gzip flag
-      // if the version of zlib doesn't support it
-      if (flags == (wxZLIB_ZLIB | wxZLIB_GZIP)
-              && strcmp(zlib_version, "1.2.") < 0)
-        flags &= ~wxZLIB_GZIP;
-
-      int bits = flags ? MAX_WBITS : -MAX_WBITS;
-      if (flags & wxZLIB_GZIP)
-        bits |= (flags & wxZLIB_ZLIB) ? 0x20 : 0x10;
+      // see zlib.h for documentation on windowBits
+      int windowBits = MAX_WBITS;
+      switch (flags) {
+        case wxZLIB_NO_HEADER:  windowBits = -MAX_WBITS; break;
+        case wxZLIB_ZLIB:       windowBits = MAX_WBITS; break;
+        case wxZLIB_GZIP:       windowBits = MAX_WBITS | ZSTREAM_GZIP; break;
+        case wxZLIB_AUTO:       windowBits = MAX_WBITS | ZSTREAM_AUTO; break;
+        default:                wxFAIL_MSG(wxT("Invalid zlib flag"));
+      }
 
 
-      if (inflateInit2(m_inflate, bits) == Z_OK)
+      if (inflateInit2(m_inflate, windowBits) == Z_OK)
         return;
     }
   }
         return;
     }
   }
@@ -102,36 +122,49 @@ size_t wxZlibInputStream::OnSysRead(void *buffer, size_t size)
   m_inflate->avail_out = size;
 
   while (err == Z_OK && m_inflate->avail_out > 0) {
   m_inflate->avail_out = size;
 
   while (err == Z_OK && m_inflate->avail_out > 0) {
-    if (m_inflate->avail_in == 0) {
+    if (m_inflate->avail_in == 0 && m_parent_i_stream->IsOk()) {
       m_parent_i_stream->Read(m_z_buffer, m_z_size);
       m_inflate->next_in = m_z_buffer;
       m_inflate->avail_in = m_parent_i_stream->LastRead();
       m_parent_i_stream->Read(m_z_buffer, m_z_size);
       m_inflate->next_in = m_z_buffer;
       m_inflate->avail_in = m_parent_i_stream->LastRead();
+    }
+    err = inflate(m_inflate, Z_SYNC_FLUSH);
+  }
 
 
-      if (m_inflate->avail_in == 0) {
-        if (m_parent_i_stream->Eof())
-          wxLogError(_("Can't read inflate stream: unexpected EOF in underlying stream."));
-        m_lasterror = wxSTREAM_READ_ERROR;
+  switch (err) {
+    case Z_OK:
         break;
         break;
+
+    case Z_STREAM_END:
+      // Unread any data taken from past the end of the deflate stream, so that
+      // any additional data can be read from the underlying stream (the crc
+      // in a gzip for example)
+      if (m_inflate->avail_in) {
+        m_parent_i_stream->Ungetch(m_inflate->next_in, m_inflate->avail_in);
+        m_inflate->avail_in = 0;
       }
       }
-    }
-    err = inflate(m_inflate, Z_NO_FLUSH);
-  }
+      m_lasterror = wxSTREAM_EOF;
+      break;
 
 
-  if (err == Z_STREAM_END) {
-    // Unread any data taken from past the end of the deflate stream, so that
-    // any additional data can be read from the underlying stream (the crc
-    // in a gzip for example)
-    if (m_inflate->avail_in) {
-      m_parent_i_stream->Ungetch(m_inflate->next_in, m_inflate->avail_in);
-      m_inflate->avail_in = 0;
-    }
-    m_lasterror = wxSTREAM_EOF;
-  } else if (err != Z_OK) {
-    wxString msg(m_inflate->msg, *wxConvCurrent);
-    if (!msg)
-      msg.Format(_("zlib error %d"), err);
-    wxLogError(_("Can't read from inflate stream: %s\n"), msg.c_str());
-    m_lasterror = wxSTREAM_READ_ERROR;
+    case Z_BUF_ERROR:
+      // Indicates that zlib was expecting more data, but the parent stream
+      // has none. Other than Eof the error will have been already reported
+      // by the parent strean,
+      m_lasterror = wxSTREAM_READ_ERROR;
+      if (m_parent_i_stream->Eof())
+#if WXWIN_COMPATIBILITY_2_4
+        if (m_24compatibilty)
+          m_lasterror = wxSTREAM_EOF;
+        else
+#endif
+          wxLogError(_("Can't read inflate stream: unexpected EOF in underlying stream."));
+      break;
+
+    default:
+      wxString msg(m_inflate->msg, *wxConvCurrent);
+      if (!msg)
+        msg.Format(_("zlib error %d"), err);
+      wxLogError(_("Can't read from inflate stream: %s"), msg.c_str());
+      m_lasterror = wxSTREAM_READ_ERROR;
   }
 
   size -= m_inflate->avail_out;
   }
 
   size -= m_inflate->avail_out;
@@ -139,6 +172,15 @@ size_t wxZlibInputStream::OnSysRead(void *buffer, size_t size)
   return size;
 }
 
   return size;
 }
 
+/* static */ bool wxZlibInputStream::CanHandleGZip()
+{
+  const char *dot = strchr(zlibVersion(), '.');
+  int major = atoi(zlibVersion());
+  int minor = dot ? atoi(dot + 1) : 0;
+  return major > 1 || (major == 1 && minor >= 2);
+}
+
+
 //////////////////////
 // wxZlibOutputStream
 //////////////////////
 //////////////////////
 // wxZlibOutputStream
 //////////////////////
@@ -162,24 +204,34 @@ wxZlibOutputStream::wxZlibOutputStream(wxOutputStream& stream,
     wxASSERT_MSG(level >= 0 && level <= 9, wxT("wxZlibOutputStream compression level must be between 0 and 9!"));
   }
 
     wxASSERT_MSG(level >= 0 && level <= 9, wxT("wxZlibOutputStream compression level must be between 0 and 9!"));
   }
 
-  if (m_z_buffer) {
-     m_deflate = new z_stream_s;
+  // if gzip is asked for but not supported...
+  if (flags == wxZLIB_GZIP && !CanHandleGZip()) {
+    wxLogError(_("Gzip not supported by this version of zlib"));
+    m_lasterror = wxSTREAM_WRITE_ERROR;
+    return;
+  }
 
 
-     if (m_deflate) {
-        memset(m_deflate, 0, sizeof(z_stream_s));
-        m_deflate->next_out = m_z_buffer;
-        m_deflate->avail_out = m_z_size;
+  if (m_z_buffer) {
+    m_deflate = new z_stream_s;
 
 
-        wxASSERT(flags == 0 || flags == wxZLIB_ZLIB || flags == wxZLIB_GZIP);
+    if (m_deflate) {
+      memset(m_deflate, 0, sizeof(z_stream_s));
+      m_deflate->next_out = m_z_buffer;
+      m_deflate->avail_out = m_z_size;
  
  
-        int bits = flags ? MAX_WBITS : -MAX_WBITS;
-        if (flags & wxZLIB_GZIP)
-            bits |= 0x10;
-
-        if (deflateInit2(m_deflate, level, Z_DEFLATED, bits, 
-                         8, Z_DEFAULT_STRATEGY) == Z_OK)
-          return;
-     }
+      // see zlib.h for documentation on windowBits
+      int windowBits = MAX_WBITS;
+      switch (flags) {
+        case wxZLIB_NO_HEADER:  windowBits = -MAX_WBITS; break;
+        case wxZLIB_ZLIB:       windowBits = MAX_WBITS; break;
+        case wxZLIB_GZIP:       windowBits = MAX_WBITS | ZSTREAM_GZIP; break;
+        default:                wxFAIL_MSG(wxT("Invalid zlib flag"));
+      }
+
+      if (deflateInit2(m_deflate, level, Z_DEFLATED, windowBits, 
+                       8, Z_DEFAULT_STRATEGY) == Z_OK)
+        return;
+    }
   }
 
   wxLogError(_("Can't initialize zlib deflate stream."));
   }
 
   wxLogError(_("Can't initialize zlib deflate stream."));
@@ -261,7 +313,7 @@ size_t wxZlibOutputStream::OnSysWrite(const void *buffer, size_t size)
     wxString msg(m_deflate->msg, *wxConvCurrent);
     if (!msg)
       msg.Format(_("zlib error %d"), err);
     wxString msg(m_deflate->msg, *wxConvCurrent);
     if (!msg)
       msg.Format(_("zlib error %d"), err);
-    wxLogError(_("Can't write to deflate stream: %s\n"), msg.c_str());
+    wxLogError(_("Can't write to deflate stream: %s"), msg.c_str());
   }
 
   size -= m_deflate->avail_in;
   }
 
   size -= m_deflate->avail_in;
@@ -269,6 +321,11 @@ size_t wxZlibOutputStream::OnSysWrite(const void *buffer, size_t size)
   return size;
 }
 
   return size;
 }
 
+/* static */ bool wxZlibOutputStream::CanHandleGZip()
+{ 
+  return wxZlibInputStream::CanHandleGZip();
+}
+
 #endif
   // wxUSE_ZLIB && wxUSE_STREAMS
   
 #endif
   // wxUSE_ZLIB && wxUSE_STREAMS