From 7460f22cc7e783b1dd480c2fbf8ef6fc90a0360c Mon Sep 17 00:00:00 2001 From: dgp Date: Tue, 21 Jan 2014 21:32:53 +0000 Subject: Backport of bytearray append machinery to support bug fixes in ReadBytes. --- generic/tclBinary.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++ generic/tclIO.c | 17 +++------- generic/tclInt.h | 2 ++ 3 files changed, 98 insertions(+), 13 deletions(-) diff --git a/generic/tclBinary.c b/generic/tclBinary.c index dbb296b..68289f2 100644 --- a/generic/tclBinary.c +++ b/generic/tclBinary.c @@ -549,6 +549,98 @@ UpdateStringOfByteArray( /* *---------------------------------------------------------------------- * + * TclAppendBytesToByteArray -- + * + * This function appends an array of bytes to a byte array object. Note + * that the object *must* be unshared, and the array of bytes *must not* + * refer to the object being appended to. + * + * Results: + * None. + * + * Side effects: + * Allocates enough memory for an array of bytes of the requested total + * size, or possibly larger. [Bug 2992970] + * + *---------------------------------------------------------------------- + */ + +#define TCL_MIN_GROWTH 1024 +void +TclAppendBytesToByteArray( + Tcl_Obj *objPtr, + const unsigned char *bytes, + int len) +{ + ByteArray *byteArrayPtr; + int needed; + + if (Tcl_IsShared(objPtr)) { + Tcl_Panic("%s called with shared object","TclAppendBytesToByteArray"); + } + if (len < 0) { + Tcl_Panic("%s must be called with definite number of bytes to append", + "TclAppendBytesToByteArray"); + } + if (len == 0) { + /* Append zero bytes is a no-op. */ + return; + } + if (objPtr->typePtr != &tclByteArrayType) { + SetByteArrayFromAny(NULL, objPtr); + } + byteArrayPtr = GET_BYTEARRAY(objPtr); + + if (len > INT_MAX - byteArrayPtr->used) { + Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); + } + + needed = byteArrayPtr->used + len; + /* + * If we need to, resize the allocated space in the byte array. + */ + + if (needed > byteArrayPtr->allocated) { + ByteArray *ptr = NULL; + int attempt; + + if (needed <= INT_MAX/2) { + /* Try to allocate double the total space that is needed. */ + attempt = 2 * needed; + ptr = (ByteArray *) attemptckrealloc((void *) byteArrayPtr, + BYTEARRAY_SIZE(attempt)); + } + if (ptr == NULL) { + /* Try to allocate double the increment that is needed (plus). */ + unsigned int limit = INT_MAX - needed; + unsigned int extra = len + TCL_MIN_GROWTH; + int growth = (int) ((extra > limit) ? limit : extra); + + attempt = needed + growth; + ptr = (ByteArray *) attemptckrealloc((void *) byteArrayPtr, + BYTEARRAY_SIZE(attempt)); + } + if (ptr == NULL) { + /* Last chance: Try to allocate exactly what is needed. */ + attempt = needed; + ptr = (ByteArray *) ckrealloc((void *)byteArrayPtr, + BYTEARRAY_SIZE(attempt)); + } + byteArrayPtr = ptr; + byteArrayPtr->allocated = attempt; + SET_BYTEARRAY(objPtr, byteArrayPtr); + } + + if (bytes) { + memcpy(byteArrayPtr->bytes + byteArrayPtr->used, bytes, len); + } + byteArrayPtr->used += len; + TclInvalidateStringRep(objPtr); +} + +/* + *---------------------------------------------------------------------- + * * Tcl_BinaryObjCmd -- * * This procedure implements the "binary" Tcl command. diff --git a/generic/tclIO.c b/generic/tclIO.c index f1d8909..c1b7ee9 100644 --- a/generic/tclIO.c +++ b/generic/tclIO.c @@ -5586,20 +5586,8 @@ ReadBytes( toRead = srcLen; } + TclAppendBytesToByteArray(objPtr, NULL, toRead); dst = (char *) Tcl_GetByteArrayFromObj(objPtr, &length); - if (toRead > length - offset - 1) { - /* - * Double the existing size of the object or make enough room to hold - * all the characters we may get from the source buffer, whichever is - * larger. - */ - - length = offset * 2; - if (offset < toRead) { - length = offset + toRead + 1; - } - dst = (char *) Tcl_SetByteArrayLength(objPtr, length); - } dst += offset; if (statePtr->flags & INPUT_NEED_NL) { @@ -5607,6 +5595,7 @@ ReadBytes( if ((srcLen == 0) || (*src != '\n')) { *dst = '\r'; *offsetPtr += 1; + Tcl_SetByteArrayLength(objPtr, *offsetPtr); return 1; } *dst++ = '\n'; @@ -5619,11 +5608,13 @@ ReadBytes( dstWrote = toRead; if (TranslateInputEOL(statePtr, dst, src, &dstWrote, &srcRead) != 0) { if (dstWrote == 0) { + Tcl_SetByteArrayLength(objPtr, *offsetPtr); return -1; } } bufPtr->nextRemoved += srcRead; *offsetPtr += dstWrote; + Tcl_SetByteArrayLength(objPtr, *offsetPtr); return dstWrote; } diff --git a/generic/tclInt.h b/generic/tclInt.h index dc28b97..64d39a0 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -2477,6 +2477,8 @@ MODULE_SCOPE char tclEmptyString; *---------------------------------------------------------------- */ +MODULE_SCOPE void TclAppendBytesToByteArray(Tcl_Obj *objPtr, + const unsigned char *bytes, int len); MODULE_SCOPE void TclAdvanceContinuations(int* line, int** next, int loc); MODULE_SCOPE void TclAdvanceLines(int *line, const char *start, const char *end); -- cgit v0.12