This is some bits and pieces of an implementation of the ''tclBigIntType'' suggested on [Proper integers for Tcl 8.5 or 9.0]. * There is no guarantee the below will even compile, but I think it is mostly correct. * All of it is about implementing the Tcl_Obj type, i.e., it is about storage. There is not yet any code for doing any operations (save possibly some format conversions) on the data. Tcl_ObjType tclBigIntType = { "bigint", /* name */ FreeBigIntInternalRep, /* freeIntRepProc */ DupBigIntInternalRep, /* dupIntRepProc */ UpdateStringOfBigInt, /* updateStringProc */ SetBigIntFromAny /* setFromAnyProc */ }; /* * The following structure represents a subtype of bigint, which is a * particular internal representation for a bigint object plus a set of * procedures that provide standard operations on objects of that type. */ typedef struct Tcl_BigIntSubtype { char *name; /* Name of the subtype, e.g. "SLM-hexadecimal". */ int version; /* A struct version number. Just in case it will evolve. */ /* * These are all as in Tcl_ObjType. */ Tcl_FreeInternalRepProc *freeIntRepProc; /* Called to free any storage for the type's internal rep. NULL if the internal rep does not need freeing. */ Tcl_DupInternalRepProc *dupIntRepProc; /* Called to create a new object as a copy of an existing object. */ Tcl_UpdateStringProc *updateStringProc; /* Called to update the string rep from the type's internal representation. */ Tcl_SetFromAnyProc *setFromAnyProc; /* Called to convert the object's internal rep to this type. Frees the internal rep of the old type. Returns TCL_ERROR on failure. */ /* Here should follow pointers to functions that carry out arithmetical operations on big integers of this subtype. TO DO: Define these. */ } Tcl_BigIntSubtype; #define GET_SUBTYPE(objPtr) ( \ (Tcl_BigIntSubtype *)(objPtr) ->internalRep.twoPtrValue.ptr1 \ ) #define SUBTYPE_ACTION(field,objPtr) ( \ GET_SUBTYPE(objPtr) -> field \ ) #define BIGINT_VALUE(objPtr) ((objPtr) ->internalRep.twoPtrValue.ptr2) /* *---------------------------------------------------------------------- * * FreeBigIntInternalRep -- * * Deallocate the storage associated with a bigint data object's * internal representation. * * Results: * None. * * Side effects: * Frees memory. * *---------------------------------------------------------------------- */ static void FreeBigIntInternalRep(objPtr) Tcl_Obj *objPtr; /* Object with internal rep to free. */ { if (GET_SUBTYPE(objPtr) != NULL) { SUBTYPE_ACTION(freeIntRepProc,objPtr)(objPtr); } } /* *---------------------------------------------------------------------- * * UpdateStringOfBigInt -- * * Update the string representation for an integer object. * Note: This only does anything when the subtype is non-NULL. * * An existing old string rep is not freed 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 int-to-string conversion. * *---------------------------------------------------------------------- */ static void UpdateStringOfBigInt(objPtr) Tcl_Obj *objPtr; /* Object with string rep to update. */ { if (GET_SUBTYPE(objPtr) != NULL) { SUBTYPE_ACTION(updateStringProc,objPtr)(objPtr); } } /* *---------------------------------------------------------------------- * * DupBigIntInternalRep -- * * Initialize the internal representation of a new Tcl_Obj to a * copy of the internal representation of an existing bigint object. * * Results: * None. * * Side effects: * copyPtr's internal rep is set to a copy of srcPtr's internal * representation. * *---------------------------------------------------------------------- */ static void DupBigIntInternalRep(srcPtr, copyPtr) Tcl_Obj *srcPtr; /* Object with internal rep to copy. Must have an internal rep of type "bigint". */ Tcl_Obj *copyPtr; /* Object with internal rep to set. Must not currently have an internal rep.*/ { if (GET_SUBTYPE(srcPtr) == NULL) { copyPtr->internalRep.twoPtrValue.ptr1 = NULL; copyPtr->internalRep.twoPtrValue.ptr2 = NULL; } else { SUBTYPE_ACTION(dupIntRepProc,srcPtr)(srcPtr,copyPtr); } } /* *---------------------------------------------------------------------- * * SetBigIntFromAny -- * * Attempt to change the type of the Tcl object "objPtr" to bigint. * This always uses the null bigint subtype, so in practice it * only verifies that the string representation of the object is * a valid bigint. * * Results: * The return value is a standard object Tcl result. If an error * occurs during conversion and "interp" is not NULL, an error * message is left in that interpreter's result. * * Side effects: * If no error occurs and the "objPtr" has an internal * representation, then that is freed. * *---------------------------------------------------------------------- */ static int SetBigIntFromAny(interp, objPtr) Tcl_Interp *interp; /* Used for error reporting if not NULL. */ register Tcl_Obj *objPtr; /* The object to convert. */ { char *string; register char *p; /* Get the string representation. Make it up-to-date if necessary. */ p = string = Tcl_GetString(objPtr); /* Now check that "objPtr"s string can be parsed as an int. First skip initial spaces. */ for ( ; isspace(UCHAR(*p)); p++) { /* INTL: ISO space. */ /* Empty loop body. */ } /* Then step past the sign, if there is one. Q: Some languages allow multiple signs. Maybe we should too? */ if (*p == '-' || *p == '+') { p++; } /* Next check for a non-decimal-number prefix, and branch out into the various cases depending on what is found. */ if (*p != '0') { /* There was no prefix (they all start with 0), so what follows should be a non-empty sequence of decimal digits. Scan through that. */ if (!isdigit(UCHAR(*p))) { /* INTL: digit */ goto badInteger; } for ( ; isdigit(UCHAR(*p)); p++) { /* INTL: digit */ /* Empty loop body. */ } } else { /* There is a prefix. But of what kind? */ p++; switch (*p) { case 'x': case 'X': /* A hexadecimal 0x prefix has been found. The rest must be a nonempty sequence of hexadecimal digits. */ p++; if (!isxdigit(UCHAR(*p))) { goto badInteger; } p++; for ( ; isxdigit(UCHAR(*p)); p++) { /* INTL: digit */ /* Empty loop body. */ } break; case 'o': case 'O': /* A 0o prefix has been found. Since there is a movement to make this the only octal prefix in Tcl 9, we may at least allow it as one octal prefix here. For this to be an integer though, the rest must be a nonempty sequence of octal digits. */ p++; if (!isdigit(UCHAR(*p)) || (UCHAR(*p) >= '8')) { /* INTL: digit */ goto badInteger; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* Just 0 is however also acceptable as an octal prefix. */ p++; for ( ; isdigit(UCHAR(*p)) && (UCHAR(*p) < '8'); p++) { /* INTL: digit */ /* Empty loop body. */ } } } /* A valid sequence of digits has been found. What remains is to check that there is no garbage after it. */ for ( ; isspace(UCHAR(*p)); p++) { /* INTL: ISO space. */ /* Empty loop body. */ } if (*p != 0) { goto badInteger; } /* The string is a valid bigint! Free the old internalRep, then set the new type. */ if ((objPtr->typePtr != NULL) && (objPtr->typePtr->freeIntRepProc != NULL)) { objPtr->typePtr->freeIntRepProc(objPtr); } objPtr->typePtr = &tclBigIntType; objPtr->internalRep.twoPtrValue.ptr1 = NULL; objPtr->internalRep.twoPtrValue.ptr2 = NULL; return TCL_OK; badInteger: /* If we get here, the string is NOT a valid bigint. Generate an appropriate error message if "interp" is non-null. */ if (interp != NULL) { /* Must copy string before resetting the result in case a caller is trying to convert the interpreter's result to an int. */ Tcl_DString ds; Tcl_DStringInit(&ds); Tcl_DStringAppend(&ds, "expected integer (possibly big) but got \"", -1); Tcl_DStringAppend(&ds, string, length); Tcl_DStringAppend(&ds, "\" instead", -1); Tcl_DStringResult(interp, &ds); } return TCL_ERROR; } /* * The following functions and definitions implement a "SLM-hexadecimal" * subtype of bigint. The internal representation of that type is a * straightforward string, but with a slightly different structure from * the normal string representations of integers. The first character is * the sign, and it is one of '+', '0', and '-'. If it is '0' then the * string ends there and the value is 0. If it is '+' or '-' then the * integer is positive or negative respectively, and the remaining * characters are the digits of the integer. The least significant * digit comes first, and then the others in order of ascending * significance. The SLM part of the name stands for "Sign, Least * significant, Most significant" and highlights the order of these * extremes. * * The digit characters are 0123456789ABCDEF, as the "hexadecimal" part * of the name indicates. The last (most significant) digit must not be 0. * * Examples: * * Decimal SLM-hexadecimal * ------- --------------- * 0 0 * 1 +1 * 2 +2 * 10 +A * 16 +01 * 256 +001 * -1 -1 * -10 -A * 1000 +8E3 */ /* *---------------------------------------------------------------------- * * FreeBigIntSLMHexadecimalInternalRep -- * * Deallocate the storage associated with a SLM-hexadecimal bigint * data object's internal representation. * * Results: * None. * * Side effects: * The twoPtrValue.ptr2 of the internal representation of * "objPtr" should not be NULL and we furthermore assume that * it points to a string. The subtype (twoPtrValue.ptr1) is * changed to NULL. * *---------------------------------------------------------------------- */ static void FreeBigIntSLMHexadecimalInternalRep(objPtr) Tcl_Obj *objPtr; /* Object with internal rep to free. */ { ckfree( (char *) BIGINT_VALUE(objPtr) ); objPtr->internalRep.twoPtrValue.ptr1 = NULL; } /* *---------------------------------------------------------------------- * * DupBigIntSLMHexadecimalInternalRep -- * * Initialize the internal representation of a new Tcl_Obj to a * copy of the internal representation of an existing bigint object. * * Results: * None. * * Side effects: * copyPtr's internal rep is set to a copy of srcPtr's internal * representation. * *---------------------------------------------------------------------- */ static void DupBigIntSLMHexadecimalInternalRep(srcPtr, copyPtr) Tcl_Obj *srcPtr; /* Object with internal rep to copy. Must have an internal rep of type "bigint", subtype "SLM-hexadecimal". */ Tcl_Obj *copyPtr; /* Object with internal rep to set. Must not currently have an internal rep.*/ { --- if (GET_SUBTYPE(srcPtr) == NULL) { copyPtr->internalRep.twoPtrValue.ptr1 = NULL; copyPtr->internalRep.twoPtrValue.ptr2 = NULL; } else { SUBTYPE_ACTION(dupIntRepProc,srcPtr)(srcPtr,copyPtr); } } void CarryIntoSLM (dsPtr, offset, radix, shift, carry) Tcl_DString *dsPtr; /* Pointer to the dynamic string structure into which new data will be carried. */ int offset; /* Offset into dynamic string of first digit. */ unsigned char radix; /* The radix used in the dynamic string. */ long shift; /* The factor by which the data already in the string should be multiplied. */ long carry; /* The carry to add after having multiplied. */ { register char *p = /* Points to current digit. */ Tcl_DStringValue(dsPtr) + offset; char newchar; while (*p != 0) { carry += shift * (*p - '0'); *p = '0' + carry % radix; carry = carry / radix; p++; } while (carry > 0) { newchar = '0' + carry % radix; Tcl_DStringAppend(dsPtr, &newchar, 1); carry = carry / radix; } } /* *---------------------------------------------------------------------- * * UpdateStringOfBigInt_SLMHexadecimal -- * * Update the string representation for a big integer object * of subtype SLM-hexadecimal. * * An existing old string rep is not freed so storage will be * lost if this has not already been done. A new string is * allocated. * * Results: * None. * * Side effects: * The twoPtrValue.ptr2 of the internal representation of * "objPtr" should not be NULL and we furthermore assume that * it points to a valid SLM-hexadecimal string. * *---------------------------------------------------------------------- */ static void UpdateStringOfBigInt_SLMHexadecimal(objPtr) Tcl_Obj *objPtr; /* Object with string rep to update. */ { Tcl_DString temp; /* Temporary storage for the decimal digits. They appear here in least-to-most order. */ char *hexPtr; /* Internal object representation pointer. */ int len; /* Length of computed string representation. */ Tcl_DStringInit(&temp); /* First the digits are converted to decimal. The more significant hex digits are processed first. */ hexPtr = (char *) objPtr->internalRep.twoPtrValue.ptr2; { register char *p; /* Points to hex digit being shifted in. */ p = hexPtr + strlen(hexPtr) - 1; while (p > hexPtr) { CarryIntoSLM (&temp, 0, 10, 16, (*p >= 'A') ? (*p - 'A' + 10) : (*p - '0') ); p--; } } /* Then memory is allocated to hold the string representation and data are copied. It is necessary to reverse the order of digits. */ len = Tcl_DStringLength(&temp); if (*hexPtr == '-') { len++; } objPtr->bytes = (char *) ckalloc((unsigned) len + 1); objPtr->length = len; { register char *p, *q; /* Moving pointers. */ p = Tcl_DStringValue(&temp); q = objPtr->bytes + len; *q = '\0'; q--; while (*p != '\0') { *q = *p; p++; q--; } if (*hexPtr == '-') { *q = '-'; } } Tcl_DStringFree(&temp); }