summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclBinary.c92
-rw-r--r--generic/tclIO.c119
-rw-r--r--generic/tclInt.h2
-rw-r--r--generic/tclStringObj.c29
4 files changed, 144 insertions, 98 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 3636861..f8baba3 100644
--- a/generic/tclIO.c
+++ b/generic/tclIO.c
@@ -205,9 +205,9 @@ static int HaveVersion(const Tcl_ChannelType *typePtr,
static void PeekAhead(Channel *chanPtr, char **dstEndPtr,
GetsState *gsPtr);
static int ReadBytes(ChannelState *statePtr, Tcl_Obj *objPtr,
- int charsLeft, int *offsetPtr);
+ int charsLeft);
static int ReadChars(ChannelState *statePtr, Tcl_Obj *objPtr,
- int charsLeft, int *offsetPtr, int *factorPtr);
+ int charsLeft, int *factorPtr);
static void RecycleBuffer(ChannelState *statePtr,
ChannelBuffer *bufPtr, int mustDiscard);
static int StackSetBlockMode(Channel *chanPtr, int mode);
@@ -5058,7 +5058,7 @@ DoReadChars(
ChannelState *statePtr = chanPtr->state;
/* State info for channel */
ChannelBuffer *bufPtr;
- int offset, factor, copied, copiedNow, result;
+ int factor, copied, copiedNow, result;
Tcl_Encoding encoding;
#define UTF_EXPANSION_FACTOR 1024
@@ -5080,27 +5080,21 @@ DoReadChars(
* We're going to access objPtr->bytes directly, so we must ensure
* that this is actually a string object (otherwise it might have
* been pure Unicode).
+ *
+ * Probably not needed anymore.
*/
TclGetString(objPtr);
}
- offset = 0;
- } else {
- if (encoding == NULL) {
- Tcl_GetByteArrayFromObj(objPtr, &offset);
- } else {
- TclGetStringFromObj(objPtr, &offset);
- }
}
for (copied = 0; (unsigned) toRead > 0; ) {
copiedNow = -1;
if (statePtr->inQueueHead != NULL) {
if (encoding == NULL) {
- copiedNow = ReadBytes(statePtr, objPtr, toRead, &offset);
+ copiedNow = ReadBytes(statePtr, objPtr, toRead);
} else {
- copiedNow = ReadChars(statePtr, objPtr, toRead, &offset,
- &factor);
+ copiedNow = ReadChars(statePtr, objPtr, toRead, &factor);
}
/*
@@ -5145,11 +5139,6 @@ DoReadChars(
}
ResetFlag(statePtr, CHANNEL_BLOCKED);
- if (encoding == NULL) {
- Tcl_SetByteArrayLength(objPtr, offset);
- } else {
- Tcl_SetObjLength(objPtr, offset);
- }
/*
* Update the notifier state so we don't block while there is still data
@@ -5175,13 +5164,11 @@ DoReadChars(
* allocated to hold data read from the channel as needed.
*
* Results:
- * The return value is the number of bytes appended to the object and
- * *offsetPtr is filled with the total number of bytes in the object
- * (greater than the return value if there were already bytes in the
- * object).
+ * The return value is the number of bytes appended to the object, or
+ * -1 to indicate that zero bytes were read due to an EOF.
*
* Side effects:
- * None.
+ * The storage of bytes in objPtr can cause (re-)allocation of memory.
*
*---------------------------------------------------------------------------
*/
@@ -5194,24 +5181,18 @@ ReadBytes(
* been allocated to hold data, not how many
* bytes of data have been stored in the
* object. */
- int bytesToRead, /* Maximum number of bytes to store, or < 0 to
+ int bytesToRead) /* Maximum number of bytes to store, or < 0 to
* get all available bytes. Bytes are obtained
* from the first buffer in the queue - even
* if this number is larger than the number of
* bytes available in the first buffer, only
* the bytes from the first buffer are
* returned. */
- int *offsetPtr) /* On input, contains how many bytes of objPtr
- * have been used to hold data. On output,
- * filled with how many bytes are now being
- * used. */
{
- int toRead, srcLen, offset, length, srcRead, dstWrote;
+ int toRead, srcLen, length, srcRead, dstWrote;
ChannelBuffer *bufPtr;
char *src, *dst;
- offset = *offsetPtr;
-
bufPtr = statePtr->inQueueHead;
src = RemovePoint(bufPtr);
srcLen = BytesLeft(bufPtr);
@@ -5221,27 +5202,17 @@ ReadBytes(
toRead = srcLen;
}
- 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;
+ (void) Tcl_GetByteArrayFromObj(objPtr, &length);
+ TclAppendBytesToByteArray(objPtr, NULL, toRead);
+ dst = (char *) Tcl_GetByteArrayFromObj(objPtr, NULL);
+ dst += length;
if (statePtr->flags & INPUT_NEED_NL) {
ResetFlag(statePtr, INPUT_NEED_NL);
if ((srcLen == 0) || (*src != '\n')) {
*dst = '\r';
- *offsetPtr += 1;
+ length += 1;
+ Tcl_SetByteArrayLength(objPtr, length);
return 1;
}
*dst++ = '\n';
@@ -5254,11 +5225,13 @@ ReadBytes(
dstWrote = toRead;
if (TranslateInputEOL(statePtr, dst, src, &dstWrote, &srcRead) != 0) {
if (dstWrote == 0) {
+ Tcl_SetByteArrayLength(objPtr, length);
return -1;
}
}
bufPtr->nextRemoved += srcRead;
- *offsetPtr += dstWrote;
+ length += dstWrote;
+ Tcl_SetByteArrayLength(objPtr, length);
return dstWrote;
}
@@ -5304,17 +5277,13 @@ ReadChars(
* available in the first buffer, only the
* characters from the first buffer are
* returned. */
- int *offsetPtr, /* On input, contains how many bytes of objPtr
- * have been used to hold data. On output,
- * filled with how many bytes are now being
- * used. */
int *factorPtr) /* On input, contains a guess of how many
* bytes need to be allocated to hold the
* result of converting N source bytes to
* UTF-8. On output, contains another guess
* based on the data seen so far. */
{
- int toRead, factor, offset, spaceLeft, srcLen, dstNeeded;
+ int toRead, factor, srcLen, dstNeeded, numBytes;
int srcRead, dstWrote, numChars, dstRead;
ChannelBuffer *bufPtr;
char *src, *dst;
@@ -5322,7 +5291,6 @@ ReadChars(
int encEndFlagSuppressed = 0;
factor = *factorPtr;
- offset = *offsetPtr;
bufPtr = statePtr->inQueueHead;
src = RemovePoint(bufPtr);
@@ -5340,37 +5308,9 @@ ReadChars(
*/
dstNeeded = TCL_UTF_MAX - 1 + toRead * factor / UTF_EXPANSION_FACTOR;
- spaceLeft = objPtr->length - offset;
-
- if (dstNeeded > spaceLeft) {
- /*
- * Double the existing size of the object or make enough room to hold
- * all the characters we want from the source buffer, whichever is
- * larger.
- */
-
- int length = offset + ((offset < dstNeeded) ? dstNeeded : offset);
-
- if (Tcl_AttemptSetObjLength(objPtr, length) == 0) {
- length = offset + dstNeeded;
- if (Tcl_AttemptSetObjLength(objPtr, length) == 0) {
- dstNeeded = TCL_UTF_MAX - 1 + toRead;
- length = offset + dstNeeded;
- Tcl_SetObjLength(objPtr, length);
- }
- }
- spaceLeft = length - offset;
- }
- if (toRead == srcLen) {
- /*
- * Want to convert the whole buffer in one pass. If we have enough
- * space, convert it using all available space in object rather than
- * using the factor.
- */
-
- dstNeeded = spaceLeft;
- }
- dst = objPtr->bytes + offset;
+ (void) TclGetStringFromObj(objPtr, &numBytes);
+ Tcl_AppendToObj(objPtr, NULL, dstNeeded);
+ dst = TclGetString(objPtr) + numBytes;
/*
* [Bug 1462248]: The cause of the crash reported in this bug is this:
@@ -5441,7 +5381,7 @@ ReadChars(
*dst = '\r';
}
statePtr->inputEncodingFlags &= ~TCL_ENCODING_START;
- *offsetPtr += 1;
+ Tcl_SetObjLength(objPtr, numBytes + 1);
if (encEndFlagSuppressed) {
statePtr->inputEncodingFlags |= TCL_ENCODING_END;
@@ -5482,6 +5422,7 @@ ReadChars(
SetFlag(statePtr, CHANNEL_NEED_MORE_DATA);
}
+ Tcl_SetObjLength(objPtr, numBytes);
return -1;
}
@@ -5506,7 +5447,8 @@ ReadChars(
memcpy(RemovePoint(nextPtr), src, (size_t) srcLen);
RecycleBuffer(statePtr, bufPtr, 0);
statePtr->inQueueHead = nextPtr;
- return ReadChars(statePtr, objPtr, charsToRead, offsetPtr, factorPtr);
+ Tcl_SetObjLength(objPtr, numBytes);
+ return ReadChars(statePtr, objPtr, charsToRead, factorPtr);
}
dstRead = dstWrote;
@@ -5519,6 +5461,7 @@ ReadChars(
*/
if (dstWrote == 0) {
+ Tcl_SetObjLength(objPtr, numBytes);
return -1;
}
statePtr->inputEncodingState = oldState;
@@ -5558,7 +5501,7 @@ ReadChars(
if (dstWrote > srcRead + 1) {
*factorPtr = dstWrote * UTF_EXPANSION_FACTOR / srcRead;
}
- *offsetPtr += dstWrote;
+ Tcl_SetObjLength(objPtr, numBytes + dstWrote);
return numChars;
}
diff --git a/generic/tclInt.h b/generic/tclInt.h
index b45f8e3..a998460 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -2479,6 +2479,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);
diff --git a/generic/tclStringObj.c b/generic/tclStringObj.c
index a929d04..d96d814 100644
--- a/generic/tclStringObj.c
+++ b/generic/tclStringObj.c
@@ -1139,7 +1139,8 @@ Tcl_AppendLimitedToObj(
if (ellipsis == NULL) {
ellipsis = "...";
}
- toCopy = Tcl_UtfPrev(bytes+limit+1-strlen(ellipsis), bytes) - bytes;
+ toCopy = (bytes == NULL) ? limit
+ : Tcl_UtfPrev(bytes+limit+1-strlen(ellipsis), bytes) - bytes;
}
/*
@@ -1386,7 +1387,8 @@ AppendUnicodeToUnicodeRep(
* due to the reallocs below.
*/
int offset = -1;
- if (unicode >= stringPtr->unicode && unicode <= stringPtr->unicode
+ if (unicode && unicode >= stringPtr->unicode
+ && unicode <= stringPtr->unicode
+ stringPtr->uallocated / sizeof(Tcl_UniChar)) {
offset = unicode - stringPtr->unicode;
}
@@ -1405,8 +1407,10 @@ AppendUnicodeToUnicodeRep(
* trailing null.
*/
- memcpy(stringPtr->unicode + stringPtr->numChars, unicode,
- appendNumChars * sizeof(Tcl_UniChar));
+ if (unicode) {
+ memcpy(stringPtr->unicode + stringPtr->numChars, unicode,
+ appendNumChars * sizeof(Tcl_UniChar));
+ }
stringPtr->unicode[numChars] = 0;
stringPtr->numChars = numChars;
stringPtr->allocated = 0;
@@ -1478,8 +1482,8 @@ AppendUtfToUnicodeRep(
int numBytes) /* Number of bytes of "bytes" to convert. */
{
Tcl_DString dsPtr;
- int numChars;
- Tcl_UniChar *unicode;
+ int numChars = numBytes;
+ Tcl_UniChar *unicode = NULL;
if (numBytes < 0) {
numBytes = (bytes ? strlen(bytes) : 0);
@@ -1489,8 +1493,11 @@ AppendUtfToUnicodeRep(
}
Tcl_DStringInit(&dsPtr);
- numChars = Tcl_NumUtfChars(bytes, numBytes);
- unicode = (Tcl_UniChar *)Tcl_UtfToUniCharDString(bytes, numBytes, &dsPtr);
+ if (bytes) {
+ numChars = Tcl_NumUtfChars(bytes, numBytes);
+ unicode = (Tcl_UniChar *) Tcl_UtfToUniCharDString(bytes, numBytes,
+ &dsPtr);
+ }
AppendUnicodeToUnicodeRep(objPtr, unicode, numChars);
Tcl_DStringFree(&dsPtr);
}
@@ -1547,7 +1554,7 @@ AppendUtfToUtfRep(
* due to the reallocs below.
*/
int offset = -1;
- if (bytes >= objPtr->bytes
+ if (bytes && bytes >= objPtr->bytes
&& bytes <= objPtr->bytes + objPtr->length) {
offset = bytes - objPtr->bytes;
}
@@ -1585,7 +1592,9 @@ AppendUtfToUtfRep(
stringPtr->numChars = -1;
stringPtr->hasUnicode = 0;
- memcpy(objPtr->bytes + oldLength, bytes, (size_t) numBytes);
+ if (bytes) {
+ memcpy(objPtr->bytes + oldLength, bytes, (size_t) numBytes);
+ }
objPtr->bytes[newLength] = 0;
objPtr->length = newLength;
}