Diff
Not logged in

Differences From Artifact [7677e61c14]:

To Artifact [7f6c541caf]:


1
2
3
4
5
6
7
8
9
10
11
12
13

14
15
16
17
18
19
20
1
2
3
4
5
6
7
8
9
10
11
12

13
14
15
16
17
18
19
20












-
+







/* 
 * tclBinary.c --
 *
 *	This file contains the implementation of the "binary" Tcl built-in
 *	command and the Tcl binary data object.
 *
 * 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.13 2003/02/21 21:54:11 dkf Exp $
 * RCS: @(#) $Id: tclBinary.c,v 1.14 2003/12/02 09:29:54 dkf Exp $
 */

#include "tclInt.h"
#include "tclPort.h"
#include <math.h>

/*
57
58
59
60
61
62
63
64


65
66
67
68
69
70
71
57
58
59
60
61
62
63

64
65
66
67
68
69
70
71
72







-
+
+







static int		GetFormatSpec _ANSI_ARGS_((char **formatPtr,
			    char *cmdPtr, int *countPtr));
static Tcl_Obj *	ScanNumber _ANSI_ARGS_((unsigned char *buffer,
			    int type, Tcl_HashTable **numberCachePtr));
static int		SetByteArrayFromAny _ANSI_ARGS_((Tcl_Interp *interp,
			    Tcl_Obj *objPtr));
static void		UpdateStringOfByteArray _ANSI_ARGS_((Tcl_Obj *listPtr));

static void		DeleteScanNumberCache _ANSI_ARGS_((
			    Tcl_HashTable *numberCachePtr));

/*
 * 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
747
748
749
750
751
752
753
754

755
756
757
758
759
760
761
748
749
750
751
752
753
754

755
756
757
758
759
760
761
762







-
+







			} else {
			    offset = count;
			}
			break;
		    }
		    default: {
			errorString = str;
			goto badfield;
			goto badField;
		    }
		}
	    }
	    if (offset > length) {
		length = offset;
	    }
	    if (length == 0) {
1046
1047
1048
1049
1050
1051
1052
1053

1054
1055
1056
1057
1058
1059
1060
1061
1062
1047
1048
1049
1050
1051
1052
1053

1054


1055
1056
1057
1058
1059
1060
1061







-
+
-
-







		}
		switch (cmd) {
		    case 'a':
		    case 'A': {
			unsigned char *src;

			if (arg >= objc) {
			    if (numberCachePtr != NULL) {
			    DeleteScanNumberCache(numberCachePtr);
				Tcl_DeleteHashTable(numberCachePtr);
			    }
			    goto badIndex;
			}
			if (count == BINARY_ALL) {
			    count = length - offset;
			} else {
			    if (count == BINARY_NOCOUNT) {
				count = 1;
1082
1083
1084
1085
1086
1087
1088
1089

1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104

1105
1106
1107
1108
1109
1110
1111
1112
1113
1081
1082
1083
1084
1085
1086
1087

1088


1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100

1101


1102
1103
1104
1105
1106
1107
1108







-
+
-
-












-
+
-
-







			    }
			}
			valuePtr = Tcl_NewByteArrayObj(src, size);
			resultPtr = Tcl_ObjSetVar2(interp, objv[arg],
				NULL, valuePtr, TCL_LEAVE_ERR_MSG);
			arg++;
			if (resultPtr == NULL) {
			    if (numberCachePtr != NULL) {
			    DeleteScanNumberCache(numberCachePtr);
				Tcl_DeleteHashTable(numberCachePtr);
			    }
			    Tcl_DecrRefCount(valuePtr);	/* unneeded */
			    return TCL_ERROR;
			}
			offset += count;
			break;
		    }
		    case 'b':
		    case 'B': {
			unsigned char *src;
			char *dest;

			if (arg >= objc) {
			    if (numberCachePtr != NULL) {
			    DeleteScanNumberCache(numberCachePtr);
				Tcl_DeleteHashTable(numberCachePtr);
			    }
			    goto badIndex;
			}
			if (count == BINARY_ALL) {
			    count = (length - offset) * 8;
			} else {
			    if (count == BINARY_NOCOUNT) {
				count = 1;
1141
1142
1143
1144
1145
1146
1147
1148

1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165

1166
1167
1168
1169
1170
1171
1172
1173
1174
1136
1137
1138
1139
1140
1141
1142

1143


1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157

1158


1159
1160
1161
1162
1163
1164
1165







-
+
-
-














-
+
-
-







			    }
			}
			
			resultPtr = Tcl_ObjSetVar2(interp, objv[arg],
				NULL, valuePtr, TCL_LEAVE_ERR_MSG);
			arg++;
			if (resultPtr == NULL) {
			    if (numberCachePtr != NULL) {
			    DeleteScanNumberCache(numberCachePtr);
				Tcl_DeleteHashTable(numberCachePtr);
			    }
			    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) {
			    if (numberCachePtr != NULL) {
			    DeleteScanNumberCache(numberCachePtr);
				Tcl_DeleteHashTable(numberCachePtr);
			    }
			    goto badIndex;
			}
			if (count == BINARY_ALL) {
			    count = (length - offset)*2;
			} else {
			    if (count == BINARY_NOCOUNT) {
				count = 1;
1202
1203
1204
1205
1206
1207
1208
1209

1210
1211
1212
1213
1214
1215
1216
1217
1218
1193
1194
1195
1196
1197
1198
1199

1200


1201
1202
1203
1204
1205
1206
1207







-
+
-
-







			    }
			}
			
			resultPtr = Tcl_ObjSetVar2(interp, objv[arg],
				NULL, valuePtr, TCL_LEAVE_ERR_MSG);
			arg++;
			if (resultPtr == NULL) {
			    if (numberCachePtr != NULL) {
			    DeleteScanNumberCache(numberCachePtr);
				Tcl_DeleteHashTable(numberCachePtr);
			    }
			    Tcl_DecrRefCount(valuePtr);	/* unneeded */
			    return TCL_ERROR;
			}
			offset += (count + 1) / 2;
			break;
		    }
		    case 'c': {
1242
1243
1244
1245
1246
1247
1248
1249

1250
1251
1252
1253
1254
1255
1256
1257
1258
1231
1232
1233
1234
1235
1236
1237

1238


1239
1240
1241
1242
1243
1244
1245







-
+
-
-







			unsigned char *src;

			size = sizeof(double);
			/* fall through */
			
			scanNumber:
			if (arg >= objc) {
			    if (numberCachePtr != NULL) {
			    DeleteScanNumberCache(numberCachePtr);
				Tcl_DeleteHashTable(numberCachePtr);
			    }
			    goto badIndex;
			}
			if (count == BINARY_NOCOUNT) {
			    if ((length - offset) < size) {
				goto done;
			    }
			    valuePtr = ScanNumber(buffer+offset, cmd,
1277
1278
1279
1280
1281
1282
1283
1284

1285
1286
1287
1288
1289
1290
1291
1292
1293
1264
1265
1266
1267
1268
1269
1270

1271


1272
1273
1274
1275
1276
1277
1278







-
+
-
-







			    offset += count*size;
			}

			resultPtr = Tcl_ObjSetVar2(interp, objv[arg],
				NULL, valuePtr, TCL_LEAVE_ERR_MSG);
			arg++;
			if (resultPtr == NULL) {
			    if (numberCachePtr != NULL) {
			    DeleteScanNumberCache(numberCachePtr);
				Tcl_DeleteHashTable(numberCachePtr);
			    }
			    Tcl_DecrRefCount(valuePtr);	/* unneeded */
			    return TCL_ERROR;
			}
			break;
		    }
		    case 'x': {
			if (count == BINARY_NOCOUNT) {
1310
1311
1312
1313
1314
1315
1316
1317

1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330

1331
1332
1333
1334

1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346

1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368


1369
1370
1371
1372
1373
1374
1375
1295
1296
1297
1298
1299
1300
1301

1302


1303
1304
1305
1306
1307
1308
1309
1310
1311
1312

1313


1314

1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326

1327


1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346

1347
1348
1349
1350
1351
1352
1353
1354
1355







-
+
-
-










-
+
-
-

-
+











-
+
-
-



















-
+
+







			} else {
			    offset -= count;
			}
			break;
		    }
		    case '@': {
			if (count == BINARY_NOCOUNT) {
			    if (numberCachePtr != NULL) {
			    DeleteScanNumberCache(numberCachePtr);
				Tcl_DeleteHashTable(numberCachePtr);
			    }
			    goto badCount;
			}
			if ((count == BINARY_ALL) || (count > length)) {
			    offset = length;
			} else {
			    offset = count;
			}
			break;
		    }
		    default: {
			if (numberCachePtr != NULL) {
			DeleteScanNumberCache(numberCachePtr);
			    Tcl_DeleteHashTable(numberCachePtr);
			}
			errorString = str;
			goto badfield;
			goto badField;
		    }
		}
	    }

	    /*
	     * Set the result to the last position of the cursor.
	     */

	    done:
	    Tcl_ResetResult(interp);
	    Tcl_SetLongObj(Tcl_GetObjResult(interp), arg - 4);
	    if (numberCachePtr != NULL) {
	    DeleteScanNumberCache(numberCachePtr);
		Tcl_DeleteHashTable(numberCachePtr);
	    }
	    break;
	}
    }
    return TCL_OK;

    badValue:
    Tcl_ResetResult(interp);
    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "expected ", errorString,
	    " string but got \"", errorValue, "\" instead", NULL);
    return TCL_ERROR;

    badCount:
    errorString = "missing count for \"@\" field specifier";
    goto error;

    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);
	return TCL_ERROR;
1663
1664
1665
1666
1667
1668
1669
1670

1671
1672
1673
1674
1675


1676
1677
1678
1679





1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699






1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712







































1643
1644
1645
1646
1647
1648
1649

1650
1651
1652
1653
1654

1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743







-
+




-
+
+




+
+
+
+
+




















+
+
+
+
+
+













+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
		     * 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.
		     */
		    Tcl_DeleteHashTable(tablePtr);
		    DeleteScanNumberCache(tablePtr);
		    *numberCachePtrPtr = NULL;
		    return Tcl_NewLongObj(value);
		} else {
		    register Tcl_Obj *objPtr = Tcl_NewLongObj(value);
		    /* Don't need to fiddle with refcount... */

		    Tcl_IncrRefCount(objPtr);
		    Tcl_SetHashValue(hPtr, (ClientData) objPtr);
		    return objPtr;
		}
	    }

	    /*
	     * Do not cache wide values; they are already too large to
	     * use as keys.
	     */
	case 'w':
	    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);
	    return Tcl_NewWideIntObj((Tcl_WideInt) uwvalue);
	case 'W':
	    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);
	    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 too.
	     */
	case 'f': {
	    float fvalue;
	    memcpy((VOID *) &fvalue, (VOID *) buffer, sizeof(float));
	    return Tcl_NewDoubleObj(fvalue);
	}
	case 'd': {
	    double dvalue;
	    memcpy((VOID *) &dvalue, (VOID *) buffer, sizeof(double));
	    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(numberCachePtr)
    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_Obj *) Tcl_GetHashValue(hEntry);

	Tcl_DecrRefCount(value);
	hEntry = Tcl_NextHashEntry(&search);
    }
    Tcl_DeleteHashTable(numberCachePtr);
}