summaryrefslogtreecommitdiffstats
path: root/generic/tclStringObj.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclStringObj.c')
-rw-r--r--generic/tclStringObj.c1671
1 files changed, 818 insertions, 853 deletions
diff --git a/generic/tclStringObj.c b/generic/tclStringObj.c
index b54dc35..86f0c62 100644
--- a/generic/tclStringObj.c
+++ b/generic/tclStringObj.c
@@ -38,44 +38,28 @@
#include "tommath.h"
/*
- * Set COMPAT to 1 to restore the shimmering patterns to those of Tcl 8.5.
- * This is an escape hatch in case the changes have some unexpected unwelcome
- * impact on performance. If things go well, this mechanism can go away when
- * post-8.6 development begins.
- */
-
-#define COMPAT 0
-
-/*
* Prototypes for functions defined later in this file:
*/
static void AppendPrintfToObjVA(Tcl_Obj *objPtr,
const char *format, va_list argList);
static void AppendUnicodeToUnicodeRep(Tcl_Obj *objPtr,
- const Tcl_UniChar *unicode,
- size_t appendNumChars);
+ const Tcl_UniChar *unicode, int appendNumChars);
static void AppendUnicodeToUtfRep(Tcl_Obj *objPtr,
- const Tcl_UniChar *unicode, size_t numChars);
+ const Tcl_UniChar *unicode, int numChars);
static void AppendUtfToUnicodeRep(Tcl_Obj *objPtr,
- const char *bytes, size_t numBytes);
+ const char *bytes, int numBytes);
static void AppendUtfToUtfRep(Tcl_Obj *objPtr,
- const char *bytes, size_t numBytes);
+ const char *bytes, int numBytes);
static void DupStringInternalRep(Tcl_Obj *objPtr,
Tcl_Obj *copyPtr);
-static int ExtendStringRepWithUnicode(Tcl_Obj *objPtr,
- const Tcl_UniChar *unicode, size_t numChars);
-static void ExtendUnicodeRepWithString(Tcl_Obj *objPtr,
- const char *bytes, size_t numBytes,
- size_t numAppendChars);
static void FillUnicodeRep(Tcl_Obj *objPtr);
static void FreeStringInternalRep(Tcl_Obj *objPtr);
-static void GrowStringBuffer(Tcl_Obj *objPtr, size_t needed, int flag);
-static void GrowUnicodeBuffer(Tcl_Obj *objPtr, size_t needed);
+static void GrowUnicodeBuffer(Tcl_Obj *objPtr, int needed);
static int SetStringFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr);
static void SetUnicodeObj(Tcl_Obj *objPtr,
- const Tcl_UniChar *unicode, size_t numChars);
-static size_t UnicodeLength(const Tcl_UniChar *unicode);
+ const Tcl_UniChar *unicode, int numChars);
+static int UnicodeLength(const Tcl_UniChar *unicode);
static void UpdateStringOfString(Tcl_Obj *objPtr);
/*
@@ -83,7 +67,7 @@ static void UpdateStringOfString(Tcl_Obj *objPtr);
* functions that can be invoked by generic object code.
*/
-const Tcl_ObjType tclStringType = {
+Tcl_ObjType tclStringType = {
"string", /* name */
FreeStringInternalRep, /* freeIntRepPro */
DupStringInternalRep, /* dupIntRepProc */
@@ -106,46 +90,48 @@ const Tcl_ObjType tclStringType = {
*/
typedef struct String {
- size_t numChars; /* The number of chars in the string.
- * TCL_STRLEN means this value has not been
- * calculated, and anything else means that
- * there is a valid Unicode rep, or that the
- * number of UTF bytes == the number of
- * chars. */
+ int numChars; /* The number of chars in the string. -1 means
+ * this value has not been calculated. >= 0
+ * means that there is a valid Unicode rep, or
+ * that the number of UTF bytes == the number
+ * of chars. */
size_t allocated; /* The amount of space actually allocated for
* the UTF string (minus 1 byte for the
* termination char). */
- size_t maxChars; /* Max number of chars that can fit in the
- * space allocated for the unicode array. */
+ size_t uallocated; /* The amount of space actually allocated for
+ * the Unicode string (minus 2 bytes for the
+ * termination char). */
int hasUnicode; /* Boolean determining whether the string has
* a Unicode representation. */
- Tcl_UniChar unicode[1]; /* The array of Unicode chars. The actual size
- * of this field depends on the 'maxChars'
+ Tcl_UniChar unicode[2]; /* The array of Unicode chars. The actual size
+ * of this field depends on the 'uallocated'
* field above. */
} String;
#define STRING_MAXCHARS \
- (((size_t)ULONG_MAX - sizeof(String))/sizeof(Tcl_UniChar))
-#define STRING_SIZE(numChars) \
- (sizeof(String) + ((numChars) * sizeof(Tcl_UniChar)))
+ (1 + (int)(((size_t)UINT_MAX - sizeof(String))/sizeof(Tcl_UniChar)))
+#define STRING_UALLOC(numChars) \
+ ((numChars) * sizeof(Tcl_UniChar))
+#define STRING_SIZE(ualloc) \
+ ((unsigned) ((ualloc) \
+ ? (sizeof(String) - sizeof(Tcl_UniChar) + (ualloc)) \
+ : sizeof(String)))
#define stringCheckLimits(numChars) \
- if ((numChars) > STRING_MAXCHARS) { \
- Tcl_Panic("max length for a Tcl unicode value (%lu chars) exceeded", \
+ if ((numChars) < 0 || (numChars) > STRING_MAXCHARS) { \
+ Tcl_Panic("max length for a Tcl unicode value (%d chars) exceeded", \
STRING_MAXCHARS); \
}
-#define stringAttemptAlloc(numChars) \
- (String *) attemptckalloc(STRING_SIZE(numChars))
-#define stringAlloc(numChars) \
- (String *) ckalloc(STRING_SIZE(numChars))
#define stringRealloc(ptr, numChars) \
- (String *) ckrealloc((ptr), STRING_SIZE(numChars))
+ (String *) ckrealloc((char *) ptr, \
+ (unsigned) STRING_SIZE(STRING_UALLOC(numChars)) )
#define stringAttemptRealloc(ptr, numChars) \
- (String *) attemptckrealloc((ptr), STRING_SIZE(numChars))
+ (String *) attemptckrealloc((char *) ptr, \
+ (unsigned) STRING_SIZE(STRING_UALLOC(numChars)) )
#define GET_STRING(objPtr) \
- ((String *) (objPtr)->internalRep.otherValuePtr)
+ ((String *) (objPtr)->internalRep.twoPtrValue.ptr1)
#define SET_STRING(objPtr, stringPtr) \
- ((objPtr)->internalRep.otherValuePtr = (void *) (stringPtr))
-
+ ((objPtr)->internalRep.twoPtrValue.ptr1 = (void *) (stringPtr))
+
/*
* TCL STRING GROWTH ALGORITHM
*
@@ -154,7 +140,8 @@ typedef struct String {
*
* Attempt to allocate 2 * (originalLength + appendLength)
* On failure:
- * attempt to allocate originalLength + 2*appendLength + TCL_MIN_GROWTH
+ * attempt to allocate originalLength + 2*appendLength +
+ * TCL_GROWTH_MIN_ALLOC
*
* This algorithm allows very good performance, as it rapidly increases the
* memory allocated for a given string, which minimizes the number of
@@ -167,98 +154,39 @@ typedef struct String {
* cover the request, but which hopefully will be less than the total
* available memory.
*
- * The addition of TCL_MIN_GROWTH allows for efficient handling of very
+ * The addition of TCL_GROWTH_MIN_ALLOC allows for efficient handling of very
* small appends. Without this extra slush factor, a sequence of several small
* appends would cause several memory allocations. As long as
- * TCL_MIN_GROWTH is a reasonable size, we can avoid that behavior.
+ * TCL_GROWTH_MIN_ALLOC is a reasonable size, we can avoid that behavior.
*
* The growth algorithm can be tuned by adjusting the following parameters:
*
- * TCL_MIN_GROWTH Additional space, in bytes, to allocate when
+ * TCL_GROWTH_MIN_ALLOC Additional space, in bytes, to allocate when
* the double allocation has failed. Default is
- * 1024 (1 kilobyte). See tclInt.h.
+ * 1024 (1 kilobyte).
*/
-#ifndef TCL_MIN_UNICHAR_GROWTH
-#define TCL_MIN_UNICHAR_GROWTH TCL_MIN_GROWTH/sizeof(Tcl_UniChar)
+#ifndef TCL_GROWTH_MIN_ALLOC
+#define TCL_GROWTH_MIN_ALLOC 1024
#endif
static void
-GrowStringBuffer(
- Tcl_Obj *objPtr,
- size_t needed,
- int flag)
-{
- /*
- * Pre-conditions:
- * objPtr->typePtr == &tclStringType
- * needed > stringPtr->allocated
- * flag || objPtr->bytes != NULL
- */
-
- String *stringPtr = GET_STRING(objPtr);
- char *ptr = NULL;
- size_t attempt;
-
- if (objPtr->bytes == tclEmptyStringRep) {
- objPtr->bytes = NULL;
- }
- if (flag == 0 || stringPtr->allocated > 0) {
- attempt = 2 * needed;
- if (attempt+1 > needed) {
- ptr = attemptckrealloc(objPtr->bytes, attempt + 1);
- }
- if (ptr == NULL) {
- /*
- * Take care computing the amount of modest growth to avoid
- * overflow into invalid argument values for attempt.
- */
-
- size_t limit = INT_MAX - needed; // TODO FIXME
- size_t extra = needed - objPtr->length + TCL_MIN_GROWTH;
- int growth = (int) ((extra > limit) ? limit : extra);
-
- attempt = needed + growth;
- if (attempt+1 > needed) {
- ptr = attemptckrealloc(objPtr->bytes, attempt + 1);
- }
- }
- }
- if (ptr == NULL) {
- /*
- * First allocation - just big enough; or last chance fallback.
- */
-
- attempt = needed;
- // TODO Beware the case right at the limit (which should fail...)
- ptr = ckrealloc(objPtr->bytes, attempt + 1);
- }
- objPtr->bytes = ptr;
- stringPtr->allocated = attempt;
-}
-
-static void
GrowUnicodeBuffer(
Tcl_Obj *objPtr,
- size_t needed)
+ int needed)
{
- /*
- * Pre-conditions:
- * objPtr->typePtr == &tclStringType
- * needed > stringPtr->maxChars
- * needed < STRING_MAXCHARS
+ /* Pre-conditions:
+ * objPtr->typePtr == &tclStringType
+ * STRING_UALLOC(needed) > stringPtr->uallocated
+ * needed < STRING_MAXCHARS
*/
-
String *ptr = NULL, *stringPtr = GET_STRING(objPtr);
- size_t attempt;
-
- if (stringPtr->maxChars > 0) {
- /*
- * Subsequent appends - apply the growth algorithm.
- */
+ int attempt;
+ if (stringPtr->uallocated > 0) {
+ /* Subsequent appends - apply the growth algorithm. */
attempt = 2 * needed;
- if (attempt > needed && attempt <= STRING_MAXCHARS) {
+ if (attempt >= 0 && attempt <= STRING_MAXCHARS) {
ptr = stringAttemptRealloc(stringPtr, attempt);
}
if (ptr == NULL) {
@@ -266,30 +194,24 @@ GrowUnicodeBuffer(
* Take care computing the amount of modest growth to avoid
* overflow into invalid argument values for attempt.
*/
-
unsigned int limit = STRING_MAXCHARS - needed;
unsigned int extra = needed - stringPtr->numChars
- + TCL_MIN_UNICHAR_GROWTH;
+ + TCL_GROWTH_MIN_ALLOC/sizeof(Tcl_UniChar);
int growth = (int) ((extra > limit) ? limit : extra);
-
attempt = needed + growth;
- if (attempt > needed) {
- ptr = stringAttemptRealloc(stringPtr, attempt);
- }
+ ptr = stringAttemptRealloc(stringPtr, attempt);
}
}
if (ptr == NULL) {
- /*
- * First allocation - just big enough; or last chance fallback.
- */
-
+ /* First allocation - just big enough; or last chance fallback. */
attempt = needed;
ptr = stringRealloc(stringPtr, attempt);
}
stringPtr = ptr;
- stringPtr->maxChars = attempt;
+ stringPtr->uallocated = STRING_UALLOC(attempt);
SET_STRING(objPtr, stringPtr);
}
+
/*
*----------------------------------------------------------------------
@@ -322,9 +244,9 @@ Tcl_Obj *
Tcl_NewStringObj(
const char *bytes, /* Points to the first of the length bytes
* used to initialize the new object. */
- size_t length) /* The number of bytes to copy from "bytes"
+ int length) /* The number of bytes to copy from "bytes"
* when initializing the new object. If
- * TCL_STRLEN, use bytes up to the first NUL
+ * negative, use bytes up to the first NUL
* byte. */
{
return Tcl_DbNewStringObj(bytes, length, "unknown", 0);
@@ -334,14 +256,14 @@ Tcl_Obj *
Tcl_NewStringObj(
const char *bytes, /* Points to the first of the length bytes
* used to initialize the new object. */
- size_t length) /* The number of bytes to copy from "bytes"
+ int length) /* The number of bytes to copy from "bytes"
* when initializing the new object. If
- * TCL_STRLEN, use bytes up to the first NUL
+ * negative, use bytes up to the first NUL
* byte. */
{
- Tcl_Obj *objPtr;
+ register Tcl_Obj *objPtr;
- if (length == TCL_STRLEN) {
+ if (length < 0) {
length = (bytes? strlen(bytes) : 0);
}
TclNewStringObj(objPtr, bytes, length);
@@ -383,18 +305,18 @@ Tcl_Obj *
Tcl_DbNewStringObj(
const char *bytes, /* Points to the first of the length bytes
* used to initialize the new object. */
- size_t length, /* The number of bytes to copy from "bytes"
+ int length, /* The number of bytes to copy from "bytes"
* when initializing the new object. If
- * TCL_STRLEN, use bytes up to the first NUL
+ * negative, use bytes up to the first NUL
* byte. */
const char *file, /* The name of the source file calling this
* function; used for debugging. */
int line) /* Line number in the source file; used for
* debugging. */
{
- Tcl_Obj *objPtr;
+ register Tcl_Obj *objPtr;
- if (length == TCL_STRLEN) {
+ if (length < 0) {
length = (bytes? strlen(bytes) : 0);
}
TclDbNewObj(objPtr, file, line);
@@ -406,9 +328,9 @@ Tcl_Obj *
Tcl_DbNewStringObj(
const char *bytes, /* Points to the first of the length bytes
* used to initialize the new object. */
- size_t length, /* The number of bytes to copy from "bytes"
+ register int length, /* The number of bytes to copy from "bytes"
* when initializing the new object. If
- * TCL_STRLEN, use bytes up to the first NUL
+ * negative, use bytes up to the first NUL
* byte. */
const char *file, /* The name of the source file calling this
* function; used for debugging. */
@@ -442,7 +364,7 @@ Tcl_Obj *
Tcl_NewUnicodeObj(
const Tcl_UniChar *unicode, /* The unicode string used to initialize the
* new object. */
- size_t numChars) /* Number of characters in the unicode
+ int numChars) /* Number of characters in the unicode
* string. */
{
Tcl_Obj *objPtr;
@@ -469,56 +391,70 @@ Tcl_NewUnicodeObj(
*----------------------------------------------------------------------
*/
-size_t
+int
Tcl_GetCharLength(
Tcl_Obj *objPtr) /* The String object to get the num chars
* of. */
{
String *stringPtr;
- size_t numChars;
-
- /*
- * Optimize the case where we're really dealing with a bytearray object
- * without string representation; we don't need to convert to a string to
- * perform the get-length operation.
- */
-
- if (TclIsPureByteArray(objPtr)) {
- size_t length;
-
- (void) Tcl_GetByteArrayFromObj(objPtr, &length);
- return length;
- }
-
- /*
- * OK, need to work with the object as a string.
- */
SetStringFromAny(NULL, objPtr);
stringPtr = GET_STRING(objPtr);
- numChars = stringPtr->numChars;
/*
- * If numChars is unknown, compute it.
+ * If numChars is unknown, then calculate the number of characaters while
+ * populating the Unicode string.
*/
- if (numChars == TCL_STRLEN) {
- TclNumUtfChars(numChars, objPtr->bytes, objPtr->length);
- stringPtr->numChars = numChars;
+ if (stringPtr->numChars == -1) {
+ register int i = objPtr->length;
+ register unsigned char *str = (unsigned char *) objPtr->bytes;
+
+ /*
+ * This is a speed sensitive function, so run specially over the
+ * string to count continuous ascii characters before resorting to the
+ * Tcl_NumUtfChars call. This is a long form of:
+ stringPtr->numChars = Tcl_NumUtfChars(objPtr->bytes,objPtr->length);
+ *
+ * TODO: Consider macro-izing this.
+ */
-#if COMPAT
- if (numChars < objPtr->length) {
+ while (i && (*str < 0xC0)) {
+ i--;
+ str++;
+ }
+ stringPtr->numChars = objPtr->length - i;
+ if (i) {
+ stringPtr->numChars += Tcl_NumUtfChars(objPtr->bytes
+ + (objPtr->length - i), i);
+ }
+
+ if (stringPtr->numChars == objPtr->length) {
/*
- * Since we've just computed the number of chars, and not all UTF
- * chars are 1-byte long, go ahead and populate the unicode
+ * Since we've just calculated the number of chars, and all UTF
+ * chars are 1-byte long, we don't need to store the unicode
+ * string.
+ */
+
+ stringPtr->hasUnicode = 0;
+ } else {
+ /*
+ * Since we've just calucalated the number of chars, and not all
+ * UTF chars are 1-byte long, go ahead and populate the unicode
* string.
*/
FillUnicodeRep(objPtr);
+
+ /*
+ * We need to fetch the pointer again because we have just
+ * reallocated the structure to make room for the Unicode data.
+ */
+
+ stringPtr = GET_STRING(objPtr);
}
-#endif
}
- return numChars;
+ return stringPtr->numChars;
}
/*
@@ -542,44 +478,41 @@ Tcl_UniChar
Tcl_GetUniChar(
Tcl_Obj *objPtr, /* The object to get the Unicode charater
* from. */
- size_t index) /* Get the index'th Unicode character. */
+ int index) /* Get the index'th Unicode character. */
{
+ Tcl_UniChar unichar;
String *stringPtr;
- /*
- * Optimize the case where we're really dealing with a bytearray object
- * without string representation; we don't need to convert to a string to
- * perform the indexing operation.
- */
-
- if (TclIsPureByteArray(objPtr)) {
- unsigned char *bytes = Tcl_GetByteArrayFromObj(objPtr, NULL);
+ SetStringFromAny(NULL, objPtr);
+ stringPtr = GET_STRING(objPtr);
- return (Tcl_UniChar) bytes[index];
- }
+ if (stringPtr->numChars == -1) {
+ /*
+ * We haven't yet calculated the length, so we don't have the Unicode
+ * str. We need to know the number of chars before we can do indexing.
+ */
- /*
- * OK, need to work with the object as a string.
- */
+ Tcl_GetCharLength(objPtr);
- SetStringFromAny(NULL, objPtr);
- stringPtr = GET_STRING(objPtr);
+ /*
+ * We need to fetch the pointer again because we may have just
+ * reallocated the structure.
+ */
+ stringPtr = GET_STRING(objPtr);
+ }
if (stringPtr->hasUnicode == 0) {
/*
- * If numChars is unknown, compute it.
+ * All of the characters in the Utf string are 1 byte chars, so we
+ * don't store the unicode char. We get the Utf string and convert the
+ * index'th byte to a Unicode character.
*/
- if (stringPtr->numChars == TCL_STRLEN) {
- TclNumUtfChars(stringPtr->numChars, objPtr->bytes, objPtr->length);
- }
- if (stringPtr->numChars == objPtr->length) {
- return (Tcl_UniChar) objPtr->bytes[index];
- }
- FillUnicodeRep(objPtr);
- stringPtr = GET_STRING(objPtr);
+ unichar = (Tcl_UniChar) objPtr->bytes[index];
+ } else {
+ unichar = stringPtr->unicode[index];
}
- return stringPtr->unicode[index];
+ return unichar;
}
/*
@@ -606,7 +539,30 @@ Tcl_GetUnicode(
Tcl_Obj *objPtr) /* The object to find the unicode string
* for. */
{
- return Tcl_GetUnicodeFromObj(objPtr, NULL);
+ String *stringPtr;
+
+ SetStringFromAny(NULL, objPtr);
+ stringPtr = GET_STRING(objPtr);
+
+ if ((stringPtr->numChars == -1) || (stringPtr->hasUnicode == 0)) {
+ /*
+ * We haven't yet calculated the length, or all of the characters in
+ * the Utf string are 1 byte chars (so we didn't store the unicode
+ * str). Since this function must return a unicode string, and one has
+ * not yet been stored, force the Unicode to be calculated and stored
+ * now.
+ */
+
+ FillUnicodeRep(objPtr);
+
+ /*
+ * We need to fetch the pointer again because we have just reallocated
+ * the structure to make room for the Unicode data.
+ */
+
+ stringPtr = GET_STRING(objPtr);
+ }
+ return stringPtr->unicode;
}
/*
@@ -632,7 +588,7 @@ Tcl_UniChar *
Tcl_GetUnicodeFromObj(
Tcl_Obj *objPtr, /* The object to find the unicode string
* for. */
- size_t *lengthPtr) /* If non-NULL, the location where the string
+ int *lengthPtr) /* If non-NULL, the location where the string
* rep's unichar length should be stored. If
* NULL, no length is stored. */
{
@@ -641,8 +597,22 @@ Tcl_GetUnicodeFromObj(
SetStringFromAny(NULL, objPtr);
stringPtr = GET_STRING(objPtr);
- if (stringPtr->hasUnicode == 0) {
+ if ((stringPtr->numChars == -1) || (stringPtr->hasUnicode == 0)) {
+ /*
+ * We haven't yet calculated the length, or all of the characters in
+ * the Utf string are 1 byte chars (so we didn't store the unicode
+ * str). Since this function must return a unicode string, and one has
+ * not yet been stored, force the Unicode to be calculated and stored
+ * now.
+ */
+
FillUnicodeRep(objPtr);
+
+ /*
+ * We need to fetch the pointer again because we have just reallocated
+ * the structure to make room for the Unicode data.
+ */
+
stringPtr = GET_STRING(objPtr);
}
@@ -674,56 +644,55 @@ Tcl_GetUnicodeFromObj(
Tcl_Obj *
Tcl_GetRange(
Tcl_Obj *objPtr, /* The Tcl object to find the range of. */
- size_t first, /* First index of the range. */
- size_t last) /* Last index of the range. */
+ int first, /* First index of the range. */
+ int last) /* Last index of the range. */
{
Tcl_Obj *newObjPtr; /* The Tcl object to find the range of. */
String *stringPtr;
- /*
- * Optimize the case where we're really dealing with a bytearray object
- * without string representation; we don't need to convert to a string to
- * perform the substring operation.
- */
-
- if (TclIsPureByteArray(objPtr)) {
- unsigned char *bytes = Tcl_GetByteArrayFromObj(objPtr, NULL);
-
- return Tcl_NewByteArrayObj(bytes+first, last-first+1);
- }
-
- /*
- * OK, need to work with the object as a string.
- */
-
SetStringFromAny(NULL, objPtr);
stringPtr = GET_STRING(objPtr);
- if (stringPtr->hasUnicode == 0) {
+ if (stringPtr->numChars == -1) {
/*
- * If numChars is unknown, compute it.
+ * We haven't yet calculated the length, so we don't have the Unicode
+ * str. We need to know the number of chars before we can do indexing.
*/
- if (stringPtr->numChars == TCL_STRLEN) {
- TclNumUtfChars(stringPtr->numChars, objPtr->bytes, objPtr->length);
- }
- if (stringPtr->numChars == objPtr->length) {
- newObjPtr = Tcl_NewStringObj(objPtr->bytes + first, last-first+1);
+ Tcl_GetCharLength(objPtr);
- /*
- * Since we know the char length of the result, store it.
- */
+ /*
+ * We need to fetch the pointer again because we may have just
+ * reallocated the structure.
+ */
- SetStringFromAny(NULL, newObjPtr);
- stringPtr = GET_STRING(newObjPtr);
- stringPtr->numChars = newObjPtr->length;
- return newObjPtr;
- }
- FillUnicodeRep(objPtr);
stringPtr = GET_STRING(objPtr);
}
- return Tcl_NewUnicodeObj(stringPtr->unicode + first, last-first+1);
+ if (objPtr->bytes && (stringPtr->numChars == objPtr->length)) {
+ char *str = TclGetString(objPtr);
+
+ /*
+ * All of the characters in the Utf string are 1 byte chars, so we
+ * don't store the unicode char. Create a new string object containing
+ * the specified range of chars.
+ */
+
+ newObjPtr = Tcl_NewStringObj(&str[first], last-first+1);
+
+ /*
+ * Since we know the new string only has 1-byte chars, we can set it's
+ * numChars field.
+ */
+
+ SetStringFromAny(NULL, newObjPtr);
+ stringPtr = GET_STRING(newObjPtr);
+ stringPtr->numChars = last-first+1;
+ } else {
+ newObjPtr = Tcl_NewUnicodeObj(stringPtr->unicode + first,
+ last-first+1);
+ }
+ return newObjPtr;
}
/*
@@ -749,13 +718,12 @@ Tcl_GetRange(
void
Tcl_SetStringObj(
- Tcl_Obj *objPtr, /* Object whose internal rep to init. */
+ register Tcl_Obj *objPtr, /* Object whose internal rep to init. */
const char *bytes, /* Points to the first of the length bytes
* used to initialize the object. */
- size_t length) /* The number of bytes to copy from "bytes"
- * when initializing the object. If
- * TCL_STRLEN, use bytes up to the first NUL
- * byte.*/
+ register int length) /* The number of bytes to copy from "bytes"
+ * when initializing the object. If negative,
+ * use bytes up to the first NUL byte.*/
{
if (Tcl_IsShared(objPtr)) {
Tcl_Panic("%s called with shared object", "Tcl_SetStringObj");
@@ -766,6 +734,7 @@ Tcl_SetStringObj(
*/
TclFreeIntRep(objPtr);
+ objPtr->typePtr = NULL;
/*
* Free any old string rep, then set the string rep to a copy of the
@@ -773,7 +742,7 @@ Tcl_SetStringObj(
*/
TclInvalidateStringRep(objPtr);
- if (length == TCL_STRLEN) {
+ if (length < 0) {
length = (bytes? strlen(bytes) : 0);
}
TclInitStringRep(objPtr, bytes, length);
@@ -804,83 +773,100 @@ Tcl_SetStringObj(
void
Tcl_SetObjLength(
- Tcl_Obj *objPtr, /* Pointer to object. This object must not
+ register Tcl_Obj *objPtr, /* Pointer to object. This object must not
* currently be shared. */
- size_t length) /* Number of bytes desired for string
+ register int length) /* Number of bytes desired for string
* representation of object, not including
* terminating null byte. */
{
String *stringPtr;
- if (length == TCL_STRLEN) {
+ if (length < 0) {
/*
- * Setting to a negative length is nonsense. This is probably the
+ * Setting to a negative length is nonsense. This is probably the
* result of overflowing the signed integer range.
*/
-
Tcl_Panic("Tcl_SetObjLength: negative length requested: "
- "%lu (integer overflow?)", length);
+ "%d (integer overflow?)", length);
}
if (Tcl_IsShared(objPtr)) {
Tcl_Panic("%s called with shared object", "Tcl_SetObjLength");
}
-
- if (objPtr->bytes && objPtr->length == length) {
- return;
- }
-
SetStringFromAny(NULL, objPtr);
+
stringPtr = GET_STRING(objPtr);
- if (objPtr->bytes != NULL) {
+ /*
+ * Check that we're not extending a pure unicode string.
+ */
+
+ if ((size_t)length > stringPtr->allocated &&
+ (objPtr->bytes != NULL || stringPtr->hasUnicode == 0)) {
/*
- * Change length of an existing string rep.
+ * Not enough space in current string. Reallocate the string space and
+ * free the old string.
*/
- if (length > stringPtr->allocated) {
- /*
- * Need to enlarge the buffer.
- */
- if (objPtr->bytes == tclEmptyStringRep) {
- objPtr->bytes = ckalloc(length + 1);
- } else {
- objPtr->bytes = ckrealloc(objPtr->bytes, length + 1);
+
+ if (objPtr->bytes != tclEmptyStringRep) {
+ objPtr->bytes = ckrealloc((char *) objPtr->bytes,
+ (unsigned) (length + 1));
+ } else {
+ char *newBytes = ckalloc((unsigned) (length+1));
+
+ if (objPtr->bytes != NULL && objPtr->length != 0) {
+ memcpy(newBytes, objPtr->bytes, (size_t) objPtr->length);
+ TclInvalidateStringRep(objPtr);
}
- stringPtr->allocated = length;
+ objPtr->bytes = newBytes;
}
+ stringPtr->allocated = length;
+ /*
+ * Invalidate the unicode data.
+ */
+
+ stringPtr->hasUnicode = 0;
+ }
+
+ if (objPtr->bytes != NULL) {
objPtr->length = length;
- objPtr->bytes[length] = 0;
+ if (objPtr->bytes != tclEmptyStringRep) {
+ /*
+ * Ensure the string is NUL-terminated.
+ */
+
+ objPtr->bytes[length] = 0;
+ }
/*
* Invalidate the unicode data.
*/
- stringPtr->numChars = TCL_STRLEN;
+ stringPtr->numChars = -1;
stringPtr->hasUnicode = 0;
} else {
/*
* Changing length of pure unicode string.
*/
+ size_t uallocated = STRING_UALLOC(length);
+
stringCheckLimits(length);
- if (length > stringPtr->maxChars) {
+ if (uallocated > stringPtr->uallocated) {
stringPtr = stringRealloc(stringPtr, length);
SET_STRING(objPtr, stringPtr);
- stringPtr->maxChars = length;
+ stringPtr->uallocated = uallocated;
}
+ stringPtr->numChars = length;
+ stringPtr->hasUnicode = (length > 0);
/*
- * Mark the new end of the unicode string
+ * Ensure the string is NUL-terminated.
*/
- stringPtr->numChars = length;
stringPtr->unicode[length] = 0;
- stringPtr->hasUnicode = 1;
-
- /*
- * Can only get here when objPtr->bytes == NULL. No need to invalidate
- * the string rep.
- */
+ stringPtr->allocated = 0;
+ objPtr->length = 0;
}
}
@@ -909,93 +895,111 @@ Tcl_SetObjLength(
int
Tcl_AttemptSetObjLength(
- Tcl_Obj *objPtr, /* Pointer to object. This object must not
+ register Tcl_Obj *objPtr, /* Pointer to object. This object must not
* currently be shared. */
- size_t length) /* Number of bytes desired for string
+ register int length) /* Number of bytes desired for string
* representation of object, not including
* terminating null byte. */
{
String *stringPtr;
- if (length == TCL_STRLEN) {
+ if (length < 0) {
/*
- * Setting to a negative length is nonsense. This is probably the
+ * Setting to a negative length is nonsense. This is probably the
* result of overflowing the signed integer range.
*/
-
return 0;
}
if (Tcl_IsShared(objPtr)) {
Tcl_Panic("%s called with shared object", "Tcl_AttemptSetObjLength");
}
- if (objPtr->bytes && objPtr->length == length) {
- return 1;
- }
-
SetStringFromAny(NULL, objPtr);
+
stringPtr = GET_STRING(objPtr);
- if (objPtr->bytes != NULL) {
+ /*
+ * Check that we're not extending a pure unicode string.
+ */
+
+ if (length > (int) stringPtr->allocated &&
+ (objPtr->bytes != NULL || stringPtr->hasUnicode == 0)) {
+ char *newBytes;
+
/*
- * Change length of an existing string rep.
+ * Not enough space in current string. Reallocate the string space and
+ * free the old string.
*/
- if (length > stringPtr->allocated) {
- /*
- * Need to enlarge the buffer.
- */
-
- char *newBytes;
- if (objPtr->bytes == tclEmptyStringRep) {
- newBytes = attemptckalloc(length + 1);
- } else {
- newBytes = attemptckrealloc(objPtr->bytes, length + 1);
+ if (objPtr->bytes != tclEmptyStringRep) {
+ newBytes = attemptckrealloc(objPtr->bytes,
+ (unsigned)(length + 1));
+ if (newBytes == NULL) {
+ return 0;
}
+ } else {
+ newBytes = attemptckalloc((unsigned) (length + 1));
if (newBytes == NULL) {
return 0;
}
- objPtr->bytes = newBytes;
- stringPtr->allocated = length;
+ if (objPtr->bytes != NULL && objPtr->length != 0) {
+ memcpy(newBytes, objPtr->bytes, (size_t) objPtr->length);
+ TclInvalidateStringRep(objPtr);
+ }
}
+ objPtr->bytes = newBytes;
+ stringPtr->allocated = length;
+ /*
+ * Invalidate the unicode data.
+ */
+
+ stringPtr->hasUnicode = 0;
+ }
+
+ if (objPtr->bytes != NULL) {
objPtr->length = length;
- objPtr->bytes[length] = 0;
+ if (objPtr->bytes != tclEmptyStringRep) {
+ /*
+ * Ensure the string is NULL-terminated.
+ */
+
+ objPtr->bytes[length] = 0;
+ }
/*
* Invalidate the unicode data.
*/
- stringPtr->numChars = TCL_STRLEN;
+ stringPtr->numChars = -1;
stringPtr->hasUnicode = 0;
} else {
/*
* Changing length of pure unicode string.
*/
+ size_t uallocated = STRING_UALLOC(length);
if (length > STRING_MAXCHARS) {
return 0;
}
- if (length > stringPtr->maxChars) {
+
+ if (uallocated > stringPtr->uallocated) {
stringPtr = stringAttemptRealloc(stringPtr, length);
if (stringPtr == NULL) {
return 0;
}
SET_STRING(objPtr, stringPtr);
- stringPtr->maxChars = length;
+ stringPtr->uallocated = uallocated;
}
+ stringPtr->numChars = length;
+ stringPtr->hasUnicode = (length > 0);
/*
- * Mark the new end of the unicode string.
+ * Ensure the string is NUL-terminated.
*/
stringPtr->unicode[length] = 0;
- stringPtr->numChars = length;
- stringPtr->hasUnicode = 1;
-
- /*
- * Can only get here when objPtr->bytes == NULL. No need to invalidate
- * the string rep.
- */
+ stringPtr->allocated = 0;
+ objPtr->length = 0;
}
return 1;
}
@@ -1021,7 +1025,7 @@ Tcl_SetUnicodeObj(
Tcl_Obj *objPtr, /* The object to set the string of. */
const Tcl_UniChar *unicode, /* The unicode string used to initialize the
* object. */
- size_t numChars) /* Number of characters in the unicode
+ int numChars) /* Number of characters in the unicode
* string. */
{
if (Tcl_IsShared(objPtr)) {
@@ -1031,19 +1035,15 @@ Tcl_SetUnicodeObj(
SetUnicodeObj(objPtr, unicode, numChars);
}
-static size_t
+static int
UnicodeLength(
const Tcl_UniChar *unicode)
{
- size_t numChars = 0;
+ int numChars = 0;
if (unicode) {
- while (unicode[numChars] != 0) {
- size_t oldNumChars = numChars++;
- if (oldNumChars > numChars) {
- // TODO Handle overflow better?
- break;
- }
+ while (numChars >= 0 && unicode[numChars] != 0) {
+ numChars++;
}
}
stringCheckLimits(numChars);
@@ -1055,12 +1055,13 @@ SetUnicodeObj(
Tcl_Obj *objPtr, /* The object to set the string of. */
const Tcl_UniChar *unicode, /* The unicode string used to initialize the
* object. */
- size_t numChars) /* Number of characters in the unicode
+ int numChars) /* Number of characters in the unicode
* string. */
{
String *stringPtr;
+ size_t uallocated;
- if (numChars == TCL_STRLEN) {
+ if (numChars < 0) {
numChars = UnicodeLength(unicode);
}
@@ -1069,18 +1070,19 @@ SetUnicodeObj(
*/
stringCheckLimits(numChars);
- stringPtr = stringAlloc(numChars);
- SET_STRING(objPtr, stringPtr);
- objPtr->typePtr = &tclStringType;
+ uallocated = STRING_UALLOC(numChars);
+ stringPtr = (String *) ckalloc(STRING_SIZE(uallocated));
- stringPtr->maxChars = numChars;
- memcpy(stringPtr->unicode, unicode, numChars * sizeof(Tcl_UniChar));
- stringPtr->unicode[numChars] = 0;
stringPtr->numChars = numChars;
- stringPtr->hasUnicode = 1;
+ stringPtr->uallocated = uallocated;
+ stringPtr->hasUnicode = (numChars > 0);
+ stringPtr->allocated = 0;
+ memcpy(stringPtr->unicode, unicode, uallocated);
+ stringPtr->unicode[numChars] = 0;
TclInvalidateStringRep(objPtr);
- stringPtr->allocated = 0;
+ objPtr->typePtr = &tclStringType;
+ SET_STRING(objPtr, stringPtr);
}
/*
@@ -1103,14 +1105,13 @@ SetUnicodeObj(
void
Tcl_AppendLimitedToObj(
- Tcl_Obj *objPtr, /* Points to the object to append to. */
+ register Tcl_Obj *objPtr, /* Points to the object to append to. */
const char *bytes, /* Points to the bytes to append to the
* object. */
- size_t length, /* The number of bytes available to be
- * appended from "bytes". If TCL_STRLEN, then
- * all bytes up to a NUL byte are
- * available. */
- size_t limit, /* The maximum number of bytes to append to
+ register int length, /* The number of bytes available to be
+ * appended from "bytes". If < 0, then all
+ * bytes up to a NUL byte are available. */
+ register int limit, /* The maximum number of bytes to append to
* the object. */
const char *ellipsis) /* Ellipsis marker string, appended to the
* object to indicate not all available bytes
@@ -1123,20 +1124,23 @@ Tcl_AppendLimitedToObj(
Tcl_Panic("%s called with shared object", "Tcl_AppendLimitedToObj");
}
- if (length == TCL_STRLEN) {
+ SetStringFromAny(NULL, objPtr);
+
+ if (length < 0) {
length = (bytes ? strlen(bytes) : 0);
}
if (length == 0) {
return;
}
- if (length <= limit || limit == INT_MAX) {
+ if (length <= limit) {
toCopy = length;
} else {
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;
}
/*
@@ -1145,10 +1149,8 @@ Tcl_AppendLimitedToObj(
* objPtr's string rep.
*/
- SetStringFromAny(NULL, objPtr);
stringPtr = GET_STRING(objPtr);
-
- if (stringPtr->hasUnicode && stringPtr->numChars > 0) {
+ if (stringPtr->hasUnicode != 0) {
AppendUtfToUnicodeRep(objPtr, bytes, toCopy);
} else {
AppendUtfToUtfRep(objPtr, bytes, toCopy);
@@ -1159,10 +1161,10 @@ Tcl_AppendLimitedToObj(
}
stringPtr = GET_STRING(objPtr);
- if (stringPtr->hasUnicode && stringPtr->numChars > 0) {
- AppendUtfToUnicodeRep(objPtr, ellipsis, strlen(ellipsis));
+ if (stringPtr->hasUnicode != 0) {
+ AppendUtfToUnicodeRep(objPtr, ellipsis, -1);
} else {
- AppendUtfToUtfRep(objPtr, ellipsis, strlen(ellipsis));
+ AppendUtfToUtfRep(objPtr, ellipsis, -1);
}
}
@@ -1185,14 +1187,13 @@ Tcl_AppendLimitedToObj(
void
Tcl_AppendToObj(
- Tcl_Obj *objPtr, /* Points to the object to append to. */
+ register Tcl_Obj *objPtr, /* Points to the object to append to. */
const char *bytes, /* Points to the bytes to append to the
* object. */
- size_t length) /* The number of bytes to append from "bytes".
- * If TCL_STRLEN, then append all bytes up to
- * NUL byte. */
+ register int length) /* The number of bytes to append from "bytes".
+ * If < 0, then append all bytes up to NUL
+ * byte. */
{
- // TODO Handle appending more than INT_MAX bytes
Tcl_AppendLimitedToObj(objPtr, bytes, length, INT_MAX, NULL);
}
@@ -1202,7 +1203,7 @@ Tcl_AppendToObj(
* Tcl_AppendUnicodeToObj --
*
* This function appends a Unicode string to an object in the most
- * efficient manner possible. Length must *not* be TCL_STRLEN.
+ * efficient manner possible. Length must be >= 0.
*
* Results:
* None.
@@ -1215,10 +1216,10 @@ Tcl_AppendToObj(
void
Tcl_AppendUnicodeToObj(
- Tcl_Obj *objPtr, /* Points to the object to append to. */
+ register Tcl_Obj *objPtr, /* Points to the object to append to. */
const Tcl_UniChar *unicode, /* The unicode string to append to the
* object. */
- size_t length) /* Number of chars in "unicode". */
+ int length) /* Number of chars in "unicode". */
{
String *stringPtr;
@@ -1239,11 +1240,7 @@ Tcl_AppendUnicodeToObj(
* objPtr's string rep.
*/
- if (stringPtr->hasUnicode
-#if COMPAT
- && stringPtr->numChars > 0
-#endif
- ) {
+ if (stringPtr->hasUnicode != 0) {
AppendUnicodeToUnicodeRep(objPtr, unicode, length);
} else {
AppendUnicodeToUtfRep(objPtr, unicode, length);
@@ -1274,73 +1271,35 @@ Tcl_AppendObjToObj(
Tcl_Obj *appendObjPtr) /* Object to append. */
{
String *stringPtr;
- size_t length, numChars, appendNumChars = TCL_STRLEN;
- const char *bytes;
-
- /*
- * Special case: second object is standard-empty is fast case. We know
- * that appending nothing to anything leaves that starting anything...
- */
-
- if (appendObjPtr->bytes == tclEmptyStringRep) {
- return;
- }
-
- /*
- * Handle append of one bytearray object to another as a special case.
- * Note that we only do this when the objects don't have string reps; if
- * it did, then appending the byte arrays together could well lose
- * information; this is a special-case optimization only.
- */
-
- if ((TclIsPureByteArray(objPtr) || objPtr->bytes == tclEmptyStringRep)
- && TclIsPureByteArray(appendObjPtr)) {
- unsigned char *bytesSrc;
- size_t lengthSrc, lengthTotal;
-
- /*
- * We do not assume that objPtr and appendObjPtr must be distinct!
- * This makes this code a bit more complex than it otherwise would be,
- * but in turn makes it much safer.
- */
-
- (void) Tcl_GetByteArrayFromObj(objPtr, &length);
- (void) Tcl_GetByteArrayFromObj(appendObjPtr, &lengthSrc);
- lengthTotal = length + lengthSrc;
- if (((length > lengthSrc) ? length : lengthSrc) > lengthTotal) {
- Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX);
- }
- bytesSrc = Tcl_GetByteArrayFromObj(appendObjPtr, NULL);
- TclAppendBytesToByteArray(objPtr, bytesSrc, lengthSrc);
- return;
- }
-
- /*
- * Must append as strings.
- */
+ int length, numChars, allOneByteChars;
+ char *bytes;
SetStringFromAny(NULL, objPtr);
- stringPtr = GET_STRING(objPtr);
/*
* If objPtr has a valid Unicode rep, then get a Unicode string from
* appendObjPtr and append it.
*/
- if (stringPtr->hasUnicode
-#if COMPAT
- && stringPtr->numChars > 0
-#endif
- ) {
+ stringPtr = GET_STRING(objPtr);
+ if (stringPtr->hasUnicode != 0) {
/*
* If appendObjPtr is not of the "String" type, don't convert it.
*/
if (appendObjPtr->typePtr == &tclStringType) {
- Tcl_UniChar *unicode =
- Tcl_GetUnicodeFromObj(appendObjPtr, &numChars);
+ stringPtr = GET_STRING(appendObjPtr);
+ if ((stringPtr->numChars == -1) || (stringPtr->hasUnicode == 0)) {
+ /*
+ * If appendObjPtr is a string obj with no valid Unicode rep,
+ * then fill its unicode rep.
+ */
- AppendUnicodeToUnicodeRep(objPtr, unicode, numChars);
+ FillUnicodeRep(appendObjPtr);
+ stringPtr = GET_STRING(appendObjPtr);
+ }
+ AppendUnicodeToUnicodeRep(objPtr, stringPtr->unicode,
+ stringPtr->numChars);
} else {
bytes = TclGetStringFromObj(appendObjPtr, &length);
AppendUtfToUnicodeRep(objPtr, bytes, length);
@@ -1356,21 +1315,21 @@ Tcl_AppendObjToObj(
bytes = TclGetStringFromObj(appendObjPtr, &length);
+ allOneByteChars = 0;
numChars = stringPtr->numChars;
- if (numChars != TCL_STRLEN && appendObjPtr->typePtr == &tclStringType) {
- String *appendStringPtr = GET_STRING(appendObjPtr);
-
- appendNumChars = appendStringPtr->numChars;
+ if ((numChars >= 0) && (appendObjPtr->typePtr == &tclStringType)) {
+ stringPtr = GET_STRING(appendObjPtr);
+ if ((stringPtr->numChars >= 0) && (stringPtr->numChars == length)) {
+ numChars += stringPtr->numChars;
+ allOneByteChars = 1;
+ }
}
AppendUtfToUtfRep(objPtr, bytes, length);
- if (numChars != TCL_STRLEN && appendNumChars != TCL_STRLEN
-#if COMPAT
- && appendNumChars == length
-#endif
- ) {
- stringPtr->numChars = numChars + appendNumChars;
+ if (allOneByteChars) {
+ stringPtr = GET_STRING(objPtr);
+ stringPtr->numChars = numChars;
}
}
@@ -1395,12 +1354,12 @@ static void
AppendUnicodeToUnicodeRep(
Tcl_Obj *objPtr, /* Points to the object to append to. */
const Tcl_UniChar *unicode, /* String to append. */
- size_t appendNumChars) /* Number of chars of "unicode" to append. */
+ int appendNumChars) /* Number of chars of "unicode" to append. */
{
String *stringPtr;
int numChars;
- if (appendNumChars == TCL_STRLEN) {
+ if (appendNumChars < 0) {
appendNumChars = UnicodeLength(unicode);
}
if (appendNumChars == 0) {
@@ -1421,27 +1380,23 @@ AppendUnicodeToUnicodeRep(
numChars = stringPtr->numChars + appendNumChars;
stringCheckLimits(numChars);
- if (numChars > stringPtr->maxChars) {
- ptrdiff_t offset = -1;
-
+ if (STRING_UALLOC(numChars) > stringPtr->uallocated) {
/*
* Protect against case where unicode points into the existing
- * stringPtr->unicode array. Force it to follow any relocations due to
- * the reallocs below.
+ * stringPtr->unicode array. Force it to follow any relocations
+ * due to the reallocs below.
*/
-
- if (unicode >= stringPtr->unicode
- && unicode <= stringPtr->unicode + stringPtr->maxChars) {
+ int offset = -1;
+ if (unicode && unicode >= stringPtr->unicode
+ && unicode <= stringPtr->unicode
+ + stringPtr->uallocated / sizeof(Tcl_UniChar)) {
offset = unicode - stringPtr->unicode;
}
GrowUnicodeBuffer(objPtr, numChars);
stringPtr = GET_STRING(objPtr);
- /*
- * Relocate unicode if needed; see above.
- */
-
+ /* Relocate unicode if needed; see above. */
if (offset >= 0) {
unicode = stringPtr->unicode + offset;
}
@@ -1452,8 +1407,10 @@ AppendUnicodeToUnicodeRep(
* trailing null.
*/
- memmove(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;
@@ -1482,23 +1439,22 @@ static void
AppendUnicodeToUtfRep(
Tcl_Obj *objPtr, /* Points to the object to append to. */
const Tcl_UniChar *unicode, /* String to convert to UTF. */
- size_t numChars) /* Number of chars of "unicode" to convert. */
+ int numChars) /* Number of chars of "unicode" to convert. */
{
- String *stringPtr = GET_STRING(objPtr);
-
- numChars = ExtendStringRepWithUnicode(objPtr, unicode, numChars);
+ Tcl_DString dsPtr;
+ const char *bytes;
- if (stringPtr->numChars != TCL_STRLEN) {
- stringPtr->numChars += numChars;
+ if (numChars < 0) {
+ numChars = UnicodeLength(unicode);
+ }
+ if (numChars == 0) {
+ return;
}
-#if COMPAT
- /*
- * Invalidate the unicode rep.
- */
-
- stringPtr->hasUnicode = 0;
-#endif
+ Tcl_DStringInit(&dsPtr);
+ bytes = Tcl_UniCharToUtfDString(unicode, numChars, &dsPtr);
+ AppendUtfToUtfRep(objPtr, bytes, Tcl_DStringLength(&dsPtr));
+ Tcl_DStringFree(&dsPtr);
}
/*
@@ -1508,7 +1464,7 @@ AppendUnicodeToUtfRep(
*
* This function converts the contents of "bytes" to Unicode and appends
* the Unicode to the Unicode rep of "objPtr". objPtr must already have a
- * valid Unicode rep. numBytes must be non-negative.
+ * valid Unicode rep.
*
* Results:
* None.
@@ -1523,18 +1479,27 @@ static void
AppendUtfToUnicodeRep(
Tcl_Obj *objPtr, /* Points to the object to append to. */
const char *bytes, /* String to convert to Unicode. */
- size_t numBytes) /* Number of bytes of "bytes" to convert. */
+ int numBytes) /* Number of bytes of "bytes" to convert. */
{
- String *stringPtr;
+ Tcl_DString dsPtr;
+ int numChars = numBytes;
+ Tcl_UniChar *unicode = NULL;
+ if (numBytes < 0) {
+ numBytes = (bytes ? strlen(bytes) : 0);
+ }
if (numBytes == 0) {
return;
}
- ExtendUnicodeRepWithString(objPtr, bytes, numBytes, TCL_STRLEN);
- TclInvalidateStringRep(objPtr);
- stringPtr = GET_STRING(objPtr);
- stringPtr->allocated = 0;
+ Tcl_DStringInit(&dsPtr);
+ if (bytes) {
+ numChars = Tcl_NumUtfChars(bytes, numBytes);
+ unicode = (Tcl_UniChar *) Tcl_UtfToUniCharDString(bytes, numBytes,
+ &dsPtr);
+ }
+ AppendUnicodeToUnicodeRep(objPtr, unicode, numChars);
+ Tcl_DStringFree(&dsPtr);
}
/*
@@ -1544,7 +1509,6 @@ AppendUtfToUnicodeRep(
*
* This function appends "numBytes" bytes of "bytes" to the UTF string
* rep of "objPtr". objPtr must already have a valid String rep.
- * numBytes must be non-negative.
*
* Results:
* None.
@@ -1559,11 +1523,14 @@ static void
AppendUtfToUtfRep(
Tcl_Obj *objPtr, /* Points to the object to append to. */
const char *bytes, /* String to append. */
- size_t numBytes) /* Number of bytes of "bytes" to append. */
+ int numBytes) /* Number of bytes of "bytes" to append. */
{
String *stringPtr;
- size_t newLength, oldLength;
+ int newLength, oldLength;
+ if (numBytes < 0) {
+ numBytes = (bytes ? strlen(bytes) : 0);
+ }
if (numBytes == 0) {
return;
}
@@ -1573,42 +1540,47 @@ AppendUtfToUtfRep(
* trailing null.
*/
- if (objPtr->bytes == NULL) {
- objPtr->length = 0;
- }
oldLength = objPtr->length;
newLength = numBytes + oldLength;
- if (newLength < oldLength) {
+ if (newLength < 0) {
Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX);
}
stringPtr = GET_STRING(objPtr);
- if (newLength > stringPtr->allocated) {
- ptrdiff_t offset = -1;
-
+ if (newLength > (int) stringPtr->allocated) {
/*
* Protect against case where unicode points into the existing
- * stringPtr->unicode array. Force it to follow any relocations due to
- * the reallocs below.
+ * stringPtr->unicode array. Force it to follow any relocations
+ * due to the reallocs below.
*/
-
- if (bytes >= objPtr->bytes
+ int offset = -1;
+ if (bytes && bytes >= objPtr->bytes
&& bytes <= objPtr->bytes + objPtr->length) {
offset = bytes - objPtr->bytes;
}
/*
- * TODO: consider passing flag=1: no overalloc on first append. This
- * would make test stringObj-8.1 fail.
+ * There isn't currently enough space in the string representation so
+ * allocate additional space. First, try to double the length
+ * required. If that fails, try a more modest allocation. See the "TCL
+ * STRING GROWTH ALGORITHM" comment at the top of this file for an
+ * explanation of this growth algorithm.
*/
- GrowStringBuffer(objPtr, newLength, 0);
+ if (Tcl_AttemptSetObjLength(objPtr, 2 * newLength) == 0) {
+ /*
+ * Take care computing the amount of modest growth to avoid
+ * overflow into invalid argument values for Tcl_SetObjLength.
+ */
+ unsigned int limit = INT_MAX - newLength;
+ unsigned int extra = numBytes + TCL_GROWTH_MIN_ALLOC;
+ int growth = (int) ((extra > limit) ? limit : extra);
- /*
- * Relocate bytes if needed; see above.
- */
+ Tcl_SetObjLength(objPtr, newLength + growth);
+ }
- if (offset >= 0) {
+ /* Relocate bytes if needed; see above. */
+ if (offset >=0) {
bytes = objPtr->bytes + offset;
}
}
@@ -1617,10 +1589,12 @@ AppendUtfToUtfRep(
* Invalidate the unicode data.
*/
- stringPtr->numChars = TCL_STRLEN;
+ stringPtr->numChars = -1;
stringPtr->hasUnicode = 0;
- memmove(objPtr->bytes + oldLength, bytes, numBytes);
+ if (bytes) {
+ memcpy(objPtr->bytes + oldLength, bytes, (size_t) numBytes);
+ }
objPtr->bytes[newLength] = 0;
objPtr->length = newLength;
}
@@ -1648,18 +1622,130 @@ Tcl_AppendStringsToObjVA(
Tcl_Obj *objPtr, /* Points to the object to append to. */
va_list argList) /* Variable argument list. */
{
+#define STATIC_LIST_SIZE 16
+ String *stringPtr;
+ int newLength, oldLength, attemptLength;
+ register char *string, *dst;
+ char *static_list[STATIC_LIST_SIZE];
+ char **args = static_list;
+ int nargs_space = STATIC_LIST_SIZE;
+ int nargs, i;
+
if (Tcl_IsShared(objPtr)) {
Tcl_Panic("%s called with shared object", "Tcl_AppendStringsToObj");
}
+ SetStringFromAny(NULL, objPtr);
+
+ /*
+ * Force the existence of a string rep. so we avoid crashes operating
+ * on a pure unicode value. [Bug 2597185]
+ */
+
+ (void) Tcl_GetStringFromObj(objPtr, &oldLength);
+
+ /*
+ * Figure out how much space is needed for all the strings, and expand the
+ * string representation if it isn't big enough. If no bytes would be
+ * appended, just return. Note that on some platforms (notably OS/390) the
+ * argList is an array so we need to use memcpy.
+ */
+
+ nargs = 0;
+ newLength = 0;
while (1) {
- const char *bytes = va_arg(argList, char *);
+ string = va_arg(argList, char *);
+ if (string == NULL) {
+ break;
+ }
+ if (nargs >= nargs_space) {
+ /*
+ * Expand the args buffer.
+ */
- if (bytes == NULL) {
+ nargs_space += STATIC_LIST_SIZE;
+ if (args == static_list) {
+ args = (void *) ckalloc(nargs_space * sizeof(char *));
+ for (i = 0; i < nargs; ++i) {
+ args[i] = static_list[i];
+ }
+ } else {
+ args = (void *) ckrealloc((void *) args,
+ nargs_space * sizeof(char *));
+ }
+ }
+ newLength += strlen(string);
+ args[nargs++] = string;
+ }
+ if (newLength == 0) {
+ goto done;
+ }
+
+ stringPtr = GET_STRING(objPtr);
+ if (oldLength + newLength > (int) stringPtr->allocated) {
+ /*
+ * There isn't currently enough space in the string representation, so
+ * allocate additional space. If the current string representation
+ * isn't empty (i.e. it looks like we're doing a series of appends)
+ * then try to allocate extra space to accomodate future growth: first
+ * try to double the required memory; if that fails, try a more modest
+ * allocation. See the "TCL STRING GROWTH ALGORITHM" comment at the
+ * top of this file for an explanation of this growth algorithm.
+ * Otherwise, if the current string representation is empty, exactly
+ * enough memory is allocated.
+ */
+
+ if (oldLength == 0) {
+ Tcl_SetObjLength(objPtr, newLength);
+ } else {
+ attemptLength = 2 * (oldLength + newLength);
+ if (Tcl_AttemptSetObjLength(objPtr, attemptLength) == 0) {
+ attemptLength = oldLength + (2 * newLength) +
+ TCL_GROWTH_MIN_ALLOC;
+ Tcl_SetObjLength(objPtr, attemptLength);
+ }
+ }
+ }
+
+ /*
+ * Make a second pass through the arguments, appending all the strings to
+ * the object.
+ */
+
+ dst = objPtr->bytes + oldLength;
+ for (i = 0; i < nargs; ++i) {
+ string = args[i];
+ if (string == NULL) {
break;
}
- Tcl_AppendToObj(objPtr, bytes, TCL_STRLEN);
+ while (*string != 0) {
+ *dst = *string;
+ dst++;
+ string++;
+ }
+ }
+
+ /*
+ * Add a null byte to terminate the string. However, be careful: it's
+ * possible that the object is totally empty (if it was empty originally
+ * and there was nothing to append). In this case dst is NULL; just leave
+ * everything alone.
+ */
+
+ if (dst != NULL) {
+ *dst = 0;
}
+ objPtr->length = oldLength + newLength;
+
+ done:
+ /*
+ * If we had to allocate a buffer from the heap, free it now.
+ */
+
+ if (args != static_list) {
+ ckfree((void *) args);
+ }
+#undef STATIC_LIST_SIZE
}
/*
@@ -1717,16 +1803,15 @@ Tcl_AppendFormatToObj(
Tcl_Interp *interp,
Tcl_Obj *appendObj,
const char *format,
- size_t objc,
+ int objc,
Tcl_Obj *const objv[])
{
- const char *span = format, *msg, *errCode;
- size_t numBytes = 0, objIndex = 0;
- int gotXpg = 0, gotSequential = 0;
- size_t originalLength, limit;
+ const char *span = format, *msg;
+ int numBytes = 0, objIndex = 0, gotXpg = 0, gotSequential = 0;
+ int originalLength, limit;
static const char *mixedXPG =
"cannot mix \"%\" and \"%n$\" conversion specifiers";
- static const char *const badIndex[2] = {
+ static const char *badIndex[2] = {
"not enough arguments for all format specifiers",
"\"%n$\" argument index out of range"
};
@@ -1746,8 +1831,7 @@ Tcl_AppendFormatToObj(
char *end;
int gotMinus, gotHash, gotZero, gotSpace, gotPlus, sawFlag;
int width, gotPrecision, precision, useShort, useWide, useBig;
- int newXpg, allocSegment = 0, segmentLimit;
- size_t numChars, segmentNumBytes;
+ int newXpg, numChars, allocSegment = 0, segmentLimit, segmentNumBytes;
Tcl_Obj *segment;
Tcl_UniChar ch;
int step = Tcl_UtfToUniChar(format, &ch);
@@ -1760,7 +1844,6 @@ Tcl_AppendFormatToObj(
if (numBytes) {
if (numBytes > limit) {
msg = overflow;
- errCode = "OVERFLOW";
goto errorMsg;
}
Tcl_AppendToObj(appendObj, span, numBytes);
@@ -1789,12 +1872,8 @@ Tcl_AppendFormatToObj(
newXpg = 0;
if (isdigit(UCHAR(ch))) {
int position = strtoul(format, &end, 10);
-
if (*end == '$') {
newXpg = 1;
- if (position == 0) {
- goto badXpgIndex;
- }
objIndex = position - 1;
format = end + 1;
step = Tcl_UtfToUniChar(format, &ch);
@@ -1803,22 +1882,18 @@ Tcl_AppendFormatToObj(
if (newXpg) {
if (gotSequential) {
msg = mixedXPG;
- errCode = "MIXEDSPECTYPES";
goto errorMsg;
}
gotXpg = 1;
} else {
if (gotXpg) {
msg = mixedXPG;
- errCode = "MIXEDSPECTYPES";
goto errorMsg;
}
gotSequential = 1;
}
- if (objIndex >= objc) {
- badXpgIndex:
+ if ((objIndex < 0) || (objIndex >= objc)) {
msg = badIndex[gotXpg];
- errCode = gotXpg ? "INDEXRANGE" : "FIELDVARMISMATCH";
goto errorMsg;
}
@@ -1866,7 +1941,6 @@ Tcl_AppendFormatToObj(
} else if (ch == '*') {
if (objIndex >= objc - 1) {
msg = badIndex[gotXpg];
- errCode = gotXpg ? "INDEXRANGE" : "FIELDVARMISMATCH";
goto errorMsg;
}
if (TclGetIntFromObj(interp, objv[objIndex], &width) != TCL_OK) {
@@ -1882,7 +1956,6 @@ Tcl_AppendFormatToObj(
}
if (width > limit) {
msg = overflow;
- errCode = "OVERFLOW";
goto errorMsg;
}
@@ -1903,7 +1976,6 @@ Tcl_AppendFormatToObj(
} else if (ch == '*') {
if (objIndex >= objc - 1) {
msg = badIndex[gotXpg];
- errCode = gotXpg ? "INDEXRANGE" : "FIELDVARMISMATCH";
goto errorMsg;
}
if (TclGetIntFromObj(interp, objv[objIndex], &precision)
@@ -1939,8 +2011,8 @@ Tcl_AppendFormatToObj(
useBig = 1;
format += step;
step = Tcl_UtfToUniChar(format, &ch);
-#ifndef TCL_WIDE_INT_IS_LONG
} else {
+#ifndef TCL_WIDE_INT_IS_LONG
useWide = 1;
#endif
}
@@ -1954,14 +2026,13 @@ Tcl_AppendFormatToObj(
*/
segment = objv[objIndex];
- numChars = TCL_STRLEN;
+ numChars = -1;
if (ch == 'i') {
ch = 'd';
}
switch (ch) {
case '\0':
msg = "format string ended in middle of field specifier";
- errCode = "INCOMPLETE";
goto errorMsg;
case 's':
if (gotPrecision) {
@@ -1991,15 +2062,13 @@ Tcl_AppendFormatToObj(
case 'u':
if (useBig) {
msg = "unsigned bignum format is invalid";
- errCode = "BADUNSIGNED";
goto errorMsg;
}
case 'd':
case 'o':
case 'x':
- case 'X':
- case 'b': {
- short s = 0; /* Silence compiler warning; only defined and
+ case 'X': {
+ short int s = 0; /* Silence compiler warning; only defined and
* used when useShort is true. */
long l;
Tcl_WideInt w;
@@ -2024,7 +2093,7 @@ Tcl_AppendFormatToObj(
Tcl_GetWideIntFromObj(NULL, objPtr, &w);
Tcl_DecrRefCount(objPtr);
}
- isNegative = (w < (Tcl_WideInt) 0);
+ isNegative = (w < (Tcl_WideInt)0);
} else if (TclGetLongFromObj(NULL, segment, &l) != TCL_OK) {
if (Tcl_GetWideIntFromObj(NULL, segment, &w) != TCL_OK) {
Tcl_Obj *objPtr;
@@ -2041,16 +2110,16 @@ Tcl_AppendFormatToObj(
l = Tcl_WideAsLong(w);
}
if (useShort) {
- s = (short) l;
- isNegative = (s < (short) 0);
+ s = (short int) l;
+ isNegative = (s < (short int)0);
} else {
- isNegative = (l < (long) 0);
+ isNegative = (l < (long)0);
}
} else if (useShort) {
- s = (short) l;
- isNegative = (s < (short) 0);
+ s = (short int) l;
+ isNegative = (s < (short int)0);
} else {
- isNegative = (l < (long) 0);
+ isNegative = (l < (long)0);
}
segment = Tcl_NewObj();
@@ -2058,9 +2127,8 @@ Tcl_AppendFormatToObj(
segmentLimit = INT_MAX;
Tcl_IncrRefCount(segment);
- if ((isNegative || gotPlus || gotSpace) && (useBig || ch=='d')) {
- Tcl_AppendToObj(segment,
- (isNegative ? "-" : gotPlus ? "+" : " "), 1);
+ if ((isNegative || gotPlus || gotSpace) && (useBig || (ch == 'd'))) {
+ Tcl_AppendToObj(segment, (isNegative ? "-" : gotPlus ? "+" : " "), 1);
segmentLimit -= 1;
}
@@ -2076,21 +2144,17 @@ Tcl_AppendFormatToObj(
Tcl_AppendToObj(segment, "0x", 2);
segmentLimit -= 2;
break;
- case 'b':
- Tcl_AppendToObj(segment, "0b", 2);
- segmentLimit -= 2;
- break;
}
}
switch (ch) {
case 'd': {
- size_t length;
+ int length;
Tcl_Obj *pure;
const char *bytes;
if (useShort) {
- pure = Tcl_NewIntObj((int) s);
+ pure = Tcl_NewIntObj((int)(s));
} else if (useWide) {
pure = Tcl_NewWideIntObj(w);
} else if (useBig) {
@@ -2119,7 +2183,7 @@ Tcl_AppendFormatToObj(
if (gotPrecision) {
if (length < precision) {
- segmentLimit -= precision - length;
+ segmentLimit -= (precision - length);
}
while (length < precision) {
Tcl_AppendToObj(segment, "0", 1);
@@ -2130,7 +2194,7 @@ Tcl_AppendFormatToObj(
if (gotZero) {
length += Tcl_GetCharLength(segment);
if (length < width) {
- segmentLimit -= width - length;
+ segmentLimit -= (width - length);
}
while (length < width) {
Tcl_AppendToObj(segment, "0", 1);
@@ -2139,7 +2203,6 @@ Tcl_AppendFormatToObj(
}
if (toAppend > segmentLimit) {
msg = overflow;
- errCode = "OVERFLOW";
goto errorMsg;
}
Tcl_AppendToObj(segment, bytes, toAppend);
@@ -2150,25 +2213,23 @@ Tcl_AppendFormatToObj(
case 'u':
case 'o':
case 'x':
- case 'X':
- case 'b': {
- Tcl_WideUInt bits = (Tcl_WideUInt) 0;
- Tcl_WideInt numDigits = (Tcl_WideInt) 0;
- int length, numBits = 4, base = 16, index = 0, shift = 0;
+ case 'X': {
+ Tcl_WideUInt bits = (Tcl_WideUInt)0;
+ Tcl_WideInt numDigits = (Tcl_WideInt)0;
+ int length, numBits = 4, base = 16;
+ int index = 0, shift = 0;
Tcl_Obj *pure;
char *bytes;
if (ch == 'u') {
base = 10;
- } else if (ch == 'o') {
+ }
+ if (ch == 'o') {
base = 8;
numBits = 3;
- } else if (ch == 'b') {
- base = 2;
- numBits = 1;
}
if (useShort) {
- unsigned short us = (unsigned short) s;
+ unsigned short int us = (unsigned short int) s;
bits = (Tcl_WideUInt) us;
while (us) {
@@ -2188,18 +2249,17 @@ Tcl_AppendFormatToObj(
mp_digit mask = (~(mp_digit)0) << (DIGIT_BIT-leftover);
numDigits = 1 +
- (((Tcl_WideInt) big.used * DIGIT_BIT) / numBits);
+ (((Tcl_WideInt)big.used * DIGIT_BIT) / numBits);
while ((mask & big.dp[big.used-1]) == 0) {
numDigits--;
mask >>= numBits;
}
if (numDigits > INT_MAX) {
msg = overflow;
- errCode = "OVERFLOW";
goto errorMsg;
}
} else if (!useBig) {
- unsigned long ul = (unsigned long) l;
+ unsigned long int ul = (unsigned long int) l;
bits = (Tcl_WideUInt) ul;
while (ul) {
@@ -2216,16 +2276,16 @@ Tcl_AppendFormatToObj(
numDigits = 1;
}
pure = Tcl_NewObj();
- Tcl_SetObjLength(pure, (int) numDigits);
+ Tcl_SetObjLength(pure, (int)numDigits);
bytes = TclGetString(pure);
- toAppend = length = (int) numDigits;
+ toAppend = length = (int)numDigits;
while (numDigits--) {
int digitOffset;
if (useBig && big.used) {
if (index < big.used && (size_t) shift <
CHAR_BIT*sizeof(Tcl_WideUInt) - DIGIT_BIT) {
- bits |= ((Tcl_WideUInt) big.dp[index++]) << shift;
+ bits |= (((Tcl_WideUInt)big.dp[index++]) <<shift);
shift += DIGIT_BIT;
}
shift -= numBits;
@@ -2243,7 +2303,7 @@ Tcl_AppendFormatToObj(
}
if (gotPrecision) {
if (length < precision) {
- segmentLimit -= precision - length;
+ segmentLimit -= (precision - length);
}
while (length < precision) {
Tcl_AppendToObj(segment, "0", 1);
@@ -2254,7 +2314,7 @@ Tcl_AppendFormatToObj(
if (gotZero) {
length += Tcl_GetCharLength(segment);
if (length < width) {
- segmentLimit -= width - length;
+ segmentLimit -= (width - length);
}
while (length < width) {
Tcl_AppendToObj(segment, "0", 1);
@@ -2263,7 +2323,6 @@ Tcl_AppendFormatToObj(
}
if (toAppend > segmentLimit) {
msg = overflow;
- errCode = "OVERFLOW";
goto errorMsg;
}
Tcl_AppendObjToObj(segment, pure);
@@ -2310,14 +2369,13 @@ Tcl_AppendFormatToObj(
p += sprintf(p, "%d", width);
if (width > length) {
length = width;
- }
+ }
}
if (gotPrecision) {
*p++ = '.';
p += sprintf(p, "%d", precision);
if (precision > INT_MAX - length) {
- msg = overflow;
- errCode = "OVERFLOW";
+ msg=overflow;
goto errorMsg;
}
length += precision;
@@ -2334,13 +2392,11 @@ Tcl_AppendFormatToObj(
allocSegment = 1;
if (!Tcl_AttemptSetObjLength(segment, length)) {
msg = overflow;
- errCode = "OVERFLOW";
goto errorMsg;
}
bytes = TclGetString(segment);
if (!Tcl_AttemptSetObjLength(segment, sprintf(bytes, spec, d))) {
msg = overflow;
- errCode = "OVERFLOW";
goto errorMsg;
}
break;
@@ -2349,7 +2405,6 @@ Tcl_AppendFormatToObj(
if (interp != NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("bad field specifier \"%c\"", ch));
- Tcl_SetErrorCode(interp, "TCL", "FORMAT", "BADTYPE", NULL);
}
goto error;
}
@@ -2362,16 +2417,18 @@ Tcl_AppendFormatToObj(
}
}
- if (width>0 && numChars==TCL_STRLEN) {
- numChars = Tcl_GetCharLength(segment);
- }
- if (!gotMinus && width>0) {
- if (numChars < width) {
- limit -= width - numChars;
+ if (width > 0) {
+ if (numChars < 0) {
+ numChars = Tcl_GetCharLength(segment);
}
- while (numChars < width) {
- Tcl_AppendToObj(appendObj, (gotZero ? "0" : " "), 1);
- numChars++;
+ if (!gotMinus) {
+ if (numChars < width) {
+ limit -= (width - numChars);
+ }
+ while (numChars < width) {
+ Tcl_AppendToObj(appendObj, (gotZero ? "0" : " "), 1);
+ numChars++;
+ }
}
}
@@ -2381,7 +2438,6 @@ Tcl_AppendFormatToObj(
Tcl_DecrRefCount(segment);
}
msg = overflow;
- errCode = "OVERFLOW";
goto errorMsg;
}
Tcl_AppendObjToObj(appendObj, segment);
@@ -2391,7 +2447,7 @@ Tcl_AppendFormatToObj(
}
if (width > 0) {
if (numChars < width) {
- limit -= width-numChars;
+ limit -= (width - numChars);
}
while (numChars < width) {
Tcl_AppendToObj(appendObj, (gotZero ? "0" : " "), 1);
@@ -2404,7 +2460,6 @@ Tcl_AppendFormatToObj(
if (numBytes) {
if (numBytes > limit) {
msg = overflow;
- errCode = "OVERFLOW";
goto errorMsg;
}
Tcl_AppendToObj(appendObj, span, numBytes);
@@ -2416,8 +2471,7 @@ Tcl_AppendFormatToObj(
errorMsg:
if (interp != NULL) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, TCL_STRLEN));
- Tcl_SetErrorCode(interp, "TCL", "FORMAT", errCode, NULL);
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, -1));
}
error:
Tcl_SetObjLength(appendObj, originalLength);
@@ -2433,7 +2487,7 @@ Tcl_AppendFormatToObj(
* A refcount zero Tcl_Obj.
*
* Side effects:
- * None.
+ * None.
*
*---------------------------------------------------------------------------
*/
@@ -2442,12 +2496,11 @@ Tcl_Obj *
Tcl_Format(
Tcl_Interp *interp,
const char *format,
- size_t objc,
+ int objc,
Tcl_Obj *const objv[])
{
int result;
Tcl_Obj *objPtr = Tcl_NewObj();
-
result = Tcl_AppendFormatToObj(interp, objPtr, format, objc, objv);
if (result != TCL_OK) {
Tcl_DecrRefCount(objPtr);
@@ -2474,10 +2527,10 @@ AppendPrintfToObjVA(
const char *format,
va_list argList)
{
- int code;
- size_t objc;
+ int code, objc;
Tcl_Obj **objv, *list = Tcl_NewObj();
const char *p;
+ char *end;
p = format;
Tcl_IncrRefCount(list);
@@ -2494,6 +2547,7 @@ AppendPrintfToObjVA(
}
do {
switch (*p) {
+
case '\0':
seekingConversion = 0;
break;
@@ -2530,7 +2584,7 @@ AppendPrintfToObjVA(
}
Tcl_ListObjAppendElement(NULL, list,
- Tcl_NewStringObj(bytes, (size_t)(end - bytes)));
+ Tcl_NewStringObj(bytes , (int)(end - bytes)));
break;
}
@@ -2546,11 +2600,11 @@ AppendPrintfToObjVA(
case -1:
case 0:
Tcl_ListObjAppendElement(NULL, list, Tcl_NewLongObj(
- (long) va_arg(argList, int)));
+ (long int)va_arg(argList, int)));
break;
case 1:
Tcl_ListObjAppendElement(NULL, list, Tcl_NewLongObj(
- va_arg(argList, long)));
+ va_arg(argList, long int)));
break;
}
break;
@@ -2564,18 +2618,15 @@ AppendPrintfToObjVA(
seekingConversion = 0;
break;
case '*':
- lastNum = (int) va_arg(argList, int);
+ lastNum = (int)va_arg(argList, int);
Tcl_ListObjAppendElement(NULL, list, Tcl_NewIntObj(lastNum));
p++;
break;
case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9': {
- char *end;
-
+ case '5': case '6': case '7': case '8': case '9':
lastNum = (int) strtoul(p, &end, 10);
p = end;
break;
- }
case '.':
gotPrecision = 1;
p++;
@@ -2611,7 +2662,7 @@ AppendPrintfToObjVA(
* A standard Tcl result.
*
* Side effects:
- * None.
+ * None.
*
*---------------------------------------------------------------------------
*/
@@ -2638,7 +2689,7 @@ Tcl_AppendPrintfToObj(
* A refcount zero Tcl_Obj.
*
* Side effects:
- * None.
+ * None.
*
*---------------------------------------------------------------------------
*/
@@ -2660,139 +2711,115 @@ Tcl_ObjPrintf(
/*
*---------------------------------------------------------------------------
*
- * TclStringObjReverse --
+ * TclGetStringStorage --
*
- * Implements the [string reverse] operation.
+ * Returns the string storage space of a Tcl_Obj.
*
* Results:
- * An unshared Tcl value which is the [string reverse] of the argument
- * supplied. When sharing rules permit, the returned value might be the
- * argument with modifications done in place.
+ * The pointer value objPtr->bytes is returned and the number of bytes
+ * allocated there is written to *sizePtr (if known).
*
* Side effects:
- * May allocate a new Tcl_Obj.
+ * May set objPtr->bytes.
*
*---------------------------------------------------------------------------
*/
-static void
-ReverseBytes(
- unsigned char *to, /* Copy bytes into here... */
- unsigned char *from, /* ...from here... */
- size_t count) /* Until this many are copied, */
- /* reversing as you go. */
+char *
+TclGetStringStorage(
+ Tcl_Obj *objPtr,
+ unsigned int *sizePtr)
{
- unsigned char *src = from + count;
- if (to == from) {
- /* Reversing in place */
- while (--src > to) {
- unsigned char c = *src;
- *src = *to;
- *to++ = c;
- }
- } else {
- while (--src >= from) {
- *to++ = *src;
- }
+ String *stringPtr;
+
+ if (objPtr->typePtr != &tclStringType || objPtr->bytes == NULL) {
+ return TclGetStringFromObj(objPtr, (int *)sizePtr);
}
+
+ stringPtr = GET_STRING(objPtr);
+ *sizePtr = stringPtr->allocated;
+ return objPtr->bytes;
}
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TclStringObjReverse --
+ *
+ * Implements the [string reverse] operation.
+ *
+ * Results:
+ * An unshared Tcl value which is the [string reverse] of the argument
+ * supplied. When sharing rules permit, the returned value might be
+ * the argument with modifications done in place.
+ *
+ * Side effects:
+ * May allocate a new Tcl_Obj.
+ *
+ *---------------------------------------------------------------------------
+ */
Tcl_Obj *
TclStringObjReverse(
Tcl_Obj *objPtr)
{
String *stringPtr;
- Tcl_UniChar ch;
-
- if (TclIsPureByteArray(objPtr)) {
- size_t numBytes;
- unsigned char *from = Tcl_GetByteArrayFromObj(objPtr, &numBytes);
+ int numChars = Tcl_GetCharLength(objPtr);
+ int i = 0, lastCharIdx = numChars - 1;
+ char *bytes;
- if (Tcl_IsShared(objPtr)) {
- objPtr = Tcl_NewByteArrayObj(NULL, numBytes);
- }
- ReverseBytes(Tcl_GetByteArrayFromObj(objPtr, NULL), from, numBytes);
+ if (numChars <= 1) {
return objPtr;
}
- SetStringFromAny(NULL, objPtr);
stringPtr = GET_STRING(objPtr);
-
if (stringPtr->hasUnicode) {
- Tcl_UniChar *from = Tcl_GetUnicode(objPtr);
- Tcl_UniChar *src = from + stringPtr->numChars;
+ Tcl_UniChar *source = stringPtr->unicode;
if (Tcl_IsShared(objPtr)) {
- Tcl_UniChar *to;
+ Tcl_UniChar *dest, ch = 0;
/*
* Create a non-empty, pure unicode value, so we can coax
* Tcl_SetObjLength into growing the unicode rep buffer.
*/
- ch = 0;
- objPtr = Tcl_NewUnicodeObj(&ch, 1);
- Tcl_SetObjLength(objPtr, stringPtr->numChars);
- to = Tcl_GetUnicode(objPtr);
- while (--src >= from) {
- *to++ = *src;
- }
- } else {
- /* Reversing in place */
- while (--src > from) {
- ch = *src;
- *src = *from;
- *from++ = ch;
+ Tcl_Obj *resultPtr = Tcl_NewUnicodeObj(&ch, 1);
+ Tcl_SetObjLength(resultPtr, numChars);
+ dest = Tcl_GetUnicode(resultPtr);
+
+ while (i < numChars) {
+ dest[i++] = source[lastCharIdx--];
}
+ return resultPtr;
}
- }
-
- if (objPtr->bytes) {
- int numChars = stringPtr->numChars;
- int numBytes = objPtr->length;
- char *to, *from = objPtr->bytes;
- if (Tcl_IsShared(objPtr)) {
- objPtr = Tcl_NewObj();
- Tcl_SetObjLength(objPtr, numBytes);
+ while (i < lastCharIdx) {
+ Tcl_UniChar tmp = source[lastCharIdx];
+ source[lastCharIdx--] = source[i];
+ source[i++] = tmp;
}
- to = objPtr->bytes;
-
- if (numChars == TCL_STRLEN || numChars < numBytes) {
- /*
- * Either numChars == TCL_STRLEN and we don't know how many chars
- * are represented by objPtr->bytes and we need Pass 1 just in
- * case, or numChars >= 0 and we know we have fewer chars than
- * bytes, so we know there's a multibyte character needing Pass 1.
- *
- * Pass 1. Reverse the bytes of each multi-byte character.
- */
- int charCount = 0;
- int bytesLeft = numBytes;
-
- while (bytesLeft) {
- /*
- * NOTE: We know that the from buffer is NUL-terminated.
- * It's part of the contract for objPtr->bytes values.
- * Thus, we can skip calling Tcl_UtfCharComplete() here.
- */
- int bytesInChar = Tcl_UtfToUniChar(from, &ch);
-
- ReverseBytes((unsigned char *)to, (unsigned char *)from,
- bytesInChar);
- to += bytesInChar;
- from += bytesInChar;
- bytesLeft -= bytesInChar;
- charCount++;
- }
+ TclInvalidateStringRep(objPtr);
+ stringPtr->allocated = 0;
+ return objPtr;
+ }
- from = to = objPtr->bytes;
- stringPtr->numChars = charCount;
+ bytes = TclGetString(objPtr);
+ if (Tcl_IsShared(objPtr)) {
+ char *dest;
+ Tcl_Obj *resultPtr = Tcl_NewObj();
+ Tcl_SetObjLength(resultPtr, numChars);
+ dest = TclGetString(resultPtr);
+ while (i < numChars) {
+ dest[i++] = bytes[lastCharIdx--];
}
- /* Pass 2. Reverse all the bytes. */
- ReverseBytes((unsigned char *)to, (unsigned char *)from, numBytes);
+ return resultPtr;
}
+ while (i < lastCharIdx) {
+ char tmp = bytes[lastCharIdx];
+ bytes[lastCharIdx--] = bytes[i];
+ bytes[i++] = tmp;
+ }
return objPtr;
}
@@ -2818,43 +2845,35 @@ FillUnicodeRep(
Tcl_Obj *objPtr) /* The object in which to fill the unicode
* rep. */
{
- String *stringPtr = GET_STRING(objPtr);
-
- ExtendUnicodeRepWithString(objPtr, objPtr->bytes, objPtr->length,
- stringPtr->numChars);
-}
-
-static void
-ExtendUnicodeRepWithString(
- Tcl_Obj *objPtr,
- const char *bytes,
- size_t numBytes,
- size_t numAppendChars)
-{
- String *stringPtr = GET_STRING(objPtr);
- int needed, numOrigChars = 0;
+ String *stringPtr;
+ size_t uallocated;
+ char *srcEnd, *src = objPtr->bytes;
Tcl_UniChar *dst;
- if (stringPtr->hasUnicode) {
- numOrigChars = stringPtr->numChars;
- }
- if (numAppendChars == TCL_STRLEN) {
- TclNumUtfChars(numAppendChars, bytes, numBytes);
+ stringPtr = GET_STRING(objPtr);
+ if (stringPtr->numChars == -1) {
+ stringPtr->numChars = Tcl_NumUtfChars(src, objPtr->length);
}
- needed = numOrigChars + numAppendChars;
- stringCheckLimits(needed);
-
- if (needed > stringPtr->maxChars) {
- GrowUnicodeBuffer(objPtr, needed);
+ stringPtr->hasUnicode = (stringPtr->numChars > 0);
+
+ stringCheckLimits(stringPtr->numChars);
+ uallocated = STRING_UALLOC(stringPtr->numChars);
+ if (uallocated > stringPtr->uallocated) {
+ GrowUnicodeBuffer(objPtr, stringPtr->numChars);
stringPtr = GET_STRING(objPtr);
}
- stringPtr->hasUnicode = 1;
- stringPtr->numChars = needed;
- for (dst=stringPtr->unicode + numOrigChars; numAppendChars-- > 0; dst++) {
- bytes += TclUtfToUniChar(bytes, dst);
+ /*
+ * Convert src to Unicode and store the coverted data in "unicode".
+ */
+
+ srcEnd = src + objPtr->length;
+ for (dst = stringPtr->unicode; src < srcEnd; dst++) {
+ src += TclUtfToUniChar(src, dst);
}
*dst = 0;
+
+ SET_STRING(objPtr, stringPtr);
}
/*
@@ -2877,58 +2896,14 @@ ExtendUnicodeRepWithString(
static void
DupStringInternalRep(
- Tcl_Obj *srcPtr, /* Object with internal rep to copy. Must have
+ register Tcl_Obj *srcPtr, /* Object with internal rep to copy. Must have
* an internal rep of type "String". */
- Tcl_Obj *copyPtr) /* Object with internal rep to set. Must not
+ register Tcl_Obj *copyPtr) /* Object with internal rep to set. Must not
* currently have an internal rep.*/
{
String *srcStringPtr = GET_STRING(srcPtr);
String *copyStringPtr = NULL;
-#if COMPAT==0
- if (srcStringPtr->numChars == TCL_STRLEN) {
- /*
- * The String struct in the source value holds zero useful data. Don't
- * bother copying it. Don't even bother allocating space in which to
- * copy it. Just let the copy be untyped.
- */
-
- return;
- }
-
- if (srcStringPtr->hasUnicode) {
- int copyMaxChars;
-
- if (srcStringPtr->maxChars / 2 >= srcStringPtr->numChars) {
- copyMaxChars = 2 * srcStringPtr->numChars;
- } else {
- copyMaxChars = srcStringPtr->maxChars;
- }
- copyStringPtr = stringAttemptAlloc(copyMaxChars);
- if (copyStringPtr == NULL) {
- copyMaxChars = srcStringPtr->numChars;
- copyStringPtr = stringAlloc(copyMaxChars);
- }
- copyStringPtr->maxChars = copyMaxChars;
- memcpy(copyStringPtr->unicode, srcStringPtr->unicode,
- srcStringPtr->numChars * sizeof(Tcl_UniChar));
- copyStringPtr->unicode[srcStringPtr->numChars] = 0;
- } else {
- copyStringPtr = stringAlloc(0);
- copyStringPtr->maxChars = 0;
- copyStringPtr->unicode[0] = 0;
- }
- copyStringPtr->hasUnicode = srcStringPtr->hasUnicode;
- copyStringPtr->numChars = srcStringPtr->numChars;
-
- /*
- * Tricky point: the string value was copied by generic object management
- * code, so it doesn't contain any extra bytes that might exist in the
- * source object.
- */
-
- copyStringPtr->allocated = copyPtr->bytes ? copyPtr->length : 0;
-#else /* COMPAT!=0 */
/*
* If the src obj is a string of 1-byte Utf chars, then copy the string
* rep of the source object and create an "empty" Unicode internal rep for
@@ -2936,33 +2911,29 @@ DupStringInternalRep(
* the string rep of the new object.
*/
- if (srcStringPtr->hasUnicode && srcStringPtr->numChars > 0) {
- /*
- * Copy the full allocation for the Unicode buffer.
- */
+ if (srcStringPtr->hasUnicode == 0) {
+ copyStringPtr = (String *) ckalloc(sizeof(String));
+ copyStringPtr->uallocated = 0;
+ } else {
+ copyStringPtr = (String *) ckalloc(
+ STRING_SIZE(srcStringPtr->uallocated));
+ copyStringPtr->uallocated = srcStringPtr->uallocated;
- copyStringPtr = stringAlloc(srcStringPtr->maxChars);
- copyStringPtr->maxChars = srcStringPtr->maxChars;
memcpy(copyStringPtr->unicode, srcStringPtr->unicode,
- srcStringPtr->numChars * sizeof(Tcl_UniChar));
+ (size_t) srcStringPtr->numChars * sizeof(Tcl_UniChar));
copyStringPtr->unicode[srcStringPtr->numChars] = 0;
- copyStringPtr->allocated = 0;
- } else {
- copyStringPtr = stringAlloc(0);
- copyStringPtr->unicode[0] = 0;
- copyStringPtr->maxChars = 0;
-
- /*
- * Tricky point: the string value was copied by generic object
- * management code, so it doesn't contain any extra bytes that might
- * exist in the source object.
- */
-
- copyStringPtr->allocated = copyPtr->length;
}
copyStringPtr->numChars = srcStringPtr->numChars;
copyStringPtr->hasUnicode = srcStringPtr->hasUnicode;
-#endif /* COMPAT==0 */
+ copyStringPtr->allocated = srcStringPtr->allocated;
+
+ /*
+ * Tricky point: the string value was copied by generic object management
+ * code, so it doesn't contain any extra bytes that might exist in the
+ * source object.
+ */
+
+ copyStringPtr->allocated = copyPtr->length;
SET_STRING(copyPtr, copyStringPtr);
copyPtr->typePtr = &tclStringType;
@@ -2988,29 +2959,43 @@ DupStringInternalRep(
static int
SetStringFromAny(
Tcl_Interp *interp, /* Used for error reporting if not NULL. */
- Tcl_Obj *objPtr) /* The object to convert. */
+ register Tcl_Obj *objPtr) /* The object to convert. */
{
- if (objPtr->typePtr != &tclStringType) {
- String *stringPtr = stringAlloc(0);
+ /*
+ * The Unicode object is optimized for the case where each UTF char in a
+ * string is only one byte. In this case, we store the value of numChars,
+ * but we don't copy the bytes to the unicodeObj->unicode.
+ */
- /*
- * Convert whatever we have into an untyped value. Just A String.
- */
+ if (objPtr->typePtr != &tclStringType) {
+ String *stringPtr;
- (void) TclGetString(objPtr);
- TclFreeIntRep(objPtr);
+ if (objPtr->typePtr != NULL) {
+ if (objPtr->bytes == NULL) {
+ objPtr->typePtr->updateStringProc(objPtr);
+ }
+ TclFreeIntRep(objPtr);
+ }
+ objPtr->typePtr = &tclStringType;
/*
- * Create a basic String intrep that just points to the UTF-8 string
- * already in place at objPtr->bytes.
+ * Allocate enough space for the basic String structure.
*/
- stringPtr->numChars = TCL_STRLEN;
- stringPtr->allocated = objPtr->length;
- stringPtr->maxChars = 0;
+ stringPtr = (String *) ckalloc(sizeof(String));
+ stringPtr->numChars = -1;
+ stringPtr->uallocated = 0;
stringPtr->hasUnicode = 0;
+
+ if (objPtr->bytes != NULL) {
+ stringPtr->allocated = objPtr->length;
+ if (objPtr->bytes != tclEmptyStringRep) {
+ objPtr->bytes[objPtr->length] = 0;
+ }
+ } else {
+ objPtr->length = 0;
+ }
SET_STRING(objPtr, stringPtr);
- objPtr->typePtr = &tclStringType;
}
return TCL_OK;
}
@@ -3037,77 +3022,57 @@ static void
UpdateStringOfString(
Tcl_Obj *objPtr) /* Object with string rep to update. */
{
- String *stringPtr = GET_STRING(objPtr);
-
- if (stringPtr->numChars == 0) {
- TclInitStringRep(objPtr, tclEmptyStringRep, 0);
- } else {
- (void) ExtendStringRepWithUnicode(objPtr, stringPtr->unicode,
- stringPtr->numChars);
- }
-}
-
-static int
-ExtendStringRepWithUnicode(
- Tcl_Obj *objPtr,
- const Tcl_UniChar *unicode,
- size_t numChars)
-{
- /*
- * Pre-condition: this is the "string" Tcl_ObjType.
- */
-
- size_t i, origLength, size = 0;
- char *dst, buf[TCL_UTF_MAX];
- String *stringPtr = GET_STRING(objPtr);
+ int i, size;
+ Tcl_UniChar *unicode;
+ char dummy[TCL_UTF_MAX];
+ char *dst;
+ String *stringPtr;
- if (numChars == TCL_STRLEN) {
- numChars = UnicodeLength(unicode);
- }
+ stringPtr = GET_STRING(objPtr);
+ if ((objPtr->bytes == NULL) || (stringPtr->allocated == 0)) {
+ if (stringPtr->numChars <= 0) {
+ /*
+ * If there is no Unicode rep, or the string has 0 chars, then set
+ * the string rep to an empty string.
+ */
- if (numChars == 0) {
- return 0;
- }
+ objPtr->bytes = tclEmptyStringRep;
+ objPtr->length = 0;
+ return;
+ }
- if (objPtr->bytes == NULL) {
- objPtr->length = 0;
- }
- size = origLength = objPtr->length;
-
- /*
- * Quick cheap check in case we have more than enough room.
- */
+ unicode = stringPtr->unicode;
- if (numChars <= (INT_MAX - size)/TCL_UTF_MAX
- && stringPtr->allocated >= size + numChars * TCL_UTF_MAX) {
- goto copyBytes;
- }
+ /*
+ * Translate the Unicode string to UTF. "size" will hold the amount of
+ * space the UTF string needs.
+ */
- for (i = 0; i < numChars; i++) {
- size_t oldSize = size;
+ if (stringPtr->numChars <= INT_MAX/TCL_UTF_MAX
+ && stringPtr->allocated >= stringPtr->numChars * (size_t)TCL_UTF_MAX) {
+ goto copyBytes;
+ }
- size += Tcl_UniCharToUtf((int) unicode[i], buf);
- if (oldSize > size) {
+ size = 0;
+ for (i = 0; i < stringPtr->numChars && size >= 0; i++) {
+ size += Tcl_UniCharToUtf((int) unicode[i], dummy);
+ }
+ if (size < 0) {
Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX);
}
- }
-
- /*
- * Grow space if needed.
- */
- if (size > stringPtr->allocated) {
- GrowStringBuffer(objPtr, size, 1);
- }
+ objPtr->bytes = (char *) ckalloc((unsigned) (size + 1));
+ objPtr->length = size;
+ stringPtr->allocated = size;
- copyBytes:
- dst = objPtr->bytes + origLength;
- for (i = 0; i < numChars; i++) {
- dst += Tcl_UniCharToUtf((int) unicode[i], dst);
+ copyBytes:
+ dst = objPtr->bytes;
+ for (i = 0; i < stringPtr->numChars; i++) {
+ dst += Tcl_UniCharToUtf(unicode[i], dst);
+ }
+ *dst = '\0';
}
- *dst = '\0';
- objPtr->length = dst - objPtr->bytes;
- return numChars;
+ return;
}
/*
@@ -3131,7 +3096,7 @@ static void
FreeStringInternalRep(
Tcl_Obj *objPtr) /* Object with internal rep to free. */
{
- ckfree(GET_STRING(objPtr));
+ ckfree((char *) GET_STRING(objPtr));
objPtr->typePtr = NULL;
}