summaryrefslogtreecommitdiffstats
path: root/generic/tclZlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclZlib.c')
-rw-r--r--generic/tclZlib.c185
1 files changed, 104 insertions, 81 deletions
diff --git a/generic/tclZlib.c b/generic/tclZlib.c
index a7e8a8a..33eebd1 100644
--- a/generic/tclZlib.c
+++ b/generic/tclZlib.c
@@ -177,6 +177,8 @@ static Tcl_ObjCmdProc ZlibStreamPutCmd;
static void ConvertError(Tcl_Interp *interp, int code,
uLong adler);
static Tcl_Obj * ConvertErrorToList(int code, uLong adler);
+static inline int Deflate(z_streamp strm, void *bufferPtr,
+ int bufferSize, int flush, int *writtenPtr);
static void ExtractHeader(gz_header *headerPtr, Tcl_Obj *dictObj);
static int GenerateHeader(Tcl_Interp *interp, Tcl_Obj *dictObj,
GzipHeader *headerPtr, int *extraSizePtr);
@@ -371,7 +373,7 @@ ConvertErrorToList(
default:
TclNewLiteralStringObj(objv[2], "UNKNOWN");
- TclNewIntObj(objv[3], code);
+ TclNewLongObj(objv[3], code);
return Tcl_NewListObj(4, objv);
}
}
@@ -578,6 +580,10 @@ ExtractHeader(
}
}
+/*
+ * Disentangle the worst of how the zlib API is used.
+ */
+
static int
SetInflateDictionary(
z_streamp strm,
@@ -605,6 +611,38 @@ SetDeflateDictionary(
}
return Z_OK;
}
+
+static inline int
+Deflate(
+ z_streamp strm,
+ void *bufferPtr,
+ int bufferSize,
+ int flush,
+ int *writtenPtr)
+{
+ int e;
+
+ strm->next_out = (Bytef *) bufferPtr;
+ strm->avail_out = (unsigned) bufferSize;
+ e = deflate(strm, flush);
+ if (writtenPtr != NULL) {
+ *writtenPtr = bufferSize - strm->avail_out;
+ }
+ return e;
+}
+
+static inline void
+AppendByteArray(
+ Tcl_Obj *listObj,
+ void *buffer,
+ int size)
+{
+ if (size > 0) {
+ Tcl_Obj *baObj = Tcl_NewByteArrayObj((unsigned char *) buffer, size);
+
+ Tcl_ListObjAppendElement(NULL, listObj, baObj);
+ }
+}
/*
*----------------------------------------------------------------------
@@ -1139,6 +1177,8 @@ Tcl_ZlibStreamSetCompressionDictionary(
*----------------------------------------------------------------------
*/
+#define BUFFER_SIZE_LIMIT 0xFFFF
+
int
Tcl_ZlibStreamPut(
Tcl_ZlibStream zshandle, /* As obtained from Tcl_ZlibStreamInit */
@@ -1148,8 +1188,7 @@ Tcl_ZlibStreamPut(
{
ZlibStreamHandle *zshPtr = (ZlibStreamHandle *) zshandle;
char *dataTmp = NULL;
- int e, size, outSize;
- Tcl_Obj *obj;
+ int e, size, outSize, toStore;
if (zshPtr->streamEnd) {
if (zshPtr->interp) {
@@ -1165,36 +1204,55 @@ Tcl_ZlibStreamPut(
zshPtr->stream.avail_in = size;
/*
- * Must not do a zero-length compress. [Bug 25842c161]
+ * Must not do a zero-length compress unless finalizing. [Bug 25842c161]
*/
- if (size == 0) {
+ if (size == 0 && flush != Z_FINISH) {
return TCL_OK;
}
if (HaveDictToSet(zshPtr)) {
e = SetDeflateDictionary(&zshPtr->stream, zshPtr->compDictObj);
if (e != Z_OK) {
- if (zshPtr->interp) {
- ConvertError(zshPtr->interp, e, zshPtr->stream.adler);
- }
+ ConvertError(zshPtr->interp, e, zshPtr->stream.adler);
return TCL_ERROR;
}
DictWasSet(zshPtr);
}
/*
- * Deflatebound doesn't seem to take various header sizes into
- * account, so we add 100 extra bytes.
+ * deflateBound() doesn't seem to take various header sizes into
+ * account, so we add 100 extra bytes. However, we can also loop
+ * around again so we also set an upper bound on the output buffer
+ * size.
*/
- outSize = deflateBound(&zshPtr->stream, zshPtr->stream.avail_in)+100;
- zshPtr->stream.avail_out = outSize;
- dataTmp = ckalloc(zshPtr->stream.avail_out);
- zshPtr->stream.next_out = (Bytef *) dataTmp;
+ outSize = deflateBound(&zshPtr->stream, size) + 100;
+ if (outSize > BUFFER_SIZE_LIMIT) {
+ outSize = BUFFER_SIZE_LIMIT;
+ }
+ dataTmp = ckalloc(outSize);
+
+ while (1) {
+ e = Deflate(&zshPtr->stream, dataTmp, outSize, flush, &toStore);
+
+ /*
+ * Test if we've filled the buffer up and have to ask deflate() to
+ * give us some more. Note that the condition for needing to
+ * repeat a buffer transfer when the result is Z_OK is whether
+ * there is no more space in the buffer we provided; the zlib
+ * library does not necessarily return a different code in that
+ * case. [Bug b26e38a3e4] [Tk Bug 10f2e7872b]
+ */
+
+ if ((e != Z_BUF_ERROR) && (e != Z_OK || toStore < outSize)) {
+ if ((e == Z_OK) || (flush == Z_FINISH && e == Z_STREAM_END)) {
+ break;
+ }
+ ConvertError(zshPtr->interp, e, zshPtr->stream.adler);
+ return TCL_ERROR;
+ }
- e = deflate(&zshPtr->stream, flush);
- while (e == Z_BUF_ERROR || (flush == Z_FINISH && e == Z_OK)) {
/*
* Output buffer too small to hold the data being generated or we
* are doing the end-of-stream flush (which can spit out masses of
@@ -1202,45 +1260,21 @@ Tcl_ZlibStreamPut(
* saving the old generated data to the outData list.
*/
- obj = Tcl_NewByteArrayObj((unsigned char *) dataTmp, outSize);
- Tcl_ListObjAppendElement(NULL, zshPtr->outData, obj);
+ AppendByteArray(zshPtr->outData, dataTmp, outSize);
- if (outSize < 0xFFFF) {
- outSize = 0xFFFF; /* There may be *lots* of data left to
- * output... */
+ if (outSize < BUFFER_SIZE_LIMIT) {
+ outSize = BUFFER_SIZE_LIMIT;
+ /* There may be *lots* of data left to output... */
dataTmp = ckrealloc(dataTmp, outSize);
}
- zshPtr->stream.avail_out = outSize;
- zshPtr->stream.next_out = (Bytef *) dataTmp;
-
- e = deflate(&zshPtr->stream, flush);
- }
-
- if (e != Z_OK && !(flush==Z_FINISH && e==Z_STREAM_END)) {
- if (zshPtr->interp) {
- ConvertError(zshPtr->interp, e, zshPtr->stream.adler);
- }
- return TCL_ERROR;
}
/*
- * And append the final data block.
+ * And append the final data block to the outData list.
*/
- if (outSize - zshPtr->stream.avail_out > 0) {
- obj = Tcl_NewByteArrayObj((unsigned char *) dataTmp,
- outSize - zshPtr->stream.avail_out);
-
- /*
- * Now append the compressed data to the outData list.
- */
-
- Tcl_ListObjAppendElement(NULL, zshPtr->outData, obj);
- }
-
- if (dataTmp) {
- ckfree(dataTmp);
- }
+ AppendByteArray(zshPtr->outData, dataTmp, toStore);
+ ckfree(dataTmp);
} else {
/*
* This is easy. Just append to the inData list.
@@ -1356,9 +1390,7 @@ Tcl_ZlibStreamGet(
if (IsRawStream(zshPtr) && HaveDictToSet(zshPtr)) {
e = SetInflateDictionary(&zshPtr->stream, zshPtr->compDictObj);
if (e != Z_OK) {
- if (zshPtr->interp) {
- ConvertError(zshPtr->interp, e, zshPtr->stream.adler);
- }
+ ConvertError(zshPtr->interp, e, zshPtr->stream.adler);
return TCL_ERROR;
}
DictWasSet(zshPtr);
@@ -2879,10 +2911,8 @@ ZlibTransformClose(
if (cd->mode == TCL_ZLIB_STREAM_DEFLATE) {
cd->outStream.avail_in = 0;
do {
- cd->outStream.next_out = (Bytef *) cd->outBuffer;
- cd->outStream.avail_out = (unsigned) cd->outAllocated;
- e = deflate(&cd->outStream, Z_FINISH);
- written = cd->outAllocated - cd->outStream.avail_out;
+ e = Deflate(&cd->outStream, cd->outBuffer, cd->outAllocated,
+ Z_FINISH, &written);
/*
* Can't be sure that deflate() won't declare the buffer to be
@@ -3083,32 +3113,28 @@ ZlibTransformOutput(
errorCodePtr);
}
- cd->outStream.next_in = (Bytef *) buf;
- cd->outStream.avail_in = toWrite;
- do {
- cd->outStream.next_out = (Bytef *) cd->outBuffer;
- cd->outStream.avail_out = cd->outAllocated;
+ /*
+ * No zero-length writes. Flushes must be explicit.
+ */
- e = deflate(&cd->outStream, Z_NO_FLUSH);
- produced = cd->outAllocated - cd->outStream.avail_out;
+ if (toWrite == 0) {
+ return 0;
+ }
- if ((e == Z_OK && produced > 0) || e == Z_BUF_ERROR) {
- /*
- * deflate() indicates that it is out of space by returning
- * Z_BUF_ERROR; in that case, we must write the whole buffer out
- * and retry to compress what is left.
- */
+ cd->outStream.next_in = (Bytef *) buf;
+ cd->outStream.avail_in = toWrite;
+ while (cd->outStream.avail_in > 0) {
+ e = Deflate(&cd->outStream, cd->outBuffer, cd->outAllocated,
+ Z_NO_FLUSH, &produced);
+ if (e != Z_OK || produced == 0) {
+ break;
+ }
- if (e == Z_BUF_ERROR) {
- produced = cd->outAllocated;
- e = Z_OK;
- }
- if (Tcl_WriteRaw(cd->parent, cd->outBuffer, produced) < 0) {
- *errorCodePtr = Tcl_GetErrno();
- return -1;
- }
+ if (Tcl_WriteRaw(cd->parent, cd->outBuffer, produced) < 0) {
+ *errorCodePtr = Tcl_GetErrno();
+ return -1;
}
- } while (e == Z_OK && produced > 0 && cd->outStream.avail_in > 0);
+ }
if (e == Z_OK) {
return toWrite - cd->outStream.avail_in;
@@ -3149,10 +3175,8 @@ ZlibTransformFlush(
* Get the bytes to go out of the compression engine.
*/
- cd->outStream.next_out = (Bytef *) cd->outBuffer;
- cd->outStream.avail_out = cd->outAllocated;
-
- e = deflate(&cd->outStream, flushType);
+ e = Deflate(&cd->outStream, cd->outBuffer, cd->outAllocated,
+ flushType, &len);
if (e != Z_OK && e != Z_BUF_ERROR) {
ConvertError(interp, e, cd->outStream.adler);
return TCL_ERROR;
@@ -3162,7 +3186,6 @@ ZlibTransformFlush(
* Write the bytes we've received to the next layer.
*/
- len = cd->outStream.next_out - (Bytef *) cd->outBuffer;
if (len > 0 && Tcl_WriteRaw(cd->parent, cd->outBuffer, len) < 0) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"problem flushing channel: %s",