[Python-checkins] CVS: python/dist/src/Modules zlibmodule.c,2.38,2.39

A.M. Kuchling akuchling@users.sourceforge.net
Tue, 20 Feb 2001 18:15:58 -0800


Update of /cvsroot/python/python/dist/src/Modules
In directory usw-pr-cvs1:/tmp/cvs-serv31686/Modules

Modified Files:
	zlibmodule.c 
Log Message:
Patch #103373 from Donovan Baarda: This patch: 
  * fixes the zlib decompress sync flush bug as reported in bug #124981
  * avoids repeat calls to (in|de)flateEnd when destroying (de)compression
    objects 
  * raises exception when allocating unused_data fails 
  * fixes memory leak when allocating unused_data fails 
  * raises exception when allocating decompress data fails 
  * removes vestigial code from decompress flush now that decompression 
    returns all available data
  * tidies code so object compress/decompress/flush routines are consistent 


Index: zlibmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Modules/zlibmodule.c,v
retrieving revision 2.38
retrieving revision 2.39
diff -C2 -r2.38 -r2.39
*** zlibmodule.c	2001/01/31 19:39:44	2.38
--- zlibmodule.c	2001/02/21 02:15:56	2.39
***************
*** 47,51 ****
  newcompobject(PyTypeObject *type)
  {
!         compobject *self;
          self = PyObject_New(compobject, type);
          if (self == NULL)
--- 47,51 ----
  newcompobject(PyTypeObject *type)
  {
!         compobject *self;        
          self = PyObject_New(compobject, type);
          if (self == NULL)
***************
*** 382,386 ****
  Decomp_dealloc(compobject *self)
  {
!     inflateEnd(&self->zst);
      Py_XDECREF(self->unused_data);
      PyObject_Del(self);
--- 382,387 ----
  Decomp_dealloc(compobject *self)
  {
!     if (self->is_initialised)
!       inflateEnd(&self->zst);
      Py_XDECREF(self->unused_data);
      PyObject_Del(self);
***************
*** 398,403 ****
  PyZlib_objcompress(compobject *self, PyObject *args)
  {
!   int err = Z_OK, inplen;
!   int length = DEFAULTALLOC;
    PyObject *RetVal;
    Byte *input;
--- 399,403 ----
  PyZlib_objcompress(compobject *self, PyObject *args)
  {
!   int err, inplen, length = DEFAULTALLOC;
    PyObject *RetVal;
    Byte *input;
***************
*** 405,444 ****
    
    if (!PyArg_ParseTuple(args, "s#:compress", &input, &inplen))
!       return NULL;
!   self->zst.avail_in = inplen;
!   self->zst.next_in = input;
    if (!(RetVal = PyString_FromStringAndSize(NULL, length))) {
!       PyErr_SetString(PyExc_MemoryError,
! 		      "Can't allocate memory to compress data");
!       return NULL;
    }
    start_total_out = self->zst.total_out;
!   self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
    self->zst.avail_out = length;
!   while (self->zst.avail_in != 0 && err == Z_OK)
!   {
!       err = deflate(&(self->zst), Z_NO_FLUSH);
!       if (self->zst.avail_out <= 0) {
! 	  if (_PyString_Resize(&RetVal, length << 1) == -1)  {
! 	      PyErr_SetString(PyExc_MemoryError,
! 			      "Can't allocate memory to compress data");
! 	      return NULL;
! 	  }
! 	  self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
! 	  self->zst.avail_out = length;
! 	  length = length << 1;
!       }
!   }
!   if (err != Z_OK) 
!   {
!       if (self->zst.msg == Z_NULL)
! 	  PyErr_Format(ZlibError, "Error %i while compressing",
! 		       err); 
!       else
! 	  PyErr_Format(ZlibError, "Error %i while compressing: %.200s",
! 		       err, self->zst.msg);  
!       Py_DECREF(RetVal);
        return NULL;
      }
    _PyString_Resize(&RetVal, self->zst.total_out - start_total_out);
    return RetVal;
--- 405,445 ----
    
    if (!PyArg_ParseTuple(args, "s#:compress", &input, &inplen))
!     return NULL;
    if (!(RetVal = PyString_FromStringAndSize(NULL, length))) {
!     PyErr_SetString(PyExc_MemoryError,
! 		    "Can't allocate memory to compress data");
!     return NULL;
    }
    start_total_out = self->zst.total_out;
!   self->zst.avail_in = inplen;
!   self->zst.next_in = input;
    self->zst.avail_out = length;
!   self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
!   err = deflate(&(self->zst), Z_NO_FLUSH);
!   /* while Z_OK and the output buffer is full, there might be more output,
!     so extend the output buffer and try again */
!   while (err == Z_OK && self->zst.avail_out == 0) {
!     if (_PyString_Resize(&RetVal, length << 1) == -1)  {
!       PyErr_SetString(PyExc_MemoryError,
! 		      "Can't allocate memory to compress data");
        return NULL;
      }
+     self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
+     self->zst.avail_out = length;
+     length = length << 1;
+     err = deflate(&(self->zst), Z_NO_FLUSH);
+   }
+   /* We will only get Z_BUF_ERROR if the output buffer was full but there 
+     wasn't more output when we tried again, so it is not an error condition */
+   if (err != Z_OK && err != Z_BUF_ERROR) {
+     if (self->zst.msg == Z_NULL)
+       PyErr_Format(ZlibError, "Error %i while compressing",
+ 		   err); 
+     else
+       PyErr_Format(ZlibError, "Error %i while compressing: %.200s",
+ 		   err, self->zst.msg);  
+     Py_DECREF(RetVal);
+     return NULL;
+   }
    _PyString_Resize(&RetVal, self->zst.total_out - start_total_out);
    return RetVal;
***************
*** 455,459 ****
  PyZlib_objdecompress(compobject *self, PyObject *args)
  {
!   int length, err, inplen;
    PyObject *RetVal;
    Byte *input;
--- 456,460 ----
  PyZlib_objdecompress(compobject *self, PyObject *args)
  {
!   int err, inplen, length = DEFAULTALLOC;
    PyObject *RetVal;
    Byte *input;
***************
*** 462,515 ****
    if (!PyArg_ParseTuple(args, "s#:decompress", &input, &inplen))
      return NULL;
    start_total_out = self->zst.total_out;
-   RetVal = PyString_FromStringAndSize(NULL, DEFAULTALLOC);
    self->zst.avail_in = inplen;
    self->zst.next_in = input;
!   self->zst.avail_out = length = DEFAULTALLOC;
    self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
!   err = Z_OK;
! 
!   while (self->zst.avail_in != 0 && err == Z_OK)
!   {
!       err = inflate(&(self->zst), Z_NO_FLUSH);
!       if (err == Z_OK && self->zst.avail_out <= 0) 
!       {
! 	  if (_PyString_Resize(&RetVal, length << 1) == -1)
! 	  {
! 	      PyErr_SetString(PyExc_MemoryError,
! 			      "Can't allocate memory to compress data");
! 	      return NULL;
! 	  }
! 	  self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
! 	  self->zst.avail_out = length;
! 	  length = length << 1;
!       }
!   }
! 
!   if (err != Z_OK && err != Z_STREAM_END) 
!   {
!       if (self->zst.msg == Z_NULL)
! 	  PyErr_Format(ZlibError, "Error %i while decompressing",
! 		       err); 
!       else
! 	  PyErr_Format(ZlibError, "Error %i while decompressing: %.200s",
! 		       err, self->zst.msg);  
        Py_DECREF(RetVal);
        return NULL;
      }
! 
!   if (err == Z_STREAM_END)
!   {
!       /* The end of the compressed data has been reached, so set 
!          the unused_data attribute to a string containing the 
!          remainder of the data in the string. */
!       int pos = self->zst.next_in - input;  /* Position in the string */
!       Py_XDECREF(self->unused_data);  /* Free the original, empty string */
! 
!       self->unused_data = PyString_FromStringAndSize((char *)input+pos,
! 						     inplen-pos);
!       if (self->unused_data == NULL) return NULL;
    }
- 
    _PyString_Resize(&RetVal, self->zst.total_out - start_total_out);
    return RetVal;
--- 463,516 ----
    if (!PyArg_ParseTuple(args, "s#:decompress", &input, &inplen))
      return NULL;
+   if (!(RetVal = PyString_FromStringAndSize(NULL, length))) {
+     PyErr_SetString(PyExc_MemoryError,
+ 		    "Can't allocate memory to compress data");
+     return NULL;
+   }
    start_total_out = self->zst.total_out;
    self->zst.avail_in = inplen;
    self->zst.next_in = input;
!   self->zst.avail_out = length;
    self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
!   err = inflate(&(self->zst), Z_SYNC_FLUSH);
!   /* while Z_OK and the output buffer is full, there might be more output,
!     so extend the output buffer and try again */
!   while (err == Z_OK && self->zst.avail_out == 0) { 
!     if (_PyString_Resize(&RetVal, length << 1) == -1) {
!       PyErr_SetString(PyExc_MemoryError,
!                       "Can't allocate memory to compress data");
!       return NULL;
!     }
!     self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
!     self->zst.avail_out = length;
!     length = length << 1;
!     err = inflate(&(self->zst), Z_SYNC_FLUSH);
!   }
!   /* The end of the compressed data has been reached, so set the unused_data 
!     attribute to a string containing the remainder of the data in the string. 
!     Note that this is also a logical place to call inflateEnd, but the old
!     behaviour of only calling it on flush() is preserved.*/
!   if (err == Z_STREAM_END) {
!     Py_XDECREF(self->unused_data);  /* Free the original, empty string */
!     self->unused_data = PyString_FromStringAndSize(self->zst.next_in,
! 						   self->zst.avail_in);
!     if (self->unused_data == NULL) {
!       PyErr_SetString(PyExc_MemoryError,
!                       "Can't allocate memory to unused_data");
        Py_DECREF(RetVal);
        return NULL;
      }
!   /* We will only get Z_BUF_ERROR if the output buffer was full but there 
!     wasn't more output when we tried again, so it is not an error condition */
!   } else if (err != Z_OK && err != Z_BUF_ERROR) {
!     if (self->zst.msg == Z_NULL)
!       PyErr_Format(ZlibError, "Error %i while decompressing",
! 		   err); 
!     else
!       PyErr_Format(ZlibError, "Error %i while decompressing: %.200s",
! 		   err, self->zst.msg);  
!     Py_DECREF(RetVal);
!     return NULL;
    }
    _PyString_Resize(&RetVal, self->zst.total_out - start_total_out);
    return RetVal;
***************
*** 527,531 ****
  PyZlib_flush(compobject *self, PyObject *args)
  {
!   int length=DEFAULTALLOC, err = Z_OK;
    PyObject *RetVal;
    int flushmode = Z_FINISH;
--- 528,532 ----
  PyZlib_flush(compobject *self, PyObject *args)
  {
!   int err, length=DEFAULTALLOC;
    PyObject *RetVal;
    int flushmode = Z_FINISH;
***************
*** 533,602 ****
  
    if (!PyArg_ParseTuple(args, "|i:flush", &flushmode))
!       return NULL;
  
    /* Flushing with Z_NO_FLUSH is a no-op, so there's no point in
       doing any work at all; just return an empty string. */
!   if (flushmode == Z_NO_FLUSH)
!     {
!       return PyString_FromStringAndSize(NULL, 0);
!     }
    
-   self->zst.avail_in = 0;
-   self->zst.next_in = Z_NULL;
    if (!(RetVal = PyString_FromStringAndSize(NULL, length))) {
!       PyErr_SetString(PyExc_MemoryError,
! 		      "Can't allocate memory to compress data");
!       return NULL;
    }
    start_total_out = self->zst.total_out;
!   self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
    self->zst.avail_out = length;
! 
!   /* When flushing the zstream, there's no input data.  
!      If zst.avail_out == 0, that means that more output space is
!      needed to complete the flush operation. */ 
!   while (1) {
!       err = deflate(&(self->zst), flushmode);
! 
!       /* If the output is Z_OK, and there's still room in the output
! 	 buffer, then the flush is complete. */
!       if ( (err == Z_OK) && self->zst.avail_out > 0) break;
! 
!       /* A nonzero return indicates some sort of error (but see 
! 	 the comment for the error handler below) */
!       if ( err != Z_OK ) break;
! 
!       /* There's no space left for output, so increase the buffer and loop 
! 	 again */
!       if (_PyString_Resize(&RetVal, length << 1) == -1)  {
! 	PyErr_SetString(PyExc_MemoryError,
! 			"Can't allocate memory to compress data");
! 	return NULL;
!       }
!       self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
!       self->zst.avail_out = length;
!       length = length << 1;
!   }
! 
!   /* Raise an exception indicating an error.  The condition for
!      detecting a error is kind of complicated; Z_OK indicates no
!      error, but if the flushmode is Z_FINISH, then Z_STREAM_END is
!      also not an error. */
!   if (err!=Z_OK && !(flushmode == Z_FINISH && err == Z_STREAM_END) )
!   {
!       if (self->zst.msg == Z_NULL)
! 	  PyErr_Format(ZlibError, "Error %i while flushing",
! 		       err); 
!       else
! 	  PyErr_Format(ZlibError, "Error %i while flushing: %.200s",
! 		       err, self->zst.msg);  
!       Py_DECREF(RetVal);
        return NULL;
!   }
! 
!   /* If flushmode is Z_FINISH, we also have to call deflateEnd() to
!      free various data structures */
! 
!   if (flushmode == Z_FINISH) {
      err=deflateEnd(&(self->zst));
      if (err!=Z_OK) {
--- 534,572 ----
  
    if (!PyArg_ParseTuple(args, "|i:flush", &flushmode))
!     return NULL;
  
    /* Flushing with Z_NO_FLUSH is a no-op, so there's no point in
       doing any work at all; just return an empty string. */
!   if (flushmode == Z_NO_FLUSH) {
!     return PyString_FromStringAndSize(NULL, 0);
!   }
    
    if (!(RetVal = PyString_FromStringAndSize(NULL, length))) {
!     PyErr_SetString(PyExc_MemoryError,
! 		    "Can't allocate memory to compress data");
!     return NULL;
    }
    start_total_out = self->zst.total_out;
!   self->zst.avail_in = 0;
    self->zst.avail_out = length;
!   self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
!   err = deflate(&(self->zst), flushmode);
!   /* while Z_OK and the output buffer is full, there might be more output,
!     so extend the output buffer and try again */
!   while (err == Z_OK && self->zst.avail_out == 0) {
!     if (_PyString_Resize(&RetVal, length << 1) == -1)  {
!       PyErr_SetString(PyExc_MemoryError,
! 		      "Can't allocate memory to compress data");
        return NULL;
!     }
!     self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
!     self->zst.avail_out = length;
!     length = length << 1;
!     err = deflate(&(self->zst), flushmode);
!   }
!   /* If flushmode is Z_FINISH, we also have to call deflateEnd() to free
!     various data structures. Note we should only get Z_STREAM_END when 
!     flushmode is Z_FINISH, but checking both for safety*/
!   if (err == Z_STREAM_END && flushmode == Z_FINISH) {
      err=deflateEnd(&(self->zst));
      if (err!=Z_OK) {
***************
*** 605,614 ****
  		     err); 
        else
! 	PyErr_Format(ZlibError,
! 		     "Error %i from deflateEnd(): %.200s",
  		     err, self->zst.msg);  
        Py_DECREF(RetVal);
        return NULL;
      }
    }
    _PyString_Resize(&RetVal, self->zst.total_out - start_total_out);
--- 575,595 ----
  		     err); 
        else
!  	PyErr_Format(ZlibError,"Error %i from deflateEnd(): %.200s",
  		     err, self->zst.msg);  
        Py_DECREF(RetVal);
        return NULL;
      }
+     self->is_initialised = 0;
+   /* We will only get Z_BUF_ERROR if the output buffer was full but there 
+     wasn't more output when we tried again, so it is not an error condition */
+   } else if (err!=Z_OK && err!=Z_BUF_ERROR) {
+     if (self->zst.msg == Z_NULL)
+       PyErr_Format(ZlibError, "Error %i while flushing",
+ 		   err); 
+     else
+       PyErr_Format(ZlibError, "Error %i while flushing: %.200s",
+ 		   err, self->zst.msg);  
+     Py_DECREF(RetVal);
+     return NULL;
    }
    _PyString_Resize(&RetVal, self->zst.total_out - start_total_out);
***************
*** 623,691 ****
  static PyObject *
  PyZlib_unflush(compobject *self, PyObject *args)
  {
!   int length=0, err;
!   PyObject *RetVal;
    
    if (!PyArg_ParseTuple(args, ""))
!       return NULL;
!   if (!(RetVal = PyString_FromStringAndSize(NULL, DEFAULTALLOC)))
!   {
!       PyErr_SetString(PyExc_MemoryError,
! 		      "Can't allocate memory to decompress data");
!       return NULL;
!   }
!   self->zst.avail_in=0;
!   self->zst.next_out = (unsigned char *)PyString_AsString(RetVal);
!   length = self->zst.avail_out = DEFAULTALLOC;
! 
!   /* I suspect that Z_BUF_ERROR is the only error code we need to check for 
!      in the following loop, but will leave the Z_OK in for now to avoid
!      destabilizing this function. --amk */
!   err = Z_OK;
!   while ( err == Z_OK )
!   {
!       err = inflate(&(self->zst), Z_FINISH);
!       if ( ( err == Z_OK || err == Z_BUF_ERROR ) && self->zst.avail_out == 0)
!       {
! 	  if (_PyString_Resize(&RetVal, length << 1) == -1)
! 	  {
! 	      PyErr_SetString(PyExc_MemoryError,
! 			      "Can't allocate memory to decompress data");
! 	      return NULL;
! 	  }
! 	  self->zst.next_out = (unsigned char *)PyString_AsString(RetVal) + length;
! 	  self->zst.avail_out = length;
! 	  length = length << 1;
! 	  err = Z_OK;
!       }
!   }
!   if (err!=Z_STREAM_END) 
!   {
!       if (self->zst.msg == Z_NULL)
! 	  PyErr_Format(ZlibError, "Error %i while decompressing",
! 		       err); 
!       else
! 	  PyErr_Format(ZlibError, "Error %i while decompressing: %.200s",
! 		       err, self->zst.msg);  
!       Py_DECREF(RetVal);
!       return NULL;
!   }
    err=inflateEnd(&(self->zst));
!   if (err!=Z_OK) 
!   {
!       if (self->zst.msg == Z_NULL)
! 	  PyErr_Format(ZlibError,
! 		       "Error %i while flushing decompression object",
! 		       err); 
!       else
! 	  PyErr_Format(ZlibError,
! 		       "Error %i while flushing decompression object: %.200s",
! 		       err, self->zst.msg);  
!       Py_DECREF(RetVal);
!       return NULL;
    }
!   _PyString_Resize(&RetVal, 
! 		   (char *)self->zst.next_out - PyString_AsString(RetVal));
!   return RetVal;
  }
  
--- 604,628 ----
  static PyObject *
  PyZlib_unflush(compobject *self, PyObject *args)
+ /*decompressor flush is a no-op because all pending data would have been 
+   flushed by the decompress method. However, this routine previously called
+   inflateEnd, causing any further decompress or flush calls to raise 
+   exceptions. This behaviour has been preserved.*/
  {
!   int err;
    
    if (!PyArg_ParseTuple(args, ""))
!     return NULL;
    err=inflateEnd(&(self->zst));
!   if (err!=Z_OK) {
!     if (self->zst.msg == Z_NULL)
!       PyErr_Format(ZlibError,  "Error %i from inflateEnd()",
! 		   err); 
!     else
!       PyErr_Format(ZlibError, "Error %i from inflateEnd(): %.200s",
! 		   err, self->zst.msg);  
!     return NULL;
    }
!   self->is_initialised = 0;
!   return PyString_FromStringAndSize(NULL, 0);
  }