diff options
Diffstat (limited to 'generic/tclBinary.c')
| -rw-r--r-- | generic/tclBinary.c | 3419 | 
1 files changed, 2428 insertions, 991 deletions
| diff --git a/generic/tclBinary.c b/generic/tclBinary.c index e0facb5..981f174 100644 --- a/generic/tclBinary.c +++ b/generic/tclBinary.c @@ -1,4 +1,4 @@ -/*  +/*   * tclBinary.c --   *   *	This file contains the implementation of the "binary" Tcl built-in @@ -7,15 +7,14 @@   * Copyright (c) 1997 by Sun Microsystems, Inc.   * Copyright (c) 1998-1999 by Scriptics Corporation.   * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclBinary.c,v 1.7 2001/04/04 16:07:20 kennykb Exp $ + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES.   */ -#include <math.h>  #include "tclInt.h" -#include "tclPort.h" +#include "tommath.h" + +#include <math.h>  /*   * The following constants are used by GetFormatSpec to indicate various @@ -26,48 +25,161 @@  #define BINARY_NOCOUNT -2	/* No count was specified in format. */  /* + * The following flags may be ORed together and returned by GetFormatSpec + */ + +#define BINARY_SIGNED 0		/* Field to be read as signed data */ +#define BINARY_UNSIGNED 1	/* Field to be read as unsigned data */ + +/* + * The following defines the maximum number of different (integer) numbers + * placed in the object cache by 'binary scan' before it bails out and + * switches back to Plan A (creating a new object for each value.) + * Theoretically, it would be possible to keep the cache about for the values + * that are already in it, but that makes the code slower in practise when + * overflow happens, and makes little odds the rest of the time (as measured + * on my machine.) It is also slower (on the sample I tried at least) to grow + * the cache to hold all items we might want to put in it; presumably the + * extra cost of managing the memory for the enlarged table outweighs the + * benefit from allocating fewer objects. This is probably because as the + * number of objects increases, the likelihood of reuse of any particular one + * drops, and there is very little gain from larger maximum cache sizes (the + * value below is chosen to allow caching to work in full with conversion of + * bytes.) - DKF + */ + +#define BINARY_SCAN_MAX_CACHE	260 + +/*   * Prototypes for local procedures defined in this file:   */ -static void		DupByteArrayInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr, -			    Tcl_Obj *copyPtr)); -static int		FormatNumber _ANSI_ARGS_((Tcl_Interp *interp, int type, -			    Tcl_Obj *src, unsigned char **cursorPtr)); -static void		FreeByteArrayInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr)); -static int		GetFormatSpec _ANSI_ARGS_((char **formatPtr, -			    char *cmdPtr, int *countPtr)); -static Tcl_Obj *	ScanNumber _ANSI_ARGS_((unsigned char *buffer, int type)); -static int		SetByteArrayFromAny _ANSI_ARGS_((Tcl_Interp *interp, -			    Tcl_Obj *objPtr)); -static void		UpdateStringOfByteArray _ANSI_ARGS_((Tcl_Obj *listPtr)); +static void		DupByteArrayInternalRep(Tcl_Obj *srcPtr, +			    Tcl_Obj *copyPtr); +static int		FormatNumber(Tcl_Interp *interp, int type, +			    Tcl_Obj *src, unsigned char **cursorPtr); +static void		FreeByteArrayInternalRep(Tcl_Obj *objPtr); +static int		GetFormatSpec(const char **formatPtr, char *cmdPtr, +			    int *countPtr, int *flagsPtr); +static Tcl_Obj *	ScanNumber(unsigned char *buffer, int type, +			    int flags, Tcl_HashTable **numberCachePtr); +static int		SetByteArrayFromAny(Tcl_Interp *interp, +			    Tcl_Obj *objPtr); +static void		UpdateStringOfByteArray(Tcl_Obj *listPtr); +static void		DeleteScanNumberCache(Tcl_HashTable *numberCachePtr); +static int		NeedReversing(int format); +static void		CopyNumber(const void *from, void *to, +			    unsigned length, int type); +/* Binary ensemble commands */ +static int		BinaryFormatCmd(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +static int		BinaryScanCmd(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +/* Binary encoding sub-ensemble commands */ +static int		BinaryEncodeHex(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +static int		BinaryDecodeHex(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +static int		BinaryEncode64(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +static int		BinaryDecode64(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +static int		BinaryEncodeUu(ClientData clientData, +			    Tcl_Interp *interp, int objc, +			    Tcl_Obj *const objv[]); +static int		BinaryDecodeUu(ClientData clientData, +			    Tcl_Interp *interp, +			    int objc, Tcl_Obj *const objv[]); +/* + * The following tables are used by the binary encoders + */ + +static const char HexDigits[16] = { +    '0', '1', '2', '3', '4', '5', '6', '7', +    '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' +}; + +static const char UueDigits[65] = { +    '`', '!', '"', '#', '$', '%', '&', '\'', +    '(', ')', '*', '+', ',', '-', '.', '/', +    '0', '1', '2', '3', '4', '5', '6', '7', +    '8', '9', ':', ';', '<', '=', '>', '?', +    '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', +    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', +    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', +    'X', 'Y', 'Z', '[', '\\',']', '^', '_', +    '`' +}; + +static const char B64Digits[65] = { +    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', +    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', +    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', +    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', +    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', +    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', +    'w', 'x', 'y', 'z', '0', '1', '2', '3', +    '4', '5', '6', '7', '8', '9', '+', '/', +    '=' +}; + +/* + * How to construct the ensembles. + */ + +static const EnsembleImplMap binaryMap[] = { +    { "format", BinaryFormatCmd, TclCompileBasicMin1ArgCmd, NULL, NULL, 0 }, +    { "scan",   BinaryScanCmd, TclCompileBasicMin2ArgCmd, NULL, NULL, 0 }, +    { "encode", NULL, NULL, NULL, NULL, 0 }, +    { "decode", NULL, NULL, NULL, NULL, 0 }, +    { NULL, NULL, NULL, NULL, NULL, 0 } +}; +static const EnsembleImplMap encodeMap[] = { +    { "hex",      BinaryEncodeHex, TclCompileBasic1ArgCmd, NULL, NULL, 0 }, +    { "uuencode", BinaryEncodeUu,  NULL, NULL, NULL, 0 }, +    { "base64",   BinaryEncode64,  NULL, NULL, NULL, 0 }, +    { NULL, NULL, NULL, NULL, NULL, 0 } +}; +static const EnsembleImplMap decodeMap[] = { +    { "hex",      BinaryDecodeHex, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 }, +    { "uuencode", BinaryDecodeUu,  TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 }, +    { "base64",   BinaryDecode64,  TclCompileBasic1Or2ArgCmd, NULL, NULL, 0 }, +    { NULL, NULL, NULL, NULL, NULL, 0 } +};  /* - * The following object type represents an array of bytes.  An array of - * bytes is not equivalent to an internationalized string.  Conceptually, a - * string is an array of 16-bit quantities organized as a sequence of properly - * formed UTF-8 characters, while a ByteArray is an array of 8-bit quantities. + * The following object type represents an array of bytes. An array of bytes + * is not equivalent to an internationalized string. Conceptually, a string is + * an array of 16-bit quantities organized as a sequence of properly formed + * UTF-8 characters, while a ByteArray is an array of 8-bit quantities.   * Accessor functions are provided to convert a ByteArray to a String or a - * String to a ByteArray.  Two or more consecutive bytes in an array of bytes + * String to a ByteArray. Two or more consecutive bytes in an array of bytes   * may look like a single UTF-8 character if the array is casually treated as - * a string.  But obtaining the String from a ByteArray is guaranteed to - * produced properly formed UTF-8 sequences so that there is a one-to-one - * map between bytes and characters. + * a string. But obtaining the String from a ByteArray is guaranteed to + * produced properly formed UTF-8 sequences so that there is a one-to-one map + * between bytes and characters.   *   * Converting a ByteArray to a String proceeds by casting each byte in the   * array to a 16-bit quantity, treating that number as a Unicode character, - * and storing the UTF-8 version of that Unicode character in the String. - * For ByteArrays consisting entirely of values 1..127, the corresponding - * String representation is the same as the ByteArray representation. + * and storing the UTF-8 version of that Unicode character in the String. For + * ByteArrays consisting entirely of values 1..127, the corresponding String + * representation is the same as the ByteArray representation.   *   * Converting a String to a ByteArray proceeds by getting the Unicode - * representation of each character in the String, casting it to a - * byte by truncating the upper 8 bits, and then storing the byte in the - * ByteArray.  Converting from ByteArray to String and back to ByteArray - * is not lossy, but converting an arbitrary String to a ByteArray may be. + * representation of each character in the String, casting it to a byte by + * truncating the upper 8 bits, and then storing the byte in the ByteArray. + * Converting from ByteArray to String and back to ByteArray is not lossy, but + * converting an arbitrary String to a ByteArray may be.   */ -Tcl_ObjType tclByteArrayType = { +const Tcl_ObjType tclByteArrayType = {      "bytearray",      FreeByteArrayInternalRep,      DupByteArrayInternalRep, @@ -76,10 +188,10 @@ Tcl_ObjType tclByteArrayType = {  };  /* - * The following structure is the internal rep for a ByteArray object. - * Keeps track of how much memory has been used and how much has been - * allocated for the byte array to enable growing and shrinking of the - * ByteArray object with fewer mallocs.   + * The following structure is the internal rep for a ByteArray object. Keeps + * track of how much memory has been used and how much has been allocated for + * the byte array to enable growing and shrinking of the ByteArray object with + * fewer mallocs.   */  typedef struct ByteArray { @@ -87,71 +199,59 @@ typedef struct ByteArray {  				 * array. */      int allocated;		/* The amount of space actually allocated  				 * minus 1 byte. */ -    unsigned char bytes[4];	/* The array of bytes.  The actual size of -				 * this field depends on the 'allocated' field +    unsigned char bytes[1];	/* The array of bytes. The actual size of this +				 * field depends on the 'allocated' field  				 * above. */  } ByteArray; -#define BYTEARRAY_SIZE(len)	\ -		((unsigned) (sizeof(ByteArray) - 4 + (len))) +#define BYTEARRAY_SIZE(len) \ +		((unsigned) (TclOffset(ByteArray, bytes) + (len)))  #define GET_BYTEARRAY(objPtr) \ -		((ByteArray *) (objPtr)->internalRep.otherValuePtr) +		((ByteArray *) (objPtr)->internalRep.twoPtrValue.ptr1)  #define SET_BYTEARRAY(objPtr, baPtr) \ -		(objPtr)->internalRep.otherValuePtr = (VOID *) (baPtr) +		(objPtr)->internalRep.twoPtrValue.ptr1 = (void *) (baPtr)  /* - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   *   * Tcl_NewByteArrayObj --   * - *	This procedure is creates a new ByteArray object and initializes - *	it from the given array of bytes. + *	This procedure is creates a new ByteArray object and initializes it + *	from the given array of bytes.   *   * Results: - *	The newly create object is returned.  This object will have no - *	initial string representation.  The returned object has a ref count - *	of 0. + *	The newly create object is returned. This object will have no initial + *	string representation. The returned object has a ref count of 0.   *   * Side effects:   *	Memory allocated for new object and copy of byte array argument.   * - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   */ -#ifdef TCL_MEM_DEBUG  #undef Tcl_NewByteArrayObj -  Tcl_Obj * -Tcl_NewByteArrayObj(bytes, length) -    CONST unsigned char *bytes;	/* The array of bytes used to initialize -				 * the new object. */ -    int length;			/* Length of the array of bytes, which must -				 * be >= 0. */ +Tcl_NewByteArrayObj( +    const unsigned char *bytes,	/* The array of bytes used to initialize the +				 * new object. */ +    int length)			/* Length of the array of bytes, which must be +				 * >= 0. */  { +#ifdef TCL_MEM_DEBUG      return Tcl_DbNewByteArrayObj(bytes, length, "unknown", 0); -} -  #else /* if not TCL_MEM_DEBUG */ - -Tcl_Obj * -Tcl_NewByteArrayObj(bytes, length) -    CONST unsigned char *bytes;	/* The array of bytes used to initialize -				 * the new object. */ -    int length;			/* Length of the array of bytes, which must -				 * be >= 0. */ -{      Tcl_Obj *objPtr;      TclNewObj(objPtr);      Tcl_SetByteArrayObj(objPtr, bytes, length);      return objPtr; -}  #endif /* TCL_MEM_DEBUG */ +}  /* - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   *   * Tcl_DbNewByteArrayObj --   * @@ -159,60 +259,44 @@ Tcl_NewByteArrayObj(bytes, length)   *	TCL_MEM_DEBUG is defined. It is the same as the Tcl_NewByteArrayObj   *	above except that it calls Tcl_DbCkalloc directly with the file name   *	and line number from its caller. This simplifies debugging since then - *	the checkmem command will report the correct file name and line number - *	when reporting objects that haven't been freed. + *	the [memory active] command will report the correct file name and line + *	number when reporting objects that haven't been freed.   *   *	When TCL_MEM_DEBUG is not defined, this procedure just returns the   *	result of calling Tcl_NewByteArrayObj.   *   * Results: - *	The newly create object is returned.  This object will have no - *	initial string representation.  The returned object has a ref count - *	of 0. + *	The newly create object is returned. This object will have no initial + *	string representation. The returned object has a ref count of 0.   *   * Side effects:   *	Memory allocated for new object and copy of byte array argument.   * - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   */ -#ifdef TCL_MEM_DEBUG -  Tcl_Obj * -Tcl_DbNewByteArrayObj(bytes, length, file, line) -    CONST unsigned char *bytes;	/* The array of bytes used to initialize -				 * the new object. */ -    int length;			/* Length of the array of bytes, which must -				 * be >= 0. */ -    CONST char *file;		/* The name of the source file calling this +Tcl_DbNewByteArrayObj( +    const unsigned char *bytes,	/* The array of bytes used to initialize the +				 * new object. */ +    int length,			/* Length of the array of bytes, which must be +				 * >= 0. */ +    const char *file,		/* The name of the source file calling this  				 * procedure; used for debugging. */ -    int line;			/* Line number in the source file; used -				 * for debugging. */ +    int line)			/* Line number in the source file; used for +				 * debugging. */  { +#ifdef TCL_MEM_DEBUG      Tcl_Obj *objPtr;      TclDbNewObj(objPtr, file, line);      Tcl_SetByteArrayObj(objPtr, bytes, length);      return objPtr; -} -  #else /* if not TCL_MEM_DEBUG */ - -Tcl_Obj * -Tcl_DbNewByteArrayObj(bytes, length, file, line) -    CONST unsigned char *bytes;	/* The array of bytes used to initialize -				 * the new object. */ -    int length;			/* Length of the array of bytes, which must -				 * be >= 0. */ -    CONST char *file;		/* The name of the source file calling this -				 * procedure; used for debugging. */ -    int line;			/* Line number in the source file; used -				 * for debugging. */ -{      return Tcl_NewByteArrayObj(bytes, length); -}  #endif /* TCL_MEM_DEBUG */ - +} +  /*   *---------------------------------------------------------------------------   * @@ -225,37 +309,38 @@ Tcl_DbNewByteArrayObj(bytes, length, file, line)   *	None.   *   * Side effects: - *	The object's old string rep and internal rep is freed. - *	Memory allocated for copy of byte array argument. + *	The object's old string rep and internal rep is freed. Memory + *	allocated for copy of byte array argument.   *   *----------------------------------------------------------------------   */  void -Tcl_SetByteArrayObj(objPtr, bytes, length) -    Tcl_Obj *objPtr;		/* Object to initialize as a ByteArray. */ -    CONST unsigned char *bytes;	/* The array of bytes to use as the new -				 * value. */ -    int length;			/* Length of the array of bytes, which must -				 * be >= 0. */ +Tcl_SetByteArrayObj( +    Tcl_Obj *objPtr,		/* Object to initialize as a ByteArray. */ +    const unsigned char *bytes,	/* The array of bytes to use as the new +				   value. May be NULL even if length > 0. */ +    int length)			/* Length of the array of bytes, which must +				   be >= 0. */  { -    Tcl_ObjType *typePtr;      ByteArray *byteArrayPtr;      if (Tcl_IsShared(objPtr)) { -	panic("Tcl_SetByteArrayObj called with shared object"); -    } -    typePtr = objPtr->typePtr; -    if ((typePtr != NULL) && (typePtr->freeIntRepProc != NULL)) { -	(*typePtr->freeIntRepProc)(objPtr); +	Tcl_Panic("%s called with shared object", "Tcl_SetByteArrayObj");      } -    Tcl_InvalidateStringRep(objPtr); +    TclFreeIntRep(objPtr); +    TclInvalidateStringRep(objPtr); -    byteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length)); +    if (length < 0) { +	length = 0; +    } +    byteArrayPtr = ckalloc(BYTEARRAY_SIZE(length));      byteArrayPtr->used = length;      byteArrayPtr->allocated = length; -    memcpy((VOID *) byteArrayPtr->bytes, (VOID *) bytes, (size_t) length); +    if ((bytes != NULL) && (length > 0)) { +	memcpy(byteArrayPtr->bytes, bytes, (size_t) length); +    }      objPtr->typePtr = &tclByteArrayType;      SET_BYTEARRAY(objPtr, byteArrayPtr);  } @@ -265,28 +350,30 @@ Tcl_SetByteArrayObj(objPtr, bytes, length)   *   * Tcl_GetByteArrayFromObj --   * - *	Attempt to get the array of bytes from the Tcl object.  If the - *	object is not already a ByteArray object, an attempt will be - *	made to convert it to one. + *	Attempt to get the array of bytes from the Tcl object. If the object + *	is not already a ByteArray object, an attempt will be made to convert + *	it to one.   *   * Results:   *	Pointer to array of bytes representing the ByteArray object.   *   * Side effects: - *	Frees old internal rep.  Allocates memory for new internal rep. + *	Frees old internal rep. Allocates memory for new internal rep.   *   *----------------------------------------------------------------------   */  unsigned char * -Tcl_GetByteArrayFromObj(objPtr, lengthPtr) -    Tcl_Obj *objPtr;		/* The ByteArray object. */ -    int *lengthPtr;		/* If non-NULL, filled with length of the +Tcl_GetByteArrayFromObj( +    Tcl_Obj *objPtr,		/* The ByteArray object. */ +    int *lengthPtr)		/* If non-NULL, filled with length of the  				 * array of bytes in the ByteArray object. */  {      ByteArray *baPtr; -     -    SetByteArrayFromAny(NULL, objPtr); + +    if (objPtr->typePtr != &tclByteArrayType) { +	SetByteArrayFromAny(NULL, objPtr); +    }      baPtr = GET_BYTEARRAY(objPtr);      if (lengthPtr != NULL) { @@ -300,32 +387,32 @@ Tcl_GetByteArrayFromObj(objPtr, lengthPtr)   *   * Tcl_SetByteArrayLength --   * - *	This procedure changes the length of the byte array for this - *	object.  Once the caller has set the length of the array, it - *	is acceptable to directly modify the bytes in the array up until - *	Tcl_GetStringFromObj() has been called on this object. + *	This procedure changes the length of the byte array for this object. + *	Once the caller has set the length of the array, it is acceptable to + *	directly modify the bytes in the array up until Tcl_GetStringFromObj() + *	has been called on this object.   *   * Results:   *	The new byte array of the specified length.   *   * Side effects: - *	Allocates enough memory for an array of bytes of the requested - *	size.  When growing the array, the old array is copied to the - *	new array; new bytes are undefined.  When shrinking, the - *	old array is truncated to the specified length. + *	Allocates enough memory for an array of bytes of the requested size. + *	When growing the array, the old array is copied to the new array; new + *	bytes are undefined. When shrinking, the old array is truncated to the + *	specified length.   * - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   */  unsigned char * -Tcl_SetByteArrayLength(objPtr, length) -    Tcl_Obj *objPtr;		/* The ByteArray object. */ -    int length;			/* New length for internal byte array. */ +Tcl_SetByteArrayLength( +    Tcl_Obj *objPtr,		/* The ByteArray object. */ +    int length)			/* New length for internal byte array. */  { -    ByteArray *byteArrayPtr, *newByteArrayPtr; -     +    ByteArray *byteArrayPtr; +      if (Tcl_IsShared(objPtr)) { -	panic("Tcl_SetObjLength called with shared object"); +	Tcl_Panic("%s called with shared object", "Tcl_SetByteArrayLength");      }      if (objPtr->typePtr != &tclByteArrayType) {  	SetByteArrayFromAny(NULL, objPtr); @@ -333,22 +420,17 @@ Tcl_SetByteArrayLength(objPtr, length)      byteArrayPtr = GET_BYTEARRAY(objPtr);      if (length > byteArrayPtr->allocated) { -	newByteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length)); -	newByteArrayPtr->used = length; -	newByteArrayPtr->allocated = length; -	memcpy((VOID *) newByteArrayPtr->bytes, -		(VOID *) byteArrayPtr->bytes, (size_t) byteArrayPtr->used); -	ckfree((char *) byteArrayPtr); -	byteArrayPtr = newByteArrayPtr; +	byteArrayPtr = ckrealloc(byteArrayPtr, BYTEARRAY_SIZE(length)); +	byteArrayPtr->allocated = length;  	SET_BYTEARRAY(objPtr, byteArrayPtr);      } -    Tcl_InvalidateStringRep(objPtr); +    TclInvalidateStringRep(objPtr);      byteArrayPtr->used = length;      return byteArrayPtr->bytes;  }  /* - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   *   * SetByteArrayFromAny --   * @@ -360,38 +442,34 @@ Tcl_SetByteArrayLength(objPtr, length)   * Side effects:   *	A ByteArray object is stored as the internal rep of objPtr.   * - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   */  static int -SetByteArrayFromAny(interp, objPtr) -    Tcl_Interp *interp;		/* Not used. */ -    Tcl_Obj *objPtr;		/* The object to convert to type ByteArray. */ +SetByteArrayFromAny( +    Tcl_Interp *interp,		/* Not used. */ +    Tcl_Obj *objPtr)		/* The object to convert to type ByteArray. */  { -    Tcl_ObjType *typePtr;      int length; -    char *src, *srcEnd; +    const char *src, *srcEnd;      unsigned char *dst;      ByteArray *byteArrayPtr;      Tcl_UniChar ch; -     -    typePtr = objPtr->typePtr; -    if (typePtr != &tclByteArrayType) { -	src = Tcl_GetStringFromObj(objPtr, &length); + +    if (objPtr->typePtr != &tclByteArrayType) { +	src = TclGetStringFromObj(objPtr, &length);  	srcEnd = src + length; -	byteArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length)); +	byteArrayPtr = ckalloc(BYTEARRAY_SIZE(length));  	for (dst = byteArrayPtr->bytes; src < srcEnd; ) {  	    src += Tcl_UtfToUniChar(src, &ch); -	    *dst++ = (unsigned char) ch; +	    *dst++ = UCHAR(ch);  	}  	byteArrayPtr->used = dst - byteArrayPtr->bytes;  	byteArrayPtr->allocated = length; -	if ((typePtr != NULL) && (typePtr->freeIntRepProc) != NULL) { -	    (*typePtr->freeIntRepProc)(objPtr); -	} +	TclFreeIntRep(objPtr);  	objPtr->typePtr = &tclByteArrayType;  	SET_BYTEARRAY(objPtr, byteArrayPtr);      } @@ -410,26 +488,26 @@ SetByteArrayFromAny(interp, objPtr)   *	None.   *   * Side effects: - *	Frees memory.  + *	Frees memory.   *   *----------------------------------------------------------------------   */  static void -FreeByteArrayInternalRep(objPtr) -    Tcl_Obj *objPtr;		/* Object with internal rep to free. */ +FreeByteArrayInternalRep( +    Tcl_Obj *objPtr)		/* Object with internal rep to free. */  { -    ckfree((char *) GET_BYTEARRAY(objPtr)); +    ckfree(GET_BYTEARRAY(objPtr)); +    objPtr->typePtr = NULL;  }  /* - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   *   * DupByteArrayInternalRep --   * - *	Initialize the internal representation of a ByteArray Tcl_Obj - *	to a copy of the internal representation of an existing ByteArray - *	object.  + *	Initialize the internal representation of a ByteArray Tcl_Obj to a + *	copy of the internal representation of an existing ByteArray object.   *   * Results:   *	None. @@ -437,55 +515,54 @@ FreeByteArrayInternalRep(objPtr)   * Side effects:   *	Allocates memory.   * - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   */  static void -DupByteArrayInternalRep(srcPtr, copyPtr) -    Tcl_Obj *srcPtr;		/* Object with internal rep to copy. */ -    Tcl_Obj *copyPtr;		/* Object with internal rep to set. */ +DupByteArrayInternalRep( +    Tcl_Obj *srcPtr,		/* Object with internal rep to copy. */ +    Tcl_Obj *copyPtr)		/* Object with internal rep to set. */  {      int length; -    ByteArray *srcArrayPtr, *copyArrayPtr;     +    ByteArray *srcArrayPtr, *copyArrayPtr;      srcArrayPtr = GET_BYTEARRAY(srcPtr);      length = srcArrayPtr->used; -    copyArrayPtr = (ByteArray *) ckalloc(BYTEARRAY_SIZE(length)); +    copyArrayPtr = ckalloc(BYTEARRAY_SIZE(length));      copyArrayPtr->used = length;      copyArrayPtr->allocated = length; -    memcpy((VOID *) copyArrayPtr->bytes, (VOID *) srcArrayPtr->bytes, -	    (size_t) length); +    memcpy(copyArrayPtr->bytes, srcArrayPtr->bytes, (size_t) length);      SET_BYTEARRAY(copyPtr, copyArrayPtr);      copyPtr->typePtr = &tclByteArrayType;  }  /* - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   *   * UpdateStringOfByteArray --   * - *	Update the string representation for a ByteArray data object. - *	Note: This procedure does not invalidate an existing old string rep - *	so storage will be lost if this has not already been done.  + *	Update the string representation for a ByteArray data object. Note: + *	This procedure does not invalidate an existing old string rep so + *	storage will be lost if this has not already been done.   *   * Results:   *	None.   *   * Side effects: - *	The object's string is set to a valid string that results from - *	the ByteArray-to-string conversion. + *	The object's string is set to a valid string that results from the + *	ByteArray-to-string conversion.   * - *	The object becomes a string object -- the internal rep is - *	discarded and the typePtr becomes NULL. + *	The object becomes a string object -- the internal rep is discarded + *	and the typePtr becomes NULL.   * - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   */  static void -UpdateStringOfByteArray(objPtr) -    Tcl_Obj *objPtr;		/* ByteArray object whose string rep to +UpdateStringOfByteArray( +    Tcl_Obj *objPtr)		/* ByteArray object whose string rep to  				 * update. */  {      int i, length, size; @@ -500,20 +577,23 @@ UpdateStringOfByteArray(objPtr)      /*       * How much space will string rep need?       */ -      +      size = length; -    for (i = 0; i < length; i++) { +    for (i = 0; i < length && size >= 0; i++) {  	if ((src[i] == 0) || (src[i] > 127)) {  	    size++;  	}      } +    if (size < 0) { +	Tcl_Panic("max size for a Tcl value (%d bytes) exceeded", INT_MAX); +    } -    dst = (char *) ckalloc((unsigned) (size + 1)); +    dst = ckalloc(size + 1);      objPtr->bytes = dst;      objPtr->length = size;      if (size == length) { -	memcpy((VOID *) dst, (VOID *) src, (size_t) size); +	memcpy(dst, src, (size_t) size);  	dst[size] = '\0';      } else {  	for (i = 0; i < length; i++) { @@ -526,9 +606,126 @@ UpdateStringOfByteArray(objPtr)  /*   *----------------------------------------------------------------------   * - * Tcl_BinaryObjCmd -- + * 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] + * + *---------------------------------------------------------------------- + */ + +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 = attemptckrealloc(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 = attemptckrealloc(byteArrayPtr, BYTEARRAY_SIZE(attempt)); +	} +	if (ptr == NULL) { +	    /* Last chance: Try to allocate exactly what is needed. */ +	    attempt = needed; +	    ptr = ckrealloc(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); +} + +/* + *---------------------------------------------------------------------- + * + * TclInitBinaryCmd -- + * + *	This function is called to create the "binary" Tcl command. See the + *	user documentation for details on what it does. + * + * Results: + *	A command token for the new command. + * + * Side effects: + *	Creates a new binary command as a mapped ensemble. + * + *---------------------------------------------------------------------- + */ + +Tcl_Command +TclInitBinaryCmd( +    Tcl_Interp *interp) +{ +    Tcl_Command binaryEnsemble; + +    binaryEnsemble = TclMakeEnsemble(interp, "binary", binaryMap); +    TclMakeEnsemble(interp, "binary encode", encodeMap); +    TclMakeEnsemble(interp, "binary decode", decodeMap); +    return binaryEnsemble; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryFormatCmd --   * - *	This procedure implements the "binary" Tcl command. + *	This procedure implements the "binary format" Tcl command.   *   * Results:   *	A standard Tcl result. @@ -539,12 +736,12 @@ UpdateStringOfByteArray(objPtr)   *----------------------------------------------------------------------   */ -int -Tcl_BinaryObjCmd(dummy, interp, objc, objv) -    ClientData dummy;		/* Not used. */ -    Tcl_Interp *interp;		/* Current interpreter. */ -    int objc;			/* Number of arguments. */ -    Tcl_Obj *CONST objv[];	/* Argument objects. */ +static int +BinaryFormatCmd( +    ClientData dummy,		/* Not used. */ +    Tcl_Interp *interp,		/* Current interpreter. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */  {      int arg;			/* Index of next argument to consume. */      int value = 0;		/* Current integer value to be packed. @@ -552,759 +749,849 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)      char cmd;			/* Current format character. */      int count;			/* Count associated with current format  				 * character. */ -    char *format;		/* Pointer to current position in format +    int flags;			/* Format field flags */ +    const char *format;	/* Pointer to current position in format  				 * string. */ -    Tcl_Obj *resultPtr;		/* Object holding result buffer. */ +    Tcl_Obj *resultPtr = NULL;	/* Object holding result buffer. */      unsigned char *buffer;	/* Start of result buffer. */      unsigned char *cursor;	/* Current position within result buffer. */      unsigned char *maxPos;	/* Greatest position within result buffer that  				 * cursor has visited.*/ -    char *errorString, *errorValue, *str; -    int offset, size, length, index; -    static char *options[] = {  -	"format",	"scan",		NULL  -    }; -    enum options {  -	BINARY_FORMAT,	BINARY_SCAN -    }; +    const char *errorString; +    const char *errorValue, *str; +    int offset, size, length;      if (objc < 2) { -    	Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); +	Tcl_WrongNumArgs(interp, 1, objv, "formatString ?arg ...?");  	return TCL_ERROR;      } -    if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0, -	    &index) != TCL_OK) { -    	return TCL_ERROR; -    } +    /* +     * To avoid copying the data, we format the string in two passes. The +     * first pass computes the size of the output buffer. The second pass +     * places the formatted data into the buffer. +     */ -    switch ((enum options) index) { -	case BINARY_FORMAT: { -	    if (objc < 3) { -		Tcl_WrongNumArgs(interp, 2, objv, "formatString ?arg arg ...?"); -		return TCL_ERROR; +    format = TclGetString(objv[1]); +    arg = 2; +    offset = 0; +    length = 0; +    while (*format != '\0') { +	str = format; +	flags = 0; +	if (!GetFormatSpec(&format, &cmd, &count, &flags)) { +	    break; +	} +	switch (cmd) { +	case 'a': +	case 'A': +	case 'b': +	case 'B': +	case 'h': +	case 'H': +	    /* +	     * For string-type specifiers, the count corresponds to the number +	     * of bytes in a single argument. +	     */ + +	    if (arg >= objc) { +		goto badIndex; +	    } +	    if (count == BINARY_ALL) { +		Tcl_GetByteArrayFromObj(objv[arg], &count); +	    } else if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    arg++; +	    if (cmd == 'a' || cmd == 'A') { +		offset += count; +	    } else if (cmd == 'b' || cmd == 'B') { +		offset += (count + 7) / 8; +	    } else { +		offset += (count + 1) / 2; +	    } +	    break; +	case 'c': +	    size = 1; +	    goto doNumbers; +	case 't': +	case 's': +	case 'S': +	    size = 2; +	    goto doNumbers; +	case 'n': +	case 'i': +	case 'I': +	    size = 4; +	    goto doNumbers; +	case 'm': +	case 'w': +	case 'W': +	    size = 8; +	    goto doNumbers; +	case 'r': +	case 'R': +	case 'f': +	    size = sizeof(float); +	    goto doNumbers; +	case 'q': +	case 'Q': +	case 'd': +	    size = sizeof(double); + +	doNumbers: +	    if (arg >= objc) { +		goto badIndex;  	    }  	    /* -	     * To avoid copying the data, we format the string in two passes. -	     * The first pass computes the size of the output buffer.  The -	     * second pass places the formatted data into the buffer. +	     * For number-type specifiers, the count corresponds to the number +	     * of elements in the list stored in a single argument. If no +	     * count is specified, then the argument is taken as a single +	     * non-list value.  	     */ -	    format = Tcl_GetString(objv[2]); -	    arg = 3; -	    offset = 0; -	    length = 0; -	    while (*format != '\0') { -		str = format; -		if (!GetFormatSpec(&format, &cmd, &count)) { -		    break; -		} -		switch (cmd) { -		    case 'a': -		    case 'A': -		    case 'b': -		    case 'B': -		    case 'h': -		    case 'H': { -			/* -			 * For string-type specifiers, the count corresponds -			 * to the number of bytes in a single argument. -			 */ - -			if (arg >= objc) { -			    goto badIndex; -			} -			if (count == BINARY_ALL) { -			    Tcl_GetByteArrayFromObj(objv[arg], &count); -			} else if (count == BINARY_NOCOUNT) { -			    count = 1; -			} -			arg++; -			if (cmd == 'a' || cmd == 'A') { -			    offset += count; -			} else if (cmd == 'b' || cmd == 'B') { -			    offset += (count + 7) / 8; -			} else { -			    offset += (count + 1) / 2; -			} -			break; -		    } -		    case 'c': { -			size = 1; -			goto doNumbers; -		    } -		    case 's': -		    case 'S': { -			size = 2; -			goto doNumbers; -		    } -		    case 'i': -		    case 'I': { -			size = 4; -			goto doNumbers; -		    } -		    case 'f': { -			size = sizeof(float); -			goto doNumbers; -		    } -		    case 'd': { -			size = sizeof(double); -			 -			doNumbers: -			if (arg >= objc) { -			    goto badIndex; -			} +	    if (count == BINARY_NOCOUNT) { +		arg++; +		count = 1; +	    } else { +		int listc; +		Tcl_Obj **listv; -			/* -			 * For number-type specifiers, the count corresponds -			 * to the number of elements in the list stored in -			 * a single argument.  If no count is specified, then -			 * the argument is taken as a single non-list value. -			 */ - -			if (count == BINARY_NOCOUNT) { -			    arg++; -			    count = 1; -			} else { -			    int listc; -			    Tcl_Obj **listv; -			    if (Tcl_ListObjGetElements(interp, objv[arg++], -				    &listc, &listv) != TCL_OK) { -				return TCL_ERROR; -			    } -			    if (count == BINARY_ALL) { -				count = listc; -			    } else if (count > listc) { -			        Tcl_AppendResult(interp,  -					"number of elements in list does not match count", -					(char *) NULL); -				return TCL_ERROR; -			    } -			} -			offset += count*size; -			break; -		    } -		    case 'x': { -			if (count == BINARY_ALL) { -			    Tcl_AppendResult(interp,  -				    "cannot use \"*\" in format string with \"x\"", -				    (char *) NULL); -			    return TCL_ERROR; -			} else if (count == BINARY_NOCOUNT) { -			    count = 1; -			} -			offset += count; -			break; -		    } -		    case 'X': { -			if (count == BINARY_NOCOUNT) { -			    count = 1; -			} -			if ((count > offset) || (count == BINARY_ALL)) { -			    count = offset; -			} -			if (offset > length) { -			    length = offset; -			} -			offset -= count; -			break; -		    } -		    case '@': { -			if (offset > length) { -			    length = offset; -			} -			if (count == BINARY_ALL) { -			    offset = length; -			} else if (count == BINARY_NOCOUNT) { -			    goto badCount; -			} else { -			    offset = count; -			} -			break; -		    } -		    default: { -			errorString = str; -			goto badfield; -		    } +		/* +		 * The macro evals its args more than once: avoid arg++ +		 */ + +		if (TclListObjGetElements(interp, objv[arg], &listc, +			&listv) != TCL_OK) { +		    return TCL_ERROR;  		} +		arg++; + +		if (count == BINARY_ALL) { +		    count = listc; +		} else if (count > listc) { +		    Tcl_SetObjResult(interp, Tcl_NewStringObj( +			    "number of elements in list does not match count", +			    -1)); +		    return TCL_ERROR; +		} +	    } +	    offset += count*size; +	    break; + +	case 'x': +	    if (count == BINARY_ALL) { +		Tcl_SetObjResult(interp, Tcl_NewStringObj( +			"cannot use \"*\" in format string with \"x\"", -1)); +		return TCL_ERROR; +	    } else if (count == BINARY_NOCOUNT) { +		count = 1;  	    } +	    offset += count; +	    break; +	case 'X': +	    if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    if ((count > offset) || (count == BINARY_ALL)) { +		count = offset; +	    } +	    if (offset > length) { +		length = offset; +	    } +	    offset -= count; +	    break; +	case '@':  	    if (offset > length) {  		length = offset;  	    } -	    if (length == 0) { -		return TCL_OK; +	    if (count == BINARY_ALL) { +		offset = length; +	    } else if (count == BINARY_NOCOUNT) { +		goto badCount; +	    } else { +		offset = count;  	    } +	    break; +	default: +	    errorString = str; +	    goto badField; +	} +    } +    if (offset > length) { +	length = offset; +    } +    if (length == 0) { +	return TCL_OK; +    } -	    /* -	     * Prepare the result object by preallocating the caclulated -	     * number of bytes and filling with nulls. -	     */ +    /* +     * Prepare the result object by preallocating the caclulated number of +     * bytes and filling with nulls. +     */ -	    resultPtr = Tcl_GetObjResult(interp); -	    buffer = Tcl_SetByteArrayLength(resultPtr, length); -	    memset((VOID *) buffer, 0, (size_t) length); +    resultPtr = Tcl_NewObj(); +    buffer = Tcl_SetByteArrayLength(resultPtr, length); +    memset(buffer, 0, (size_t) length); -	    /* -	     * Pack the data into the result object.  Note that we can skip -	     * the error checking during this pass, since we have already -	     * parsed the string once. -	     */ +    /* +     * Pack the data into the result object. Note that we can skip the +     * error checking during this pass, since we have already parsed the +     * string once. +     */ -	    arg = 3; -	    format = Tcl_GetString(objv[2]); -	    cursor = buffer; -	    maxPos = cursor; -	    while (*format != 0) { -		if (!GetFormatSpec(&format, &cmd, &count)) { -		    break; -		} -		if ((count == 0) && (cmd != '@')) { -		    arg++; -		    continue; +    arg = 2; +    format = TclGetString(objv[1]); +    cursor = buffer; +    maxPos = cursor; +    while (*format != 0) { +	flags = 0; +	if (!GetFormatSpec(&format, &cmd, &count, &flags)) { +	    break; +	} +	if ((count == 0) && (cmd != '@')) { +	    if (cmd != 'x') { +		arg++; +	    } +	    continue; +	} +	switch (cmd) { +	case 'a': +	case 'A': { +	    char pad = (char) (cmd == 'a' ? '\0' : ' '); +	    unsigned char *bytes; + +	    bytes = Tcl_GetByteArrayFromObj(objv[arg++], &length); + +	    if (count == BINARY_ALL) { +		count = length; +	    } else if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    if (length >= count) { +		memcpy(cursor, bytes, (size_t) count); +	    } else { +		memcpy(cursor, bytes, (size_t) length); +		memset(cursor + length, pad, (size_t) (count - length)); +	    } +	    cursor += count; +	    break; +	} +	case 'b': +	case 'B': { +	    unsigned char *last; + +	    str = TclGetStringFromObj(objv[arg], &length); +	    arg++; +	    if (count == BINARY_ALL) { +		count = length; +	    } else if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    last = cursor + ((count + 7) / 8); +	    if (count > length) { +		count = length; +	    } +	    value = 0; +	    errorString = "binary"; +	    if (cmd == 'B') { +		for (offset = 0; offset < count; offset++) { +		    value <<= 1; +		    if (str[offset] == '1') { +			value |= 1; +		    } else if (str[offset] != '0') { +			errorValue = str; +			Tcl_DecrRefCount(resultPtr); +			goto badValue; +		    } +		    if (((offset + 1) % 8) == 0) { +			*cursor++ = UCHAR(value); +			value = 0; +		    }  		} -		switch (cmd) { -		    case 'a': -		    case 'A': { -			char pad = (char) (cmd == 'a' ? '\0' : ' '); -			unsigned char *bytes; - -			bytes = Tcl_GetByteArrayFromObj(objv[arg++], &length); - -			if (count == BINARY_ALL) { -			    count = length; -			} else if (count == BINARY_NOCOUNT) { -			    count = 1; -			} -			if (length >= count) { -			    memcpy((VOID *) cursor, (VOID *) bytes, -				    (size_t) count); -			} else { -			    memcpy((VOID *) cursor, (VOID *) bytes, -				    (size_t) length); -			    memset((VOID *) (cursor + length), pad, -			            (size_t) (count - length)); -			} -			cursor += count; -			break; +	    } else { +		for (offset = 0; offset < count; offset++) { +		    value >>= 1; +		    if (str[offset] == '1') { +			value |= 128; +		    } else if (str[offset] != '0') { +			errorValue = str; +			Tcl_DecrRefCount(resultPtr); +			goto badValue;  		    } -		    case 'b': -		    case 'B': { -			unsigned char *last; -			 -			str = Tcl_GetStringFromObj(objv[arg++], &length); -			if (count == BINARY_ALL) { -			    count = length; -			} else if (count == BINARY_NOCOUNT) { -			    count = 1; -			} -			last = cursor + ((count + 7) / 8); -			if (count > length) { -			    count = length; -			} +		    if (!((offset + 1) % 8)) { +			*cursor++ = UCHAR(value);  			value = 0; -			errorString = "binary"; -			if (cmd == 'B') { -			    for (offset = 0; offset < count; offset++) { -				value <<= 1; -				if (str[offset] == '1') { -				    value |= 1; -				} else if (str[offset] != '0') { -				    errorValue = str; -				    goto badValue; -				} -				if (((offset + 1) % 8) == 0) { -				    *cursor++ = (unsigned char) value; -				    value = 0; -				} -			    } -			} else { -			    for (offset = 0; offset < count; offset++) { -				value >>= 1; -				if (str[offset] == '1') { -				    value |= 128; -				} else if (str[offset] != '0') { -				    errorValue = str; -				    goto badValue; -				} -				if (!((offset + 1) % 8)) { -				    *cursor++ = (unsigned char) value; -				    value = 0; -				} -			    } -			} -			if ((offset % 8) != 0) { -			    if (cmd == 'B') { -				value <<= 8 - (offset % 8); -			    } else { -				value >>= 8 - (offset % 8); -			    } -			    *cursor++ = (unsigned char) value; -			} -			while (cursor < last) { -			    *cursor++ = '\0'; -			} -			break;  		    } -		    case 'h': -		    case 'H': { -			unsigned char *last; -			int c; -			 -			str = Tcl_GetStringFromObj(objv[arg++], &length); -			if (count == BINARY_ALL) { -			    count = length; -			} else if (count == BINARY_NOCOUNT) { -			    count = 1; -			} -			last = cursor + ((count + 1) / 2); -			if (count > length) { -			    count = length; -			} +		} +	    } +	    if ((offset % 8) != 0) { +		if (cmd == 'B') { +		    value <<= 8 - (offset % 8); +		} else { +		    value >>= 8 - (offset % 8); +		} +		*cursor++ = UCHAR(value); +	    } +	    while (cursor < last) { +		*cursor++ = '\0'; +	    } +	    break; +	} +	case 'h': +	case 'H': { +	    unsigned char *last; +	    int c; + +	    str = TclGetStringFromObj(objv[arg], &length); +	    arg++; +	    if (count == BINARY_ALL) { +		count = length; +	    } else if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    last = cursor + ((count + 1) / 2); +	    if (count > length) { +		count = length; +	    } +	    value = 0; +	    errorString = "hexadecimal"; +	    if (cmd == 'H') { +		for (offset = 0; offset < count; offset++) { +		    value <<= 4; +		    if (!isxdigit(UCHAR(str[offset]))) {     /* INTL: digit */ +			errorValue = str; +			Tcl_DecrRefCount(resultPtr); +			goto badValue; +		    } +		    c = str[offset] - '0'; +		    if (c > 9) { +			c += ('0' - 'A') + 10; +		    } +		    if (c > 16) { +			c += ('A' - 'a'); +		    } +		    value |= (c & 0xf); +		    if (offset % 2) { +			*cursor++ = (char) value;  			value = 0; -			errorString = "hexadecimal"; -			if (cmd == 'H') { -			    for (offset = 0; offset < count; offset++) { -				value <<= 4; -				if (!isxdigit(UCHAR(str[offset]))) { /* INTL: digit */ -				    errorValue = str; -				    goto badValue; -				} -				c = str[offset] - '0'; -				if (c > 9) { -				    c += ('0' - 'A') + 10; -				} -				if (c > 16) { -				    c += ('A' - 'a'); -				} -				value |= (c & 0xf); -				if (offset % 2) { -				    *cursor++ = (char) value; -				    value = 0; -				} -			    } -			} else { -			    for (offset = 0; offset < count; offset++) { -				value >>= 4; - -				if (!isxdigit(UCHAR(str[offset]))) { /* INTL: digit */ -				    errorValue = str; -				    goto badValue; -				} -				c = str[offset] - '0'; -				if (c > 9) { -				    c += ('0' - 'A') + 10; -				} -				if (c > 16) { -				    c += ('A' - 'a'); -				} -				value |= ((c << 4) & 0xf0); -				if (offset % 2) { -				    *cursor++ = (unsigned char)(value & 0xff); -				    value = 0; -				} -			    } -			} -			if (offset % 2) { -			    if (cmd == 'H') { -				value <<= 4; -			    } else { -				value >>= 4; -			    } -			    *cursor++ = (unsigned char) value; -			} - -			while (cursor < last) { -			    *cursor++ = '\0'; -			} -			break;  		    } -		    case 'c': -		    case 's': -		    case 'S': -		    case 'i': -		    case 'I': -		    case 'd': -		    case 'f': { -			int listc, i; -			Tcl_Obj **listv; - -			if (count == BINARY_NOCOUNT) { -			    /* -			     * Note that we are casting away the const-ness of -			     * objv, but this is safe since we aren't going to -			     * modify the array. -			     */ - -			    listv = (Tcl_Obj**)(objv + arg); -			    listc = 1; -			    count = 1; -			} else { -			    Tcl_ListObjGetElements(interp, objv[arg], -				    &listc, &listv); -			    if (count == BINARY_ALL) { -				count = listc; -			    } -			} -			arg++; -			for (i = 0; i < count; i++) { -			    if (FormatNumber(interp, cmd, listv[i], &cursor) -				    != TCL_OK) { -				return TCL_ERROR; -			    } -			} -			break; +		} +	    } else { +		for (offset = 0; offset < count; offset++) { +		    value >>= 4; + +		    if (!isxdigit(UCHAR(str[offset]))) {     /* INTL: digit */ +			errorValue = str; +			Tcl_DecrRefCount(resultPtr); +			goto badValue;  		    } -		    case 'x': { -			if (count == BINARY_NOCOUNT) { -			    count = 1; -			} -			memset(cursor, 0, (size_t) count); -			cursor += count; -			break; +		    c = str[offset] - '0'; +		    if (c > 9) { +			c += ('0' - 'A') + 10;  		    } -		    case 'X': { -			if (cursor > maxPos) { -			    maxPos = cursor; -			} -			if (count == BINARY_NOCOUNT) { -			    count = 1; -			} -			if ((count == BINARY_ALL) -				|| (count > (cursor - buffer))) { -			    cursor = buffer; -			} else { -			    cursor -= count; -			} -			break; +		    if (c > 16) { +			c += ('A' - 'a');  		    } -		    case '@': { -			if (cursor > maxPos) { -			    maxPos = cursor; -			} -			if (count == BINARY_ALL) { -			    cursor = maxPos; -			} else { -			    cursor = buffer + count; -			} -			break; +		    value |= ((c << 4) & 0xf0); +		    if (offset % 2) { +			*cursor++ = UCHAR(value & 0xff); +			value = 0;  		    }  		}  	    } +	    if (offset % 2) { +		if (cmd == 'H') { +		    value <<= 4; +		} else { +		    value >>= 4; +		} +		*cursor++ = UCHAR(value); +	    } + +	    while (cursor < last) { +		*cursor++ = '\0'; +	    } +	    break; +	} +	case 'c': +	case 't': +	case 's': +	case 'S': +	case 'n': +	case 'i': +	case 'I': +	case 'm': +	case 'w': +	case 'W': +	case 'r': +	case 'R': +	case 'd': +	case 'q': +	case 'Q': +	case 'f': { +	    int listc, i; +	    Tcl_Obj **listv; + +	    if (count == BINARY_NOCOUNT) { +		/* +		 * Note that we are casting away the const-ness of objv, but +		 * this is safe since we aren't going to modify the array. +		 */ + +		listv = (Tcl_Obj **) (objv + arg); +		listc = 1; +		count = 1; +	    } else { +		TclListObjGetElements(interp, objv[arg], &listc, &listv); +		if (count == BINARY_ALL) { +		    count = listc; +		} +	    } +	    arg++; +	    for (i = 0; i < count; i++) { +		if (FormatNumber(interp, cmd, listv[i], &cursor)!=TCL_OK) { +		    Tcl_DecrRefCount(resultPtr); +		    return TCL_ERROR; +		} +	    }  	    break;  	} -	case BINARY_SCAN: { -	    int i; -	    Tcl_Obj *valuePtr, *elementPtr; +	case 'x': +	    if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    memset(cursor, 0, (size_t) count); +	    cursor += count; +	    break; +	case 'X': +	    if (cursor > maxPos) { +		maxPos = cursor; +	    } +	    if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    if ((count == BINARY_ALL) || (count > (cursor - buffer))) { +		cursor = buffer; +	    } else { +		cursor -= count; +	    } +	    break; +	case '@': +	    if (cursor > maxPos) { +		maxPos = cursor; +	    } +	    if (count == BINARY_ALL) { +		cursor = maxPos; +	    } else { +		cursor = buffer + count; +	    } +	    break; +	} +    } +    Tcl_SetObjResult(interp, resultPtr); +    return TCL_OK; -	    if (objc < 4) { -		Tcl_WrongNumArgs(interp, 2, objv, -			"value formatString ?varName varName ...?"); -		return TCL_ERROR; + badValue: +    Tcl_ResetResult(interp); +    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +	    "expected %s string but got \"%s\" instead", +	    errorString, errorValue)); +    return TCL_ERROR; + + badCount: +    errorString = "missing count for \"@\" field specifier"; +    goto error; + + badIndex: +    errorString = "not enough arguments for all format specifiers"; +    goto error; + + badField: +    { +	Tcl_UniChar ch; +	char buf[TCL_UTF_MAX + 1]; + +	Tcl_UtfToUniChar(errorString, &ch); +	buf[Tcl_UniCharToUtf(ch, buf)] = '\0'; +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"bad field specifier \"%s\"", buf)); +	return TCL_ERROR; +    } + + error: +    Tcl_SetObjResult(interp, Tcl_NewStringObj(errorString, -1)); +    return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryScanCmd -- + * + *	This procedure implements the "binary scan" Tcl command. + * + * Results: + *	A standard Tcl result. + * + * Side effects: + *	See the user documentation. + * + *---------------------------------------------------------------------- + */ + +int +BinaryScanCmd( +    ClientData dummy,		/* Not used. */ +    Tcl_Interp *interp,		/* Current interpreter. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */ +{ +    int arg;			/* Index of next argument to consume. */ +    int value = 0;		/* Current integer value to be packed. +				 * Initialized to avoid compiler warning. */ +    char cmd;			/* Current format character. */ +    int count;			/* Count associated with current format +				 * character. */ +    int flags;			/* Format field flags */ +    const char *format;	/* Pointer to current position in format +				 * string. */ +    Tcl_Obj *resultPtr = NULL;	/* Object holding result buffer. */ +    unsigned char *buffer;	/* Start of result buffer. */ +    const char *errorString; +    const char *str; +    int offset, size, length; + +    int i; +    Tcl_Obj *valuePtr, *elementPtr; +    Tcl_HashTable numberCacheHash; +    Tcl_HashTable *numberCachePtr; + +    if (objc < 3) { +	Tcl_WrongNumArgs(interp, 1, objv, +		"value formatString ?varName ...?"); +	return TCL_ERROR; +    } +    numberCachePtr = &numberCacheHash; +    Tcl_InitHashTable(numberCachePtr, TCL_ONE_WORD_KEYS); +    buffer = Tcl_GetByteArrayFromObj(objv[1], &length); +    format = TclGetString(objv[2]); +    arg = 3; +    offset = 0; +    while (*format != '\0') { +	str = format; +	flags = 0; +	if (!GetFormatSpec(&format, &cmd, &count, &flags)) { +	    goto done; +	} +	switch (cmd) { +	case 'a': +	case 'A': { +	    unsigned char *src; + +	    if (arg >= objc) { +		DeleteScanNumberCache(numberCachePtr); +		goto badIndex;  	    } -	    buffer = Tcl_GetByteArrayFromObj(objv[2], &length); -	    format = Tcl_GetString(objv[3]); -	    cursor = buffer; -	    arg = 4; -	    offset = 0; -	    while (*format != '\0') { -		str = format; -		if (!GetFormatSpec(&format, &cmd, &count)) { +	    if (count == BINARY_ALL) { +		count = length - offset; +	    } else { +		if (count == BINARY_NOCOUNT) { +		    count = 1; +		} +		if (count > (length - offset)) {  		    goto done;  		} -		switch (cmd) { -		    case 'a': -		    case 'A': { -			unsigned char *src; - -			if (arg >= objc) { -			    goto badIndex; -			} -			if (count == BINARY_ALL) { -			    count = length - offset; -			} else { -			    if (count == BINARY_NOCOUNT) { -				count = 1; -			    } -			    if (count > (length - offset)) { -				goto done; -			    } -			} +	    } -			src = buffer + offset; -			size = count; +	    src = buffer + offset; +	    size = count; -			/* -			 * Trim trailing nulls and spaces, if necessary. -			 */ +	    /* +	     * Trim trailing nulls and spaces, if necessary. +	     */ -			if (cmd == 'A') { -			    while (size > 0) { -				if (src[size-1] != '\0' && src[size-1] != ' ') { -				    break; -				} -				size--; -			    } -			} -			valuePtr = Tcl_NewByteArrayObj(src, size); -			resultPtr = Tcl_ObjSetVar2(interp, objv[arg], -				NULL, valuePtr, TCL_LEAVE_ERR_MSG); -			arg++; -			if (resultPtr == NULL) { -			    Tcl_DecrRefCount(valuePtr);	/* unneeded */ -			    return TCL_ERROR; -			} -			offset += count; +	    if (cmd == 'A') { +		while (size > 0) { +		    if (src[size-1] != '\0' && src[size-1] != ' ') {  			break;  		    } -		    case 'b': -		    case 'B': { -			unsigned char *src; -			char *dest; +		    size--; +		} +	    } -			if (arg >= objc) { -			    goto badIndex; -			} -			if (count == BINARY_ALL) { -			    count = (length - offset) * 8; -			} else { -			    if (count == BINARY_NOCOUNT) { -				count = 1; -			    } -			    if (count > (length - offset) * 8) { -				goto done; -			    } -			} -			src = buffer + offset; -			valuePtr = Tcl_NewObj(); -			Tcl_SetObjLength(valuePtr, count); -			dest = Tcl_GetString(valuePtr); - -			if (cmd == 'b') { -			    for (i = 0; i < count; i++) { -				if (i % 8) { -				    value >>= 1; -				} else { -				    value = *src++; -				} -				*dest++ = (char) ((value & 1) ? '1' : '0'); -			    } -			} else { -			    for (i = 0; i < count; i++) { -				if (i % 8) { -				    value <<= 1; -				} else { -				    value = *src++; -				} -				*dest++ = (char) ((value & 0x80) ? '1' : '0'); -			    } -			} -			 -			resultPtr = Tcl_ObjSetVar2(interp, objv[arg], -				NULL, valuePtr, TCL_LEAVE_ERR_MSG); -			arg++; -			if (resultPtr == NULL) { -			    Tcl_DecrRefCount(valuePtr);	/* unneeded */ -			    return TCL_ERROR; -			} -			offset += (count + 7 ) / 8; -			break; -		    } -		    case 'h': -		    case 'H': { -			char *dest; -			unsigned char *src; -			int i; -			static char hexdigit[] = "0123456789abcdef"; - -			if (arg >= objc) { -			    goto badIndex; -			} -			if (count == BINARY_ALL) { -			    count = (length - offset)*2; -			} else { -			    if (count == BINARY_NOCOUNT) { -				count = 1; -			    } -			    if (count > (length - offset)*2) { -				goto done; -			    } -			} -			src = buffer + offset; -			valuePtr = Tcl_NewObj(); -			Tcl_SetObjLength(valuePtr, count); -			dest = Tcl_GetString(valuePtr); - -			if (cmd == 'h') { -			    for (i = 0; i < count; i++) { -				if (i % 2) { -				    value >>= 4; -				} else { -				    value = *src++; -				} -				*dest++ = hexdigit[value & 0xf]; -			    } -			} else { -			    for (i = 0; i < count; i++) { -				if (i % 2) { -				    value <<= 4; -				} else { -				    value = *src++; -				} -				*dest++ = hexdigit[(value >> 4) & 0xf]; -			    } -			} -			 -			resultPtr = Tcl_ObjSetVar2(interp, objv[arg], -				NULL, valuePtr, TCL_LEAVE_ERR_MSG); -			arg++; -			if (resultPtr == NULL) { -			    Tcl_DecrRefCount(valuePtr);	/* unneeded */ -			    return TCL_ERROR; -			} -			offset += (count + 1) / 2; -			break; -		    } -		    case 'c': { -			size = 1; -			goto scanNumber; -		    } -		    case 's': -		    case 'S': { -			size = 2; -			goto scanNumber; -		    } -		    case 'i': -		    case 'I': { -			size = 4; -			goto scanNumber; -		    } -		    case 'f': { -			size = sizeof(float); -			goto scanNumber; -		    } -		    case 'd': { -			unsigned char *src; - -			size = sizeof(double); -			/* fall through */ -			 -			scanNumber: -			if (arg >= objc) { -			    goto badIndex; -			} -			if (count == BINARY_NOCOUNT) { -			    if ((length - offset) < size) { -				goto done; -			    } -			    valuePtr = ScanNumber(buffer+offset, cmd); -			    offset += size; -			} else { -			    if (count == BINARY_ALL) { -				count = (length - offset) / size; -			    } -			    if ((length - offset) < (count * size)) { -				goto done; -			    } -			    valuePtr = Tcl_NewObj(); -			    src = buffer+offset; -			    for (i = 0; i < count; i++) { -				elementPtr = ScanNumber(src, cmd); -				src += size; -				Tcl_ListObjAppendElement(NULL, valuePtr, -					elementPtr); -			    } -			    offset += count*size; -			} +	    /* +	     * Have to do this #ifdef-fery because (as part of defining +	     * Tcl_NewByteArrayObj) we removed the #def that hides this stuff +	     * normally. If this code ever gets copied to another file, it +	     * should be changed back to the simpler version. +	     */ -			resultPtr = Tcl_ObjSetVar2(interp, objv[arg], -				NULL, valuePtr, TCL_LEAVE_ERR_MSG); -			arg++; -			if (resultPtr == NULL) { -			    Tcl_DecrRefCount(valuePtr);	/* unneeded */ -			    return TCL_ERROR; -			} -			break; -		    } -		    case 'x': { -			if (count == BINARY_NOCOUNT) { -			    count = 1; -			} -			if ((count == BINARY_ALL) -				|| (count > (length - offset))) { -			    offset = length; -			} else { -			    offset += count; -			} -			break; +#ifdef TCL_MEM_DEBUG +	    valuePtr = Tcl_DbNewByteArrayObj(src, size, __FILE__, __LINE__); +#else +	    valuePtr = Tcl_NewByteArrayObj(src, size); +#endif /* TCL_MEM_DEBUG */ + +	    resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, +		    TCL_LEAVE_ERR_MSG); +	    arg++; +	    if (resultPtr == NULL) { +		DeleteScanNumberCache(numberCachePtr); +		return TCL_ERROR; +	    } +	    offset += count; +	    break; +	} +	case 'b': +	case 'B': { +	    unsigned char *src; +	    char *dest; + +	    if (arg >= objc) { +		DeleteScanNumberCache(numberCachePtr); +		goto badIndex; +	    } +	    if (count == BINARY_ALL) { +		count = (length - offset) * 8; +	    } else { +		if (count == BINARY_NOCOUNT) { +		    count = 1; +		} +		if (count > (length - offset) * 8) { +		    goto done; +		} +	    } +	    src = buffer + offset; +	    valuePtr = Tcl_NewObj(); +	    Tcl_SetObjLength(valuePtr, count); +	    dest = TclGetString(valuePtr); + +	    if (cmd == 'b') { +		for (i = 0; i < count; i++) { +		    if (i % 8) { +			value >>= 1; +		    } else { +			value = *src++;  		    } -		    case 'X': { -			if (count == BINARY_NOCOUNT) { -			    count = 1; -			} -			if ((count == BINARY_ALL) || (count > offset)) { -			    offset = 0; -			} else { -			    offset -= count; -			} -			break; +		    *dest++ = (char) ((value & 1) ? '1' : '0'); +		} +	    } else { +		for (i = 0; i < count; i++) { +		    if (i % 8) { +			value <<= 1; +		    } else { +			value = *src++;  		    } -		    case '@': { -			if (count == BINARY_NOCOUNT) { -			    goto badCount; -			} -			if ((count == BINARY_ALL) || (count > length)) { -			    offset = length; -			} else { -			    offset = count; -			} -			break; +		    *dest++ = (char) ((value & 0x80) ? '1' : '0'); +		} +	    } + +	    resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, +		    TCL_LEAVE_ERR_MSG); +	    arg++; +	    if (resultPtr == NULL) { +		DeleteScanNumberCache(numberCachePtr); +		return TCL_ERROR; +	    } +	    offset += (count + 7) / 8; +	    break; +	} +	case 'h': +	case 'H': { +	    char *dest; +	    unsigned char *src; +	    static const char hexdigit[] = "0123456789abcdef"; + +	    if (arg >= objc) { +		DeleteScanNumberCache(numberCachePtr); +		goto badIndex; +	    } +	    if (count == BINARY_ALL) { +		count = (length - offset)*2; +	    } else { +		if (count == BINARY_NOCOUNT) { +		    count = 1; +		} +		if (count > (length - offset)*2) { +		    goto done; +		} +	    } +	    src = buffer + offset; +	    valuePtr = Tcl_NewObj(); +	    Tcl_SetObjLength(valuePtr, count); +	    dest = TclGetString(valuePtr); + +	    if (cmd == 'h') { +		for (i = 0; i < count; i++) { +		    if (i % 2) { +			value >>= 4; +		    } else { +			value = *src++;  		    } -		    default: { -			errorString = str; -			goto badfield; +		    *dest++ = hexdigit[value & 0xf]; +		} +	    } else { +		for (i = 0; i < count; i++) { +		    if (i % 2) { +			value <<= 4; +		    } else { +			value = *src++;  		    } +		    *dest++ = hexdigit[(value >> 4) & 0xf];  		}  	    } -	    /* -	     * Set the result to the last position of the cursor. -	     */ +	    resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, +		    TCL_LEAVE_ERR_MSG); +	    arg++; +	    if (resultPtr == NULL) { +		DeleteScanNumberCache(numberCachePtr); +		return TCL_ERROR; +	    } +	    offset += (count + 1) / 2; +	    break; +	} +	case 'c': +	    size = 1; +	    goto scanNumber; +	case 't': +	case 's': +	case 'S': +	    size = 2; +	    goto scanNumber; +	case 'n': +	case 'i': +	case 'I': +	    size = 4; +	    goto scanNumber; +	case 'm': +	case 'w': +	case 'W': +	    size = 8; +	    goto scanNumber; +	case 'r': +	case 'R': +	case 'f': +	    size = sizeof(float); +	    goto scanNumber; +	case 'q': +	case 'Q': +	case 'd': { +	    unsigned char *src; + +	    size = sizeof(double); +	    /* fall through */ -	    done: -	    Tcl_ResetResult(interp); -	    Tcl_SetLongObj(Tcl_GetObjResult(interp), arg - 4); +	scanNumber: +	    if (arg >= objc) { +		DeleteScanNumberCache(numberCachePtr); +		goto badIndex; +	    } +	    if (count == BINARY_NOCOUNT) { +		if ((length - offset) < size) { +		    goto done; +		} +		valuePtr = ScanNumber(buffer+offset, cmd, flags, +			&numberCachePtr); +		offset += size; +	    } else { +		if (count == BINARY_ALL) { +		    count = (length - offset) / size; +		} +		if ((length - offset) < (count * size)) { +		    goto done; +		} +		valuePtr = Tcl_NewObj(); +		src = buffer + offset; +		for (i = 0; i < count; i++) { +		    elementPtr = ScanNumber(src, cmd, flags, &numberCachePtr); +		    src += size; +		    Tcl_ListObjAppendElement(NULL, valuePtr, elementPtr); +		} +		offset += count * size; +	    } + +	    resultPtr = Tcl_ObjSetVar2(interp, objv[arg], NULL, valuePtr, +		    TCL_LEAVE_ERR_MSG); +	    arg++; +	    if (resultPtr == NULL) { +		DeleteScanNumberCache(numberCachePtr); +		return TCL_ERROR; +	    } +	    break; +	} +	case 'x': +	    if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    if ((count == BINARY_ALL) || (count > (length - offset))) { +		offset = length; +	    } else { +		offset += count; +	    } +	    break; +	case 'X': +	    if (count == BINARY_NOCOUNT) { +		count = 1; +	    } +	    if ((count == BINARY_ALL) || (count > offset)) { +		offset = 0; +	    } else { +		offset -= count; +	    }  	    break; +	case '@': +	    if (count == BINARY_NOCOUNT) { +		DeleteScanNumberCache(numberCachePtr); +		goto badCount; +	    } +	    if ((count == BINARY_ALL) || (count > length)) { +		offset = length; +	    } else { +		offset = count; +	    } +	    break; +	default: +	    DeleteScanNumberCache(numberCachePtr); +	    errorString = str; +	    goto badField;  	}      } -    return TCL_OK; -    badValue: -    Tcl_ResetResult(interp); -    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "expected ", errorString, -	    " string but got \"", errorValue, "\" instead", NULL); -    return TCL_ERROR; +    /* +     * Set the result to the last position of the cursor. +     */ + + done: +    Tcl_SetObjResult(interp, Tcl_NewLongObj(arg - 3)); +    DeleteScanNumberCache(numberCachePtr); + +    return TCL_OK; -    badCount: + badCount:      errorString = "missing count for \"@\" field specifier";      goto error; -    badIndex: + badIndex:      errorString = "not enough arguments for all format specifiers";      goto error; -    badfield: { + badField: +    {  	Tcl_UniChar ch;  	char buf[TCL_UTF_MAX + 1];  	Tcl_UtfToUniChar(errorString, &ch);  	buf[Tcl_UniCharToUtf(ch, buf)] = '\0'; -	Tcl_AppendResult(interp, "bad field specifier \"", buf, "\"", NULL); +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"bad field specifier \"%s\"", buf));  	return TCL_ERROR;      } -    error: -    Tcl_AppendResult(interp, errorString, NULL); + error: +    Tcl_SetObjResult(interp, Tcl_NewStringObj(errorString, -1));      return TCL_ERROR;  } @@ -1313,15 +1600,15 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)   *   * GetFormatSpec --   * - *	This function parses the format strings used in the binary - *	format and scan commands. + *	This function parses the format strings used in the binary format and + *	scan commands.   *   * Results: - *	Moves the formatPtr to the start of the next command. Returns - *	the current command character and count in cmdPtr and countPtr. - *	The count is set to BINARY_ALL if the count character was '*' - *	or BINARY_NOCOUNT if no count was specified.  Returns 1 on - *	success, or 0 if the string did not have a format specifier. + *	Moves the formatPtr to the start of the next command. Returns the + *	current command character and count in cmdPtr and countPtr. The count + *	is set to BINARY_ALL if the count character was '*' or BINARY_NOCOUNT + *	if no count was specified. Returns 1 on success, or 0 if the string + *	did not have a format specifier.   *   * Side effects:   *	None. @@ -1330,10 +1617,11 @@ Tcl_BinaryObjCmd(dummy, interp, objc, objv)   */  static int -GetFormatSpec(formatPtr, cmdPtr, countPtr) -    char **formatPtr;		/* Pointer to format string. */ -    char *cmdPtr;		/* Pointer to location of command char. */ -    int *countPtr;		/* Pointer to repeat count value. */ +GetFormatSpec( +    const char **formatPtr,	/* Pointer to format string. */ +    char *cmdPtr,		/* Pointer to location of command char. */ +    int *countPtr,		/* Pointer to repeat count value. */ +    int *flagsPtr)		/* Pointer to field flags */  {      /*       * Skip any leading blanks. @@ -1357,13 +1645,17 @@ GetFormatSpec(formatPtr, cmdPtr, countPtr)      *cmdPtr = **formatPtr;      (*formatPtr)++; +    if (**formatPtr == 'u') { +	(*formatPtr)++; +	*flagsPtr |= BINARY_UNSIGNED; +    }      if (**formatPtr == '*') {  	(*formatPtr)++; -	(*countPtr) = BINARY_ALL; +	*countPtr = BINARY_ALL;      } else if (isdigit(UCHAR(**formatPtr))) { /* INTL: digit */ -	(*countPtr) = strtoul(*formatPtr, formatPtr, 10); +	*countPtr = strtoul(*formatPtr, (char **) formatPtr, 10);      } else { -	(*countPtr) = BINARY_NOCOUNT; +	*countPtr = BINARY_NOCOUNT;      }      return 1;  } @@ -1371,13 +1663,193 @@ GetFormatSpec(formatPtr, cmdPtr, countPtr)  /*   *----------------------------------------------------------------------   * + * NeedReversing -- + * + *	This routine determines, if bytes of a number need to be re-ordered, + *	and returns a numeric code indicating the re-ordering to be done. + *	This depends on the endiannes of the machine and the desired format. + *	It is in effect a table (whose contents depend on the endianness of + *	the system) describing whether a value needs reversing or not. Anyone + *	porting the code to a big-endian platform should take care to make + *	sure that they define WORDS_BIGENDIAN though this is already done by + *	configure for the Unix build; little-endian platforms (including + *	Windows) don't need to do anything. + * + * Results: + *	0	No re-ordering needed. + *	1	Reverse the bytes:	01234567 <-> 76543210 (little to big) + *	2	Apply this re-ordering: 01234567 <-> 45670123 (Nokia to little) + *	3	Apply this re-ordering: 01234567 <-> 32107654 (Nokia to big) + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +static int +NeedReversing( +    int format) +{ +    switch (format) { +	/* native floats and doubles: never reverse */ +    case 'd': +    case 'f': +	/* big endian ints: never reverse */ +    case 'I': +    case 'S': +    case 'W': +#ifdef WORDS_BIGENDIAN +	/* native ints: reverse if we're little-endian */ +    case 'n': +    case 't': +    case 'm': +	/* f: reverse if we're little-endian */ +    case 'Q': +    case 'R': +#else /* !WORDS_BIGENDIAN */ +	/* small endian floats: reverse if we're big-endian */ +    case 'r': +#endif /* WORDS_BIGENDIAN */ +	return 0; + +#ifdef WORDS_BIGENDIAN +	/* small endian floats: reverse if we're big-endian */ +    case 'q': +    case 'r': +#else /* !WORDS_BIGENDIAN */ +	/* native ints: reverse if we're little-endian */ +    case 'n': +    case 't': +    case 'm': +	/* f: reverse if we're little-endian */ +    case 'R': +#endif /* WORDS_BIGENDIAN */ +	/* small endian ints: always reverse */ +    case 'i': +    case 's': +    case 'w': +	return 1; + +#ifndef WORDS_BIGENDIAN +    /* +     * The Q and q formats need special handling to account for the unusual +     * byte ordering of 8-byte floats on Nokia 770 systems, which claim to be +     * little-endian, but also reverse word order. +     */ + +    case 'Q': +	if (TclNokia770Doubles()) { +	    return 3; +	} +	return 1; +    case 'q': +	if (TclNokia770Doubles()) { +	    return 2; +	} +	return 0; +#endif +    } + +    Tcl_Panic("unexpected fallthrough"); +    return 0; +} + +/* + *---------------------------------------------------------------------- + * + * CopyNumber -- + * + *	This routine is called by FormatNumber and ScanNumber to copy a + *	floating-point number. If required, bytes are reversed while copying. + *	The behaviour is only fully defined when used with IEEE float and + *	double values (guaranteed to be 4 and 8 bytes long, respectively.) + * + * Results: + *	None + * + * Side effects: + *	Copies length bytes + * + *---------------------------------------------------------------------- + */ + +static void +CopyNumber( +    const void *from,		/* source */ +    void *to,			/* destination */ +    unsigned length,		/* Number of bytes to copy */ +    int type)			/* What type of thing are we copying? */ +{ +    switch (NeedReversing(type)) { +    case 0: +	memcpy(to, from, length); +	break; +    case 1: { +	const unsigned char *fromPtr = from; +	unsigned char *toPtr = to; + +	switch (length) { +	case 4: +	    toPtr[0] = fromPtr[3]; +	    toPtr[1] = fromPtr[2]; +	    toPtr[2] = fromPtr[1]; +	    toPtr[3] = fromPtr[0]; +	    break; +	case 8: +	    toPtr[0] = fromPtr[7]; +	    toPtr[1] = fromPtr[6]; +	    toPtr[2] = fromPtr[5]; +	    toPtr[3] = fromPtr[4]; +	    toPtr[4] = fromPtr[3]; +	    toPtr[5] = fromPtr[2]; +	    toPtr[6] = fromPtr[1]; +	    toPtr[7] = fromPtr[0]; +	    break; +	} +	break; +    } +    case 2: { +	const unsigned char *fromPtr = from; +	unsigned char *toPtr = to; + +	toPtr[0] = fromPtr[4]; +	toPtr[1] = fromPtr[5]; +	toPtr[2] = fromPtr[6]; +	toPtr[3] = fromPtr[7]; +	toPtr[4] = fromPtr[0]; +	toPtr[5] = fromPtr[1]; +	toPtr[6] = fromPtr[2]; +	toPtr[7] = fromPtr[3]; +	break; +    } +    case 3: { +	const unsigned char *fromPtr = from; +	unsigned char *toPtr = to; + +	toPtr[0] = fromPtr[3]; +	toPtr[1] = fromPtr[2]; +	toPtr[2] = fromPtr[1]; +	toPtr[3] = fromPtr[0]; +	toPtr[4] = fromPtr[7]; +	toPtr[5] = fromPtr[6]; +	toPtr[6] = fromPtr[5]; +	toPtr[7] = fromPtr[4]; +	break; +    } +    } +} + +/* + *---------------------------------------------------------------------- + *   * FormatNumber --   * - *	This routine is called by Tcl_BinaryObjCmd to format a number - *	into a location pointed at by cursor. + *	This routine is called by Tcl_BinaryObjCmd to format a number into a + *	location pointed at by cursor.   *   * Results: - *	 A standard Tcl result. + *	A standard Tcl result.   *   * Side effects:   *	Moves the cursor to the next location to be written into. @@ -1386,70 +1858,153 @@ GetFormatSpec(formatPtr, cmdPtr, countPtr)   */  static int -FormatNumber(interp, type, src, cursorPtr) -    Tcl_Interp *interp;		/* Current interpreter, used to report +FormatNumber( +    Tcl_Interp *interp,		/* Current interpreter, used to report  				 * errors. */ -    int type;			/* Type of number to format. */ -    Tcl_Obj *src;		/* Number to format. */ -    unsigned char **cursorPtr;	/* Pointer to index into destination buffer. */ +    int type,			/* Type of number to format. */ +    Tcl_Obj *src,		/* Number to format. */ +    unsigned char **cursorPtr)	/* Pointer to index into destination buffer. */  { -    int value; +    long value;      double dvalue; +    Tcl_WideInt wvalue; +    float fvalue; -    if ((type == 'd') || (type == 'f')) { +    switch (type) { +    case 'd': +    case 'q': +    case 'Q':  	/* -	 * For floating point types, we need to copy the data using -	 * memcpy to avoid alignment issues. +	 * Double-precision floating point values. Tcl_GetDoubleFromObj +	 * returns TCL_ERROR for NaN, but we can check by comparing the +	 * object's type pointer.  	 */  	if (Tcl_GetDoubleFromObj(interp, src, &dvalue) != TCL_OK) { -	    return TCL_ERROR; +	    if (src->typePtr != &tclDoubleType) { +		return TCL_ERROR; +	    } +	    dvalue = src->internalRep.doubleValue;  	} -	if (type == 'd') { -	    memcpy((VOID *) *cursorPtr, (VOID *) &dvalue, sizeof(double)); -	    *cursorPtr += sizeof(double); -	} else { -	    float fvalue; +	CopyNumber(&dvalue, *cursorPtr, sizeof(double), type); +	*cursorPtr += sizeof(double); +	return TCL_OK; -	    /* -	     * Because some compilers will generate floating point exceptions -	     * on an overflow cast (e.g. Borland), we restrict the values -	     * to the valid range for float. -	     */ +    case 'f': +    case 'r': +    case 'R': +	/* +	 * Single-precision floating point values. Tcl_GetDoubleFromObj +	 * returns TCL_ERROR for NaN, but we can check by comparing the +	 * object's type pointer. +	 */ -	    if (fabs(dvalue) > (double)FLT_MAX) { -		fvalue = (dvalue >= 0.0) ? FLT_MAX : -FLT_MAX; -	    } else { -		fvalue = (float) dvalue; +	if (Tcl_GetDoubleFromObj(interp, src, &dvalue) != TCL_OK) { +	    if (src->typePtr != &tclDoubleType) { +		return TCL_ERROR;  	    } -	    memcpy((VOID *) *cursorPtr, (VOID *) &fvalue, sizeof(float)); -	    *cursorPtr += sizeof(float); +	    dvalue = src->internalRep.doubleValue;  	} -    } else { -	if (Tcl_GetIntFromObj(interp, src, &value) != TCL_OK) { + +	/* +	 * Because some compilers will generate floating point exceptions on +	 * an overflow cast (e.g. Borland), we restrict the values to the +	 * valid range for float. +	 */ + +	if (fabs(dvalue) > (double)FLT_MAX) { +	    fvalue = (dvalue >= 0.0) ? FLT_MAX : -FLT_MAX; +	} else { +	    fvalue = (float) dvalue; +	} +	CopyNumber(&fvalue, *cursorPtr, sizeof(float), type); +	*cursorPtr += sizeof(float); +	return TCL_OK; + +	/* +	 * 64-bit integer values. +	 */ +    case 'w': +    case 'W': +    case 'm': +	if (Tcl_GetWideIntFromObj(interp, src, &wvalue) != TCL_OK) { +	    return TCL_ERROR; +	} +	if (NeedReversing(type)) { +	    *(*cursorPtr)++ = UCHAR(wvalue); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 8); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 16); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 24); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 32); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 40); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 48); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 56); +	} else { +	    *(*cursorPtr)++ = UCHAR(wvalue >> 56); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 48); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 40); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 32); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 24); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 16); +	    *(*cursorPtr)++ = UCHAR(wvalue >> 8); +	    *(*cursorPtr)++ = UCHAR(wvalue); +	} +	return TCL_OK; + +	/* +	 * 32-bit integer values. +	 */ +    case 'i': +    case 'I': +    case 'n': +	if (TclGetLongFromObj(interp, src, &value) != TCL_OK) {  	    return TCL_ERROR;  	} -	if (type == 'c') { -	    *(*cursorPtr)++ = (unsigned char) value; -	} else if (type == 's') { -	    *(*cursorPtr)++ = (unsigned char) value; -	    *(*cursorPtr)++ = (unsigned char) (value >> 8); -	} else if (type == 'S') { -	    *(*cursorPtr)++ = (unsigned char) (value >> 8); -	    *(*cursorPtr)++ = (unsigned char) value; -	} else if (type == 'i') { -	    *(*cursorPtr)++ = (unsigned char) value; -	    *(*cursorPtr)++ = (unsigned char) (value >> 8); -	    *(*cursorPtr)++ = (unsigned char) (value >> 16); -	    *(*cursorPtr)++ = (unsigned char) (value >> 24); -	} else if (type == 'I') { -	    *(*cursorPtr)++ = (unsigned char) (value >> 24); -	    *(*cursorPtr)++ = (unsigned char) (value >> 16); -	    *(*cursorPtr)++ = (unsigned char) (value >> 8); -	    *(*cursorPtr)++ = (unsigned char) value; +	if (NeedReversing(type)) { +	    *(*cursorPtr)++ = UCHAR(value); +	    *(*cursorPtr)++ = UCHAR(value >> 8); +	    *(*cursorPtr)++ = UCHAR(value >> 16); +	    *(*cursorPtr)++ = UCHAR(value >> 24); +	} else { +	    *(*cursorPtr)++ = UCHAR(value >> 24); +	    *(*cursorPtr)++ = UCHAR(value >> 16); +	    *(*cursorPtr)++ = UCHAR(value >> 8); +	    *(*cursorPtr)++ = UCHAR(value); +	} +	return TCL_OK; + +	/* +	 * 16-bit integer values. +	 */ +    case 's': +    case 'S': +    case 't': +	if (TclGetLongFromObj(interp, src, &value) != TCL_OK) { +	    return TCL_ERROR; +	} +	if (NeedReversing(type)) { +	    *(*cursorPtr)++ = UCHAR(value); +	    *(*cursorPtr)++ = UCHAR(value >> 8); +	} else { +	    *(*cursorPtr)++ = UCHAR(value >> 8); +	    *(*cursorPtr)++ = UCHAR(value);  	} +	return TCL_OK; + +	/* +	 * 8-bit integer values. +	 */ +    case 'c': +	if (TclGetLongFromObj(interp, src, &value) != TCL_OK) { +	    return TCL_ERROR; +	} +	*(*cursorPtr)++ = UCHAR(value); +	return TCL_OK; + +    default: +	Tcl_Panic("unexpected fallthrough"); +	return TCL_ERROR;      } -    return TCL_OK;  }  /* @@ -1457,95 +2012,977 @@ FormatNumber(interp, type, src, cursorPtr)   *   * ScanNumber --   * - *	This routine is called by Tcl_BinaryObjCmd to scan a number - *	out of a buffer. + *	This routine is called by Tcl_BinaryObjCmd to scan a number out of a + *	buffer.   *   * Results: - *	Returns a newly created object containing the scanned number. - *	This object has a ref count of zero. + *	Returns a newly created object containing the scanned number. This + *	object has a ref count of zero.   *   * Side effects: - *	None. + *	Might reuse an object in the number cache, place a new object in the + *	cache, or delete the cache and set the reference to it (itself passed + *	in by reference) to NULL.   *   *----------------------------------------------------------------------   */  static Tcl_Obj * -ScanNumber(buffer, type) -    unsigned char *buffer;	/* Buffer to scan number from. */ -    int type;			/* Format character from "binary scan" */ +ScanNumber( +    unsigned char *buffer,	/* Buffer to scan number from. */ +    int type,			/* Format character from "binary scan" */ +    int flags,			/* Format field flags */ +    Tcl_HashTable **numberCachePtrPtr) +				/* Place to look for cache of scanned +				 * value objects, or NULL if too many +				 * different numbers have been scanned. */  {      long value; +    float fvalue; +    double dvalue; +    Tcl_WideUInt uwvalue;      /*       * We cannot rely on the compiler to properly sign extend integer values       * when we cast from smaller values to larger values because we don't know -     * the exact size of the integer types.  So, we have to handle sign +     * the exact size of the integer types. So, we have to handle sign       * extension explicitly by checking the high bit and padding with 1's as -     * needed. +     * needed. This practice is disabled if the BINARY_UNSIGNED flag is set.       */      switch (type) { -	case 'c': { -	    /* -	     * Characters need special handling.  We want to produce a -	     * signed result, but on some platforms (such as AIX) chars -	     * are unsigned.  To deal with this, check for a value that -	     * should be negative but isn't. -	     */ +    case 'c': +	/* +	 * Characters need special handling. We want to produce a signed +	 * result, but on some platforms (such as AIX) chars are unsigned. To +	 * deal with this, check for a value that should be negative but +	 * isn't. +	 */ -	    value = buffer[0]; +	value = buffer[0]; +	if (!(flags & BINARY_UNSIGNED)) {  	    if (value & 0x80) {  		value |= -0x100;  	    } -	    return Tcl_NewLongObj((long)value);  	} -	case 's': { +	goto returnNumericObject; + +	/* +	 * 16-bit numeric values. We need the sign extension trick (see above) +	 * here as well. +	 */ + +    case 's': +    case 'S': +    case 't': +	if (NeedReversing(type)) {  	    value = (long) (buffer[0] + (buffer[1] << 8)); -	    goto shortValue; -	} -	case 'S': { +	} else {  	    value = (long) (buffer[1] + (buffer[0] << 8)); -	    shortValue: +	} +	if (!(flags & BINARY_UNSIGNED)) {  	    if (value & 0x8000) {  		value |= -0x10000;  	    } -	    return Tcl_NewLongObj(value);  	} -	case 'i': { -	    value = (long) (buffer[0]  +	goto returnNumericObject; + +	/* +	 * 32-bit numeric values. +	 */ + +    case 'i': +    case 'I': +    case 'n': +	if (NeedReversing(type)) { +	    value = (long) (buffer[0]  		    + (buffer[1] << 8)  		    + (buffer[2] << 16) -		    + (buffer[3] << 24)); -	    goto intValue; -	} -	case 'I': { +		    + (((long)buffer[3]) << 24)); +	} else {  	    value = (long) (buffer[3]  		    + (buffer[2] << 8)  		    + (buffer[1] << 16) -		    + (buffer[0] << 24)); -	    intValue: +		    + (((long) buffer[0]) << 24)); +	} + +	/* +	 * Check to see if the value was sign extended properly on systems +	 * where an int is more than 32-bits. +	 * We avoid caching unsigned integers as we cannot distinguish between +	 * 32bit signed and unsigned in the hash (short and char are ok). +	 */ + +	if (flags & BINARY_UNSIGNED) { +	    return Tcl_NewWideIntObj((Tcl_WideInt)(unsigned long)value); +	} +	if ((value & (((unsigned) 1)<<31)) && (value > 0)) { +	    value -= (((unsigned) 1)<<31); +	    value -= (((unsigned) 1)<<31); +	} + +    returnNumericObject: +	if (*numberCachePtrPtr == NULL) { +	    return Tcl_NewLongObj(value); +	} else { +	    register Tcl_HashTable *tablePtr = *numberCachePtrPtr; +	    register Tcl_HashEntry *hPtr; +	    int isNew; + +	    hPtr = Tcl_CreateHashEntry(tablePtr, INT2PTR(value), &isNew); +	    if (!isNew) { +		return Tcl_GetHashValue(hPtr); +	    } +	    if (tablePtr->numEntries <= BINARY_SCAN_MAX_CACHE) { +		register Tcl_Obj *objPtr = Tcl_NewLongObj(value); + +		Tcl_IncrRefCount(objPtr); +		Tcl_SetHashValue(hPtr, objPtr); +		return objPtr; +	    } +  	    /* -	     * Check to see if the value was sign extended properly on -	     * systems where an int is more than 32-bits. +	     * We've overflowed the cache! Someone's parsing a LOT of varied +	     * binary data in a single call! Bail out by switching back to the +	     * old behaviour for the rest of the scan. +	     * +	     * Note that anyone just using the 'c' conversion (for bytes) +	     * cannot trigger this.  	     */ -	    if ((value & (((unsigned int)1)<<31)) && (value > 0)) { -		value -= (((unsigned int)1)<<31); -		value -= (((unsigned int)1)<<31); -	    } +	    DeleteScanNumberCache(tablePtr); +	    *numberCachePtrPtr = NULL;  	    return Tcl_NewLongObj(value);  	} -	case 'f': { -	    float fvalue; -	    memcpy((VOID *) &fvalue, (VOID *) buffer, sizeof(float)); -	    return Tcl_NewDoubleObj(fvalue); + +	/* +	 * Do not cache wide (64-bit) values; they are already too large to +	 * use as keys. +	 */ + +    case 'w': +    case 'W': +    case 'm': +	if (NeedReversing(type)) { +	    uwvalue = ((Tcl_WideUInt) buffer[0]) +		    | (((Tcl_WideUInt) buffer[1]) << 8) +		    | (((Tcl_WideUInt) buffer[2]) << 16) +		    | (((Tcl_WideUInt) buffer[3]) << 24) +		    | (((Tcl_WideUInt) buffer[4]) << 32) +		    | (((Tcl_WideUInt) buffer[5]) << 40) +		    | (((Tcl_WideUInt) buffer[6]) << 48) +		    | (((Tcl_WideUInt) buffer[7]) << 56); +	} else { +	    uwvalue = ((Tcl_WideUInt) buffer[7]) +		    | (((Tcl_WideUInt) buffer[6]) << 8) +		    | (((Tcl_WideUInt) buffer[5]) << 16) +		    | (((Tcl_WideUInt) buffer[4]) << 24) +		    | (((Tcl_WideUInt) buffer[3]) << 32) +		    | (((Tcl_WideUInt) buffer[2]) << 40) +		    | (((Tcl_WideUInt) buffer[1]) << 48) +		    | (((Tcl_WideUInt) buffer[0]) << 56);  	} -	case 'd': { -	    double dvalue; -	    memcpy((VOID *) &dvalue, (VOID *) buffer, sizeof(double)); -	    return Tcl_NewDoubleObj(dvalue); +	if (flags & BINARY_UNSIGNED) { +	    Tcl_Obj *bigObj = NULL; +	    mp_int big; + +	    TclBNInitBignumFromWideUInt(&big, uwvalue); +	    bigObj = Tcl_NewBignumObj(&big); +	    return bigObj;  	} +	return Tcl_NewWideIntObj((Tcl_WideInt) uwvalue); + +	/* +	 * Do not cache double values; they are already too large to use as +	 * keys and the values stored are utterly incompatible with the +	 * integer part of the cache. +	 */ + +	/* +	 * 32-bit IEEE single-precision floating point. +	 */ + +    case 'f': +    case 'R': +    case 'r': +	CopyNumber(buffer, &fvalue, sizeof(float), type); +	return Tcl_NewDoubleObj(fvalue); + +	/* +	 * 64-bit IEEE double-precision floating point. +	 */ + +    case 'd': +    case 'Q': +    case 'q': +	CopyNumber(buffer, &dvalue, sizeof(double), type); +	return Tcl_NewDoubleObj(dvalue);      }      return NULL;  } + +/* + *---------------------------------------------------------------------- + * + * DeleteScanNumberCache -- + * + *	Deletes the hash table acting as a scan number cache. + * + * Results: + *	None + * + * Side effects: + *	Decrements the reference counts of the objects in the cache. + * + *---------------------------------------------------------------------- + */ + +static void +DeleteScanNumberCache( +    Tcl_HashTable *numberCachePtr) +				/* Pointer to the hash table, or NULL (when +				 * the cache has already been deleted due to +				 * overflow.) */ +{ +    Tcl_HashEntry *hEntry; +    Tcl_HashSearch search; + +    if (numberCachePtr == NULL) { +	return; +    } + +    hEntry = Tcl_FirstHashEntry(numberCachePtr, &search); +    while (hEntry != NULL) { +	register Tcl_Obj *value = Tcl_GetHashValue(hEntry); + +	if (value != NULL) { +	    Tcl_DecrRefCount(value); +	} +	hEntry = Tcl_NextHashEntry(&search); +    } +    Tcl_DeleteHashTable(numberCachePtr); +} + +/* + * ---------------------------------------------------------------------- + * + * NOTES -- + * + *	Some measurements show that it is faster to use a table to to perform + *	uuencode and base64 value encoding than to calculate the output (at + *	least on intel P4 arch). + * + *	Conversely using a lookup table for the decoding is slower than just + *	calculating the values. We therefore use the fastest of each method. + * + *	Presumably this has to do with the size of the tables. The base64 + *	decode table is 255 bytes while the encode table is only 65 bytes. The + *	choice likely depends on CPU memory cache sizes. + */ + +/* + *---------------------------------------------------------------------- + * + * BinaryEncodeHex -- + * + *	Implement the [binary encode hex] binary encoding. clientData must be + *	a table to convert values to hexadecimal digits. + * + * Results: + *	Interp result set to an encoded byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +static int +BinaryEncodeHex( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj = NULL; +    unsigned char *data = NULL; +    unsigned char *cursor = NULL; +    int offset = 0, count = 0; + +    if (objc != 2) { +	Tcl_WrongNumArgs(interp, 1, objv, "data"); +	return TCL_ERROR; +    } + +    TclNewObj(resultObj); +    data = Tcl_GetByteArrayFromObj(objv[1], &count); +    cursor = Tcl_SetByteArrayLength(resultObj, count * 2); +    for (offset = 0; offset < count; ++offset) { +	*cursor++ = HexDigits[((data[offset] >> 4) & 0x0f)]; +	*cursor++ = HexDigits[(data[offset] & 0x0f)]; +    } +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryDecodeHex -- + * + *	Implement the [binary decode hex] binary encoding. + * + * Results: + *	Interp result set to an decoded byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +static int +BinaryDecodeHex( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj = NULL; +    unsigned char *data, *datastart, *dataend; +    unsigned char *begin, *cursor, c; +    int i, index, value, size, count = 0, cut = 0, strict = 0; +    enum {OPT_STRICT }; +    static const char *const optStrings[] = { "-strict", NULL }; + +    if (objc < 2 || objc > 3) { +	Tcl_WrongNumArgs(interp, 1, objv, "?options? data"); +	return TCL_ERROR; +    } +    for (i = 1; i < objc-1; ++i) { +	if (Tcl_GetIndexFromObj(interp, objv[i], optStrings, "option", +		TCL_EXACT, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch (index) { +	case OPT_STRICT: +	    strict = 1; +	    break; +	} +    } + +    TclNewObj(resultObj); +    datastart = data = (unsigned char *) +	    TclGetStringFromObj(objv[objc-1], &count); +    dataend = data + count; +    size = (count + 1) / 2; +    begin = cursor = Tcl_SetByteArrayLength(resultObj, size); +    while (data < dataend) { +	value = 0; +	for (i=0 ; i<2 ; i++) { +	    if (data >= dataend) { +		value <<= 4; +		break; +	    } + +	    c = *data++; +	    if (!isxdigit((int) c)) { +		if (strict || !isspace(c)) { +		    goto badChar; +		} +		i--; +		continue; +	    } + +	    value <<= 4; +	    c -= '0'; +	    if (c > 9) { +		c += ('0' - 'A') + 10; +	    } +	    if (c > 16) { +		c += ('A' - 'a'); +	    } +	    value |= (c & 0xf); +	} +	if (i < 2) { +	    cut++; +	} +	*cursor++ = UCHAR(value); +	value = 0; +    } +    if (cut > size) { +	cut = size; +    } +    Tcl_SetByteArrayLength(resultObj, cursor - begin - cut); +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; + +  badChar: +    TclDecrRefCount(resultObj); +    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +	    "invalid hexadecimal digit \"%c\" at position %d", +	    c, (int) (data - datastart - 1))); +    return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryEncode64 -- + * + *	This implements a generic 6 bit binary encoding. Input is broken into + *	6 bit chunks and a lookup table passed in via clientData is used to + *	turn these values into output characters. This is used to implement + *	base64 binary encodings. + * + * Results: + *	Interp result set to an encoded byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +#define OUTPUT(c) \ +    do {						\ +	*cursor++ = (c);				\ +	outindex++;					\ +	if (maxlen > 0 && cursor != limit) {		\ +	    if (outindex == maxlen) {			\ +		memcpy(cursor, wrapchar, wrapcharlen);	\ +		cursor += wrapcharlen;			\ +		outindex = 0;				\ +	    }						\ +	}						\ +	if (cursor > limit) {				\ +	    Tcl_Panic("limit hit");			\ +	}						\ +    } while (0) + +static int +BinaryEncode64( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj; +    unsigned char *data, *cursor, *limit; +    int maxlen = 0; +    const char *wrapchar = "\n"; +    int wrapcharlen = 1; +    int offset, i, index, size, outindex = 0, count = 0; +    enum {OPT_MAXLEN, OPT_WRAPCHAR }; +    static const char *const optStrings[] = { "-maxlen", "-wrapchar", NULL }; + +    if (objc < 2 || objc%2 != 0) { +	Tcl_WrongNumArgs(interp, 1, objv, +		"?-maxlen len? ?-wrapchar char? data"); +	return TCL_ERROR; +    } +    for (i = 1; i < objc-1; i += 2) { +	if (Tcl_GetIndexFromObj(interp, objv[i], optStrings, "option", +		TCL_EXACT, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch (index) { +	case OPT_MAXLEN: +	    if (Tcl_GetIntFromObj(interp, objv[i+1], &maxlen) != TCL_OK) { +		return TCL_ERROR; +	    } +	    if (maxlen < 0) { +		Tcl_SetObjResult(interp, Tcl_NewStringObj( +			"line length out of range", -1)); +		Tcl_SetErrorCode(interp, "TCL", "BINARY", "ENCODE", +			"LINE_LENGTH", NULL); +		return TCL_ERROR; +	    } +	    break; +	case OPT_WRAPCHAR: +	    wrapchar = Tcl_GetStringFromObj(objv[i+1], &wrapcharlen); +	    if (wrapcharlen == 0) { +		maxlen = 0; +	    } +	    break; +	} +    } + +    resultObj = Tcl_NewObj(); +    data = Tcl_GetByteArrayFromObj(objv[objc-1], &count); +    if (count > 0) { +	size = (((count * 4) / 3) + 3) & ~3; /* ensure 4 byte chunks */ +	if (maxlen > 0 && size > maxlen) { +	    int adjusted = size + (wrapcharlen * (size / maxlen)); + +	    if (size % maxlen == 0) { +		adjusted -= wrapcharlen; +	    } +	    size = adjusted; +	} +	cursor = Tcl_SetByteArrayLength(resultObj, size); +	limit = cursor + size; +	for (offset = 0; offset < count; offset+=3) { +	    unsigned char d[3] = {0, 0, 0}; + +	    for (i = 0; i < 3 && offset+i < count; ++i) { +		d[i] = data[offset + i]; +	    } +	    OUTPUT(B64Digits[d[0] >> 2]); +	    OUTPUT(B64Digits[((d[0] & 0x03) << 4) | (d[1] >> 4)]); +	    if (offset+1 < count) { +		OUTPUT(B64Digits[((d[1] & 0x0f) << 2) | (d[2] >> 6)]); +	    } else { +		OUTPUT(B64Digits[64]); +	    } +	    if (offset+2 < count) { +		OUTPUT(B64Digits[d[2] & 0x3f]); +	    } else { +		OUTPUT(B64Digits[64]); +	    } +	} +    } +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; +} +#undef OUTPUT + +/* + *---------------------------------------------------------------------- + * + * BinaryEncodeUu -- + * + *	This implements the uuencode binary encoding. Input is broken into 6 + *	bit chunks and a lookup table is used to turn these values into output + *	characters. This differs from the generic code above in that line + *	lengths are also encoded. + * + * Results: + *	Interp result set to an encoded byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +static int +BinaryEncodeUu( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj; +    unsigned char *data, *start, *cursor; +    int offset, count, rawLength, n, i, j, bits, index; +    int lineLength = 61; +    const unsigned char SingleNewline[] = { (unsigned char) '\n' }; +    const unsigned char *wrapchar = SingleNewline; +    int wrapcharlen = sizeof(SingleNewline); +    enum { OPT_MAXLEN, OPT_WRAPCHAR }; +    static const char *const optStrings[] = { "-maxlen", "-wrapchar", NULL }; + +    if (objc < 2 || objc%2 != 0) { +	Tcl_WrongNumArgs(interp, 1, objv, +		"?-maxlen len? ?-wrapchar char? data"); +	return TCL_ERROR; +    } +    for (i = 1; i < objc-1; i += 2) { +	if (Tcl_GetIndexFromObj(interp, objv[i], optStrings, "option", +		TCL_EXACT, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch (index) { +	case OPT_MAXLEN: +	    if (Tcl_GetIntFromObj(interp, objv[i+1], &lineLength) != TCL_OK) { +		return TCL_ERROR; +	    } +	    if (lineLength < 3 || lineLength > 85) { +		Tcl_SetObjResult(interp, Tcl_NewStringObj( +			"line length out of range", -1)); +		Tcl_SetErrorCode(interp, "TCL", "BINARY", "ENCODE", +			"LINE_LENGTH", NULL); +		return TCL_ERROR; +	    } +	    break; +	case OPT_WRAPCHAR: +	    wrapchar = Tcl_GetByteArrayFromObj(objv[i+1], &wrapcharlen); +	    break; +	} +    } + +    /* +     * Allocate the buffer. This is a little bit too long, but is "good +     * enough". +     */ + +    resultObj = Tcl_NewObj(); +    offset = 0; +    data = Tcl_GetByteArrayFromObj(objv[objc-1], &count); +    rawLength = (lineLength - 1) * 3 / 4; +    start = cursor = Tcl_SetByteArrayLength(resultObj, +	    (lineLength + wrapcharlen) * +	    ((count + (rawLength - 1)) / rawLength)); +    n = bits = 0; + +    /* +     * Encode the data. Each output line first has the length of raw data +     * encoded by the output line described in it by one encoded byte, then +     * the encoded data follows (encoding each 6 bits as one character). +     * Encoded lines are always terminated by a newline. +     */ + +    while (offset < count) { +	int lineLen = count - offset; + +	if (lineLen > rawLength) { +	    lineLen = rawLength; +	} +	*cursor++ = UueDigits[lineLen]; +	for (i=0 ; i<lineLen ; i++) { +	    n <<= 8; +	    n |= data[offset++]; +	    for (bits += 8; bits > 6 ; bits -= 6) { +		*cursor++ = UueDigits[(n >> (bits-6)) & 0x3f]; +	    } +	} +	if (bits > 0) { +	    n <<= 8; +	    *cursor++ = UueDigits[(n >> (bits + 2)) & 0x3f]; +	    bits = 0; +	} +	for (j=0 ; j<wrapcharlen ; ++j) { +	    *cursor++ = wrapchar[j]; +	} +    } + +    /* +     * Fix the length of the output bytearray. +     */ + +    Tcl_SetByteArrayLength(resultObj, cursor-start); +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryDecodeUu -- + * + *	Decode a uuencoded string. + * + * Results: + *	Interp result set to an byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +static int +BinaryDecodeUu( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj = NULL; +    unsigned char *data, *datastart, *dataend; +    unsigned char *begin, *cursor; +    int i, index, size, count = 0, strict = 0, lineLen; +    unsigned char c; +    enum {OPT_STRICT }; +    static const char *const optStrings[] = { "-strict", NULL }; + +    if (objc < 2 || objc > 3) { +	Tcl_WrongNumArgs(interp, 1, objv, "?options? data"); +	return TCL_ERROR; +    } +    for (i = 1; i < objc-1; ++i) { +	if (Tcl_GetIndexFromObj(interp, objv[i], optStrings, "option", +		TCL_EXACT, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch (index) { +	case OPT_STRICT: +	    strict = 1; +	    break; +	} +    } + +    TclNewObj(resultObj); +    datastart = data = (unsigned char *) +	    TclGetStringFromObj(objv[objc-1], &count); +    dataend = data + count; +    size = ((count + 3) & ~3) * 3 / 4; +    begin = cursor = Tcl_SetByteArrayLength(resultObj, size); +    lineLen = -1; + +    /* +     * The decoding loop. First, we get the length of line (strictly, the +     * number of data bytes we expect to generate from the line) we're +     * processing this time round if it is not already known (i.e., when the +     * lineLen variable is set to the magic value, -1). +     */ + +    while (data < dataend) { +	char d[4] = {0, 0, 0, 0}; + +	if (lineLen < 0) { +	    c = *data++; +	    if (c < 32 || c > 96) { +		if (strict || !isspace(c)) { +		    goto badUu; +		} +		i--; +		continue; +	    } +	    lineLen = (c - 32) & 0x3f; +	} + +	/* +	 * Now we read a four-character grouping. +	 */ + +	for (i=0 ; i<4 ; i++) { +	    if (data < dataend) { +		d[i] = c = *data++; +		if (c < 32 || c > 96) { +		    if (strict) { +			if (!isspace(c)) { +			    goto badUu; +			} else if (c == '\n') { +			    goto shortUu; +			} +		    } +		    i--; +		    continue; +		} +	    } +	} + +	/* +	 * Translate that grouping into (up to) three binary bytes output. +	 */ + +	if (lineLen > 0) { +	    *cursor++ = (((d[0] - 0x20) & 0x3f) << 2) +		    | (((d[1] - 0x20) & 0x3f) >> 4); +	    if (--lineLen > 0) { +		*cursor++ = (((d[1] - 0x20) & 0x3f) << 4) +			| (((d[2] - 0x20) & 0x3f) >> 2); +		if (--lineLen > 0) { +		    *cursor++ = (((d[2] - 0x20) & 0x3f) << 6) +			    | (((d[3] - 0x20) & 0x3f)); +		    lineLen--; +		} +	    } +	} + +	/* +	 * If we've reached the end of the line, skip until we process a +	 * newline. +	 */ + +	if (lineLen == 0 && data < dataend) { +	    lineLen = -1; +	    do { +		c = *data++; +		if (c == '\n') { +		    break; +		} else if (c >= 32 && c <= 96) { +		    data--; +		    break; +		} else if (strict || !isspace(c)) { +		    goto badUu; +		} +	    } while (data < dataend); +	} +    } + +    /* +     * Sanity check, clean up and finish. +     */ + +    if (lineLen > 0 && strict) { +	goto shortUu; +    } +    Tcl_SetByteArrayLength(resultObj, cursor - begin); +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; + +  shortUu: +    Tcl_SetObjResult(interp, Tcl_ObjPrintf("short uuencode data")); +    Tcl_SetErrorCode(interp, "TCL", "BINARY", "DECODE", "SHORT", NULL); +    TclDecrRefCount(resultObj); +    return TCL_ERROR; + +  badUu: +    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +	    "invalid uuencode character \"%c\" at position %d", +	    c, (int) (data - datastart - 1))); +    Tcl_SetErrorCode(interp, "TCL", "BINARY", "DECODE", "INVALID", NULL); +    TclDecrRefCount(resultObj); +    return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * BinaryDecode64 -- + * + *	Decode a base64 encoded string. + * + * Results: + *	Interp result set to an byte array object + * + * Side effects: + *	None + * + *---------------------------------------------------------------------- + */ + +static int +BinaryDecode64( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Tcl_Obj *resultObj = NULL; +    unsigned char *data, *datastart, *dataend, c = '\0'; +    unsigned char *begin = NULL; +    unsigned char *cursor = NULL; +    int strict = 0; +    int i, index, size, cut = 0, count = 0; +    enum { OPT_STRICT }; +    static const char *const optStrings[] = { "-strict", NULL }; + +    if (objc < 2 || objc > 3) { +	Tcl_WrongNumArgs(interp, 1, objv, "?options? data"); +	return TCL_ERROR; +    } +    for (i = 1; i < objc-1; ++i) { +	if (Tcl_GetIndexFromObj(interp, objv[i], optStrings, "option", +		TCL_EXACT, &index) != TCL_OK) { +	    return TCL_ERROR; +	} +	switch (index) { +	case OPT_STRICT: +	    strict = 1; +	    break; +	} +    } + +    TclNewObj(resultObj); +    datastart = data = (unsigned char *) +	    TclGetStringFromObj(objv[objc-1], &count); +    dataend = data + count; +    size = ((count + 3) & ~3) * 3 / 4; +    begin = cursor = Tcl_SetByteArrayLength(resultObj, size); +    while (data < dataend) { +	unsigned long value = 0; + +	/* +	 * Decode the current block. Each base64 block consists of four input +	 * characters A-Z, a-z, 0-9, +, or /. Each character supplies six bits +	 * of output data, so each block's output is 24 bits (three bytes) in +	 * length. The final block can be shorter by one or two bytes, denoted +	 * by the input ending with one or two ='s, respectively. +	 */ + +	for (i = 0; i < 4; i++) { +	    /* +	     * Get the next input character. At end of input, pad with at most +	     * two ='s. If more than two ='s would be needed, instead discard +	     * the block read thus far. +	     */ + +	    if (data < dataend) { +		c = *data++; +	    } else if (i > 1) { +		c = '='; +	    } else { +		cut += 3; +		break; +	    } + +	    /* +	     * Load the character into the block value. Handle ='s specially +	     * because they're only valid as the last character or two of the +	     * final block of input. Unless strict mode is enabled, skip any +	     * input whitespace characters. +	     */ + +	    if (cut) { +		if (c == '=' && i > 1) { +		     value <<= 6; +		     cut++; +		} else if (!strict && isspace(c)) { +		     i--; +		} else { +		    goto bad64; +		} +	    } else if (c >= 'A' && c <= 'Z') { +		value = (value << 6) | ((c - 'A') & 0x3f); +	    } else if (c >= 'a' && c <= 'z') { +		value = (value << 6) | ((c - 'a' + 26) & 0x3f); +	    } else if (c >= '0' && c <= '9') { +		value = (value << 6) | ((c - '0' + 52) & 0x3f); +	    } else if (c == '+') { +		value = (value << 6) | 0x3e; +	    } else if (c == '/') { +		value = (value << 6) | 0x3f; +	    } else if (c == '=') { +		value <<= 6; +		cut++; +	    } else if (strict || !isspace(c)) { +		goto bad64; +	    } else { +		i--; +	    } +	} +	*cursor++ = UCHAR((value >> 16) & 0xff); +	*cursor++ = UCHAR((value >> 8) & 0xff); +	*cursor++ = UCHAR(value & 0xff); + +	/* +	 * Since = is only valid within the final block, if it was encountered +	 * but there are still more input characters, confirm that strict mode +	 * is off and all subsequent characters are whitespace. +	 */ + +	if (cut && data < dataend) { +	    if (strict) { +		goto bad64; +	    } +	    for (; data < dataend; data++) { +		if (!isspace(*data)) { +		    goto bad64; +		} +	    } +	} +    } +    Tcl_SetByteArrayLength(resultObj, cursor - begin - cut); +    Tcl_SetObjResult(interp, resultObj); +    return TCL_OK; + +  bad64: +    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +	    "invalid base64 character \"%c\" at position %d", +	    (char) c, (int) (data - datastart - 1))); +    TclDecrRefCount(resultObj); +    return TCL_ERROR; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ + | 
