diff options
author | griffin <briang42@easystreet.net> | 2022-08-28 22:55:17 (GMT) |
---|---|---|
committer | griffin <briang42@easystreet.net> | 2022-08-28 22:55:17 (GMT) |
commit | 858a9e55021e040d64d6183b62ca8e8559f4538c (patch) | |
tree | 8abdec021760033e8fef7ab21721546ce81015d9 | |
parent | addfa7b662b1382b33181c3c2f1dc945e42da4e1 (diff) | |
download | tcl-858a9e55021e040d64d6183b62ca8e8559f4538c.zip tcl-858a9e55021e040d64d6183b62ca8e8559f4538c.tar.gz tcl-858a9e55021e040d64d6183b62ca8e8559f4538c.tar.bz2 |
Move ArithSeries code to its own files. More bug fixes.
-rw-r--r-- | generic/tclArithSeries.c | 955 | ||||
-rw-r--r-- | generic/tclArithSeries.h | 54 | ||||
-rw-r--r-- | generic/tclCmdAH.c | 1 | ||||
-rw-r--r-- | generic/tclCmdIL.c | 65 | ||||
-rw-r--r-- | generic/tclExecute.c | 21 | ||||
-rw-r--r-- | generic/tclInt.h | 57 | ||||
-rw-r--r-- | generic/tclListObj.c | 931 | ||||
-rw-r--r-- | unix/Makefile.in | 11 | ||||
-rw-r--r-- | win/makefile.vc | 1 |
9 files changed, 1070 insertions, 1026 deletions
diff --git a/generic/tclArithSeries.c b/generic/tclArithSeries.c new file mode 100644 index 0000000..ae9299a --- /dev/null +++ b/generic/tclArithSeries.c @@ -0,0 +1,955 @@ +/* + * tclArithSeries.c -- + * + * This file contains the ArithSeries concrete abstract list + * implementation. It implements the inner workings of the lseq command. + * + * Copyright © 2022 Brian S. Griffin. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include <assert.h> +#include "tcl.h" +#include "tclInt.h" +#include "tclArithSeries.h" + +/* -------------------------- ArithSeries object ---------------------------- */ + + +#define ArithSeriesRepPtr(arithSeriesObjPtr) \ + (ArithSeries *) ((arithSeriesObjPtr)->internalRep.twoPtrValue.ptr1) + +#define ArithSeriesIndexM(arithSeriesRepPtr, index) \ + ((arithSeriesRepPtr)->isDouble ? \ + (((ArithSeriesDbl*)(arithSeriesRepPtr))->start+((index) * ((ArithSeriesDbl*)(arithSeriesRepPtr))->step)) \ + : \ + ((arithSeriesRepPtr)->start+((index) * arithSeriesRepPtr->step))) + +#define ArithSeriesGetInternalRep(objPtr, arithRepPtr) \ + do { \ + const Tcl_ObjInternalRep *irPtr; \ + irPtr = TclFetchInternalRep((objPtr), &tclArithSeriesType); \ + (arithRepPtr) = irPtr ? (ArithSeries *)irPtr->twoPtrValue.ptr1 : NULL; \ + } while (0) + + +/* + * Prototypes for procedures defined later in this file: + */ + +static void DupArithSeriesInternalRep (Tcl_Obj *srcPtr, Tcl_Obj *copyPtr); +static void FreeArithSeriesInternalRep (Tcl_Obj *listPtr); +static int SetArithSeriesFromAny (Tcl_Interp *interp, Tcl_Obj *objPtr); +static void UpdateStringOfArithSeries (Tcl_Obj *listPtr); + +/* + * The structure below defines the arithmetic series Tcl object type by + * means of procedures that can be invoked by generic object code. + * + * The arithmetic series object is a special case of Tcl list representing + * an interval of an arithmetic series in constant space. + * + * The arithmetic series is internally represented with three integers, + * *start*, *end*, and *step*, Where the length is calculated with + * the following algorithm: + * + * if RANGE == 0 THEN + * ERROR + * if RANGE > 0 + * LEN is (((END-START)-1)/STEP) + 1 + * else if RANGE < 0 + * LEN is (((END-START)-1)/STEP) - 1 + * + * And where the equivalent's list I-th element is calculated + * as: + * + * LIST[i] = START+(STEP*i) + * + * Zero elements ranges, like in the case of START=10 END=10 STEP=1 + * are valid and will be equivalent to the empty list. + */ + +const Tcl_ObjType tclArithSeriesType = { + "arithseries", /* name */ + FreeArithSeriesInternalRep, /* freeIntRepProc */ + DupArithSeriesInternalRep, /* dupIntRepProc */ + UpdateStringOfArithSeries, /* updateStringProc */ + SetArithSeriesFromAny /* setFromAnyProc */ +}; + +/* + *---------------------------------------------------------------------- + * + * ArithSeriesLen -- + * + * Compute the length of the equivalent list where + * every element is generated starting from *start*, + * and adding *step* to generate every successive element + * that's < *end* for positive steps, or > *end* for negative + * steps. + * + * Results: + * + * The length of the list generated by the given range, + * that may be zero. + * The function returns -1 if the list is of length infiite. + * + * Side effects: + * + * None. + * + *---------------------------------------------------------------------- + */ +static Tcl_WideInt +ArithSeriesLen(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step) +{ + Tcl_WideInt len; + + if (step == 0) return 0; + len = (step ? (1 + (((end-start))/step)) : 0); + return (len < 0) ? -1 : len; +} + +/* + *---------------------------------------------------------------------- + * + * TclNewArithSeriesInt -- + * + * Creates a new ArithSeries object. The returned object has + * refcount = 0. + * + * Results: + * + * A Tcl_Obj pointer to the created ArithSeries object. + * A NULL pointer of the range is invalid. + * + * Side Effects: + * + * None. + *---------------------------------------------------------------------- + */ +Tcl_Obj * +TclNewArithSeriesInt(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step, Tcl_WideInt len) +{ + Tcl_WideInt length = (len>=0 ? len : ArithSeriesLen(start, end, step)); + Tcl_Obj *arithSeriesPtr; + ArithSeries *arithSeriesRepPtr; + + TclNewObj(arithSeriesPtr); + + if (length <= 0) { + return arithSeriesPtr; + } + + arithSeriesRepPtr = (ArithSeries*) ckalloc(sizeof (ArithSeries)); + arithSeriesRepPtr->isDouble = 0; + arithSeriesRepPtr->start = start; + arithSeriesRepPtr->end = end; + arithSeriesRepPtr->step = step; + arithSeriesRepPtr->len = length; + arithSeriesRepPtr->elements = NULL; + arithSeriesPtr->internalRep.twoPtrValue.ptr1 = arithSeriesRepPtr; + arithSeriesPtr->internalRep.twoPtrValue.ptr2 = NULL; + arithSeriesPtr->typePtr = &tclArithSeriesType; + if (length > 0) + Tcl_InvalidateStringRep(arithSeriesPtr); + + return arithSeriesPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclNewArithSeriesDbl -- + * + * Creates a new ArithSeries object with doubles. The returned object has + * refcount = 0. + * + * Results: + * + * A Tcl_Obj pointer to the created ArithSeries object. + * A NULL pointer of the range is invalid. + * + * Side Effects: + * + * None. + *---------------------------------------------------------------------- + */ +Tcl_Obj * +TclNewArithSeriesDbl(double start, double end, double step, Tcl_WideInt len) +{ + Tcl_WideInt length = (len>=0 ? len : ArithSeriesLen(start, end, step)); + Tcl_Obj *arithSeriesPtr; + ArithSeriesDbl *arithSeriesRepPtr; + + TclNewObj(arithSeriesPtr); + + if (length <= 0) { + return arithSeriesPtr; + } + + arithSeriesRepPtr = (ArithSeriesDbl*) ckalloc(sizeof (ArithSeriesDbl)); + arithSeriesRepPtr->isDouble = 1; + arithSeriesRepPtr->start = start; + arithSeriesRepPtr->end = end; + arithSeriesRepPtr->step = step; + arithSeriesRepPtr->len = length; + arithSeriesRepPtr->elements = NULL; + arithSeriesPtr->internalRep.twoPtrValue.ptr1 = arithSeriesRepPtr; + arithSeriesPtr->internalRep.twoPtrValue.ptr2 = NULL; + arithSeriesPtr->typePtr = &tclArithSeriesType; + if (length > 0) + Tcl_InvalidateStringRep(arithSeriesPtr); + + return arithSeriesPtr; +} + +/* + *---------------------------------------------------------------------- + * + * assignNumber -- + * + * Create the approprite Tcl_Obj value for the given numeric values. + * Used locally only for decoding [lseq] numeric arguments. + * refcount = 0. + * + * Results: + * + * A Tcl_Obj pointer. + * No assignment on error. + * + * Side Effects: + * + * None. + *---------------------------------------------------------------------- + */ +static void +assignNumber(int useDoubles, Tcl_WideInt *intNumberPtr, double *dblNumberPtr, Tcl_Obj *numberObj) +{ + union { + double d; + Tcl_WideInt i; + } *number; + int tcl_number_type; + + if (TclGetNumberFromObj(NULL, numberObj, (ClientData*)&number, &tcl_number_type) != TCL_OK) { + return; + } + if (useDoubles) { + if (tcl_number_type == TCL_NUMBER_DOUBLE) { + *dblNumberPtr = number->d; + } else { + *dblNumberPtr = (double)number->i; + } + } else { + if (tcl_number_type == TCL_NUMBER_INT) { + *intNumberPtr = number->i; + } else { + *intNumberPtr = (Tcl_WideInt)number->d; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TclNewArithSeriesObj -- + * + * Creates a new ArithSeries object. Some arguments may be NULL and will + * be computed based on the other given arguments. + * refcount = 0. + * + * Results: + * + * A Tcl_Obj pointer to the created ArithSeries object. + * An empty Tcl_Obj if the range is invalid. + * + * Side Effects: + * + * None. + *---------------------------------------------------------------------- + */ +Tcl_Obj * +TclNewArithSeriesObj(int useDoubles, Tcl_Obj *startObj, Tcl_Obj *endObj, Tcl_Obj *stepObj, Tcl_Obj *lenObj) +{ + double dstart, dend, dstep; + Tcl_WideInt start, end, step, len; + + if (startObj) { + assignNumber(useDoubles, &start, &dstart, startObj); + } else { + start = 0; + dstart = start; + } + if (stepObj) { + assignNumber(useDoubles, &step, &dstep, stepObj); + if (useDoubles) { + step = dstep; + } else { + dstep = step; + } + if (dstep == 0) { + return Tcl_NewObj(); + } + } + if (endObj) { + assignNumber(useDoubles, &end, &dend, endObj); + } + if (lenObj) { + Tcl_GetWideIntFromObj(NULL, lenObj, &len); + } + + if (startObj && endObj) { + if (!stepObj) { + if (useDoubles) { + dstep = (dstart < dend) ? 1.0 : -1.0; + step = dstep; + } else { + step = (start < end) ? 1 : -1; + dstep = step; + } + } + assert(dstep!=0); + if (!lenObj) { + if (useDoubles) { + len = (dend - dstart + dstep)/dstep; + } else { + len = (end - start + step)/step; + } + } + } + + if (!endObj) { + if (useDoubles) { + dend = dstart + (dstep * (len-1)); + end = dend; + } else { + end = start + (step * (len-1)); + dend = end; + } + } + + if (useDoubles) { + return TclNewArithSeriesDbl(dstart, dend, dstep, len); + } else { + return TclNewArithSeriesInt(start, end, step, len); + } +} + +/* + *---------------------------------------------------------------------- + * + * TclArithSeriesObjStep -- + * + * Return a Tcl_Obj with the step value from the give ArithSeries Obj. + * refcount = 0. + * + * Results: + * + * A Tcl_Obj pointer to the created ArithSeries object. + * A NULL pointer of the range is invalid. + * + * Side Effects: + * + * None. + *---------------------------------------------------------------------- + */ +/* + * TclArithSeriesObjStep -- + */ +int +TclArithSeriesObjStep( + Tcl_Obj *arithSeriesPtr, + Tcl_Obj **stepObj) +{ + ArithSeries *arithSeriesRepPtr; + + if (arithSeriesPtr->typePtr != &tclArithSeriesType) { + Tcl_Panic("TclArithSeriesObjIndex called with a not ArithSeries Obj."); + } + arithSeriesRepPtr = ArithSeriesRepPtr(arithSeriesPtr); + if (arithSeriesRepPtr->isDouble) { + *stepObj = Tcl_NewDoubleObj(((ArithSeriesDbl*)(arithSeriesRepPtr))->step); + } else { + *stepObj = Tcl_NewWideIntObj(arithSeriesRepPtr->step); + } + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * TclArithSeriesObjIndex -- + * + * Returns the element with the specified index in the list + * represented by the specified Arithmentic Sequence object. + * If the index is out of range, TCL_ERROR is returned, + * otherwise TCL_OK is returned and the integer value of the + * element is stored in *element. + * + * Results: + * + * TCL_OK on succes, TCL_ERROR on index out of range. + * + * Side Effects: + * + * On success, the integer pointed by *element is modified. + * + *---------------------------------------------------------------------- + */ + +int +TclArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index, Tcl_Obj **elementObj) +{ + ArithSeries *arithSeriesRepPtr; + + if (arithSeriesPtr->typePtr != &tclArithSeriesType) { + Tcl_Panic("TclArithSeriesObjIndex called with a not ArithSeries Obj."); + } + arithSeriesRepPtr = ArithSeriesRepPtr(arithSeriesPtr); + if (index < 0 || index >= arithSeriesRepPtr->len) { + return TCL_ERROR; + } + /* List[i] = Start + (Step * index) */ + if (arithSeriesRepPtr->isDouble) { + *elementObj = Tcl_NewDoubleObj(ArithSeriesIndexM(arithSeriesRepPtr, index)); + } else { + *elementObj = Tcl_NewWideIntObj(ArithSeriesIndexM(arithSeriesRepPtr, index)); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclArithSeriesObjLength + * + * Returns the length of the arithmentic series. + * + * Results: + * + * The length of the series as Tcl_WideInt. + * + * Side Effects: + * + * None. + * + *---------------------------------------------------------------------- + */ +Tcl_WideInt TclArithSeriesObjLength(Tcl_Obj *arithSeriesPtr) +{ + ArithSeries *arithSeriesRepPtr = (ArithSeries*) + arithSeriesPtr->internalRep.twoPtrValue.ptr1; + return arithSeriesRepPtr->len; +} + +/* + *---------------------------------------------------------------------- + * + * FreeArithSeriesInternalRep -- + * + * Deallocate the storage associated with an arithseries object's + * internal representation. + * + * Results: + * None. + * + * Side effects: + * Frees arithSeriesPtr's ArithSeries* internal representation and + * sets listPtr's internalRep.twoPtrValue.ptr1 to NULL. + * + *---------------------------------------------------------------------- + */ + +static void +FreeArithSeriesInternalRep(Tcl_Obj *arithSeriesPtr) +{ + ArithSeries *arithSeriesRepPtr = + (ArithSeries *) arithSeriesPtr->internalRep.twoPtrValue.ptr1; + if (arithSeriesRepPtr->elements) { + Tcl_WideInt i; + Tcl_Obj**elmts = arithSeriesRepPtr->elements; + for(i=0; i<arithSeriesRepPtr->len; i++) { + if (elmts[i]) { + Tcl_DecrRefCount(elmts[i]); + } + } + ckfree((char *) arithSeriesRepPtr->elements); + } + ckfree((char *) arithSeriesRepPtr); + arithSeriesPtr->internalRep.twoPtrValue.ptr1 = NULL; +} + +/* + *---------------------------------------------------------------------- + * + * DupArithSeriesInternalRep -- + * + * Initialize the internal representation of a arithseries Tcl_Obj to a + * copy of the internal representation of an existing arithseries object. + * + * Results: + * None. + * + * Side effects: + * We set "copyPtr"s internal rep to a pointer to a + * newly allocated ArithSeries structure. + *---------------------------------------------------------------------- + */ + +static void +DupArithSeriesInternalRep(srcPtr, copyPtr) + Tcl_Obj *srcPtr; /* Object with internal rep to copy. */ + Tcl_Obj *copyPtr; /* Object with internal rep to set. */ +{ + ArithSeries *srcArithSeriesRepPtr = + (ArithSeries *) srcPtr->internalRep.twoPtrValue.ptr1; + ArithSeries *copyArithSeriesRepPtr; + + /* + * Allocate a new ArithSeries structure. */ + + copyArithSeriesRepPtr = (ArithSeries*) ckalloc(sizeof(ArithSeries)); + *copyArithSeriesRepPtr = *srcArithSeriesRepPtr; + copyArithSeriesRepPtr->elements = NULL; + copyPtr->internalRep.twoPtrValue.ptr1 = copyArithSeriesRepPtr; + copyPtr->internalRep.twoPtrValue.ptr2 = NULL; + copyPtr->typePtr = &tclArithSeriesType; +} + +/* + *---------------------------------------------------------------------- + * + * UpdateStringOfArithSeries -- + * + * Update the string representation for an arithseries 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 list-to-string conversion. This string will be empty if the + * list has no elements. The list internal representation + * should not be NULL and we assume it is not NULL. + * + * Notes: + * At the cost of overallocation it's possible to estimate + * the length of the string representation and make this procedure + * much faster. Because the programmer shouldn't expect the + * string conversion of a big arithmetic sequence to be fast + * this version takes more care of space than time. + * + *---------------------------------------------------------------------- + */ + +static void +UpdateStringOfArithSeries(Tcl_Obj *arithSeriesPtr) +{ + ArithSeries *arithSeriesRepPtr = + (ArithSeries*) arithSeriesPtr->internalRep.twoPtrValue.ptr1; + char *elem, *p; + Tcl_Obj *elemObj; + Tcl_WideInt i; + Tcl_WideInt length = 0; + int slen; + + /* + * Pass 1: estimate space. + */ + for (i = 0; i < arithSeriesRepPtr->len; i++) { + TclArithSeriesObjIndex(arithSeriesPtr, i, &elemObj); + elem = TclGetStringFromObj(elemObj, &slen); + Tcl_DecrRefCount(elemObj); + slen += 1; /* + 1 is for the space or the nul-term */ + length += slen; + } + + /* + * Pass 2: generate the string repr. + */ + + p = Tcl_InitStringRep(arithSeriesPtr, NULL, length); + for (i = 0; i < arithSeriesRepPtr->len; i++) { + TclArithSeriesObjIndex(arithSeriesPtr, i, &elemObj); + elem = TclGetStringFromObj(elemObj, &slen); + strcpy(p, elem); + p[slen] = ' '; + p += slen+1; + Tcl_DecrRefCount(elemObj); + } + if (length > 0) arithSeriesPtr->bytes[length-1] = '\0'; + arithSeriesPtr->length = length-1; +} + +/* + *---------------------------------------------------------------------- + * + * SetArithSeriesFromAny -- + * + * The Arithmetic Series object is just an way to optimize + * Lists space complexity, so no one should try to convert + * a string to an Arithmetic Series object. + * + * This function is here just to populate the Type structure. + * + * Results: + * + * The result is always TCL_ERROR. But see Side Effects. + * + * Side effects: + * + * Tcl Panic if called. + * + *---------------------------------------------------------------------- + */ + +static int +SetArithSeriesFromAny(interp, objPtr) + Tcl_Interp *interp; /* Used for error reporting if not NULL. */ + Tcl_Obj *objPtr; /* The object to convert. */ +{ + (void)interp; + (void)objPtr; + Tcl_Panic("SetArithSeriesFromAny: should never be called"); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * TclArithSeriesObjCopy -- + * + * Makes a "pure arithSeries" copy of an ArithSeries value. This provides for the C + * level a counterpart of the [lrange $list 0 end] command, while using + * internals details to be as efficient as possible. + * + * Results: + * + * Normally returns a pointer to a new Tcl_Obj, that contains the same + * arithSeries value as *arithSeriesPtr does. The returned Tcl_Obj has a + * refCount of zero. If *arithSeriesPtr does not hold an arithSeries, + * NULL is returned, and if interp is non-NULL, an error message is + * recorded there. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Obj * +TclArithSeriesObjCopy( + Tcl_Interp *interp, /* Used to report errors if not NULL. */ + Tcl_Obj *arithSeriesPtr) /* List object for which an element array is + * to be returned. */ +{ + Tcl_Obj *copyPtr; + ArithSeries *arithSeriesRepPtr; + + ArithSeriesGetInternalRep(arithSeriesPtr, arithSeriesRepPtr); + if (NULL == arithSeriesRepPtr) { + if (SetArithSeriesFromAny(interp, arithSeriesPtr) != TCL_OK) { + /* We know this is going to panic, but it's the message we want */ + return NULL; + } + } + + TclNewObj(copyPtr); + TclInvalidateStringRep(copyPtr); + DupArithSeriesInternalRep(arithSeriesPtr, copyPtr); + return copyPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclArithSeriesObjRange -- + * + * Makes a slice of an ArithSeries value. + * *arithSeriesPtr must be known to be a valid list. + * + * Results: + * Returns a pointer to the sliced series. + * This may be a new object or the same object if not shared. + * + * Side effects: + * ?The possible conversion of the object referenced by listPtr? + * ?to a list object.? + * + *---------------------------------------------------------------------- + */ + +Tcl_Obj * +TclArithSeriesObjRange( + Tcl_Obj *arithSeriesPtr, /* List object to take a range from. */ + int fromIdx, /* Index of first element to include. */ + int toIdx) /* Index of last element to include. */ +{ + ArithSeries *arithSeriesRepPtr; + Tcl_Obj *startObj, *endObj, *stepObj; + + ArithSeriesGetInternalRep(arithSeriesPtr, arithSeriesRepPtr); + + if (fromIdx < 0) { + fromIdx = 0; + } + if (fromIdx > toIdx) { + Tcl_Obj *obj; + TclNewObj(obj); + return obj; + } + + TclArithSeriesObjIndex(arithSeriesPtr, fromIdx, &startObj); + Tcl_IncrRefCount(startObj); + TclArithSeriesObjIndex(arithSeriesPtr, toIdx, &endObj); + Tcl_IncrRefCount(endObj); + TclArithSeriesObjStep(arithSeriesPtr, &stepObj); + Tcl_IncrRefCount(stepObj); + + if (Tcl_IsShared(arithSeriesPtr) || + ((arithSeriesPtr->refCount > 1))) { + Tcl_Obj *newSlicePtr = TclNewArithSeriesObj(arithSeriesRepPtr->isDouble, + startObj, endObj, stepObj, NULL); + Tcl_DecrRefCount(startObj); + Tcl_DecrRefCount(endObj); + Tcl_DecrRefCount(stepObj); + return newSlicePtr; + } + + /* + * In-place is possible. + */ + + /* + * Even if nothing below cause any changes, we still want the + * string-canonizing effect of [lrange 0 end]. + */ + + TclInvalidateStringRep(arithSeriesPtr); + + if (arithSeriesRepPtr->isDouble) { + ArithSeriesDbl *arithSeriesDblRepPtr = (ArithSeriesDbl*)arithSeriesPtr; + double start, end, step; + Tcl_GetDoubleFromObj(NULL, startObj, &start); + Tcl_GetDoubleFromObj(NULL, endObj, &end); + Tcl_GetDoubleFromObj(NULL, stepObj, &step); + arithSeriesDblRepPtr->start = start; + arithSeriesDblRepPtr->end = end; + arithSeriesDblRepPtr->step = step; + arithSeriesDblRepPtr->len = (end-start+step)/step; + arithSeriesDblRepPtr->elements = NULL; + + } else { + Tcl_WideInt start, end, step; + Tcl_GetWideIntFromObj(NULL, startObj, &start); + Tcl_GetWideIntFromObj(NULL, endObj, &end); + Tcl_GetWideIntFromObj(NULL, stepObj, &step); + arithSeriesRepPtr->start = start; + arithSeriesRepPtr->end = end; + arithSeriesRepPtr->step = step; + arithSeriesRepPtr->len = (end-start+step)/step; + arithSeriesRepPtr->elements = NULL; + } + + Tcl_DecrRefCount(startObj); + Tcl_DecrRefCount(endObj); + Tcl_DecrRefCount(stepObj); + + return arithSeriesPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TclArithSeriesGetElements -- + * + * This function returns an (objc,objv) array of the elements in a list + * object. + * + * Results: + * The return value is normally TCL_OK; in this case *objcPtr is set to + * the count of list elements and *objvPtr is set to a pointer to an + * array of (*objcPtr) pointers to each list element. If listPtr does not + * refer to an Abstract List object and the object can not be converted + * to one, TCL_ERROR is returned and an error message will be left in the + * interpreter's result if interp is not NULL. + * + * The objects referenced by the returned array should be treated as + * readonly and their ref counts are _not_ incremented; the caller must + * do that if it holds on to a reference. Furthermore, the pointer and + * length returned by this function may change as soon as any function is + * called on the list object; be careful about retaining the pointer in a + * local data structure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TclArithSeriesGetElements( + Tcl_Interp *interp, /* Used to report errors if not NULL. */ + Tcl_Obj *objPtr, /* AbstractList object for which an element + * array is to be returned. */ + int *objcPtr, /* Where to store the count of objects + * referenced by objv. */ + Tcl_Obj ***objvPtr) /* Where to store the pointer to an array of + * pointers to the list's objects. */ +{ + if (TclHasInternalRep(objPtr,&tclArithSeriesType)) { + ArithSeries *arithSeriesRepPtr; + Tcl_Obj **objv; + int i, objc; + + ArithSeriesGetInternalRep(objPtr, arithSeriesRepPtr); + objc = arithSeriesRepPtr->len; + if (objc > 0) { + if (arithSeriesRepPtr->elements) { + /* If this exists, it has already been populated */ + objv = arithSeriesRepPtr->elements; + } else { + /* Construct the elements array */ + objv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj*) * objc); + if (objv == NULL) { + if (interp) { + Tcl_SetObjResult( + interp, + Tcl_NewStringObj("max length of a Tcl list exceeded", -1)); + Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); + } + return TCL_ERROR; + } + arithSeriesRepPtr->elements = objv; + for (i = 0; i < objc; i++) { + if (TclArithSeriesObjIndex(objPtr, i, &objv[i]) != TCL_OK) { + if (interp) { + Tcl_SetObjResult( + interp, + Tcl_NewStringObj("indexing error", -1)); + Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); + } + return TCL_ERROR; + } + Tcl_IncrRefCount(objv[i]); + } + } + } else { + objv = NULL; + } + *objvPtr = objv; + *objcPtr = objc; + } else { + if (interp != NULL) { + Tcl_SetObjResult( + interp, + Tcl_ObjPrintf("value is not an arithseries")); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "UNKNOWN", NULL); + } + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TclArithSeriesObjReverse -- + * + * Reverse the order of the ArithSeries value. + * *arithSeriesPtr must be known to be a valid list. + * + * Results: + * Returns a pointer to the reordered series. + * This may be a new object or the same object if not shared. + * + * Side effects: + * ?The possible conversion of the object referenced by listPtr? + * ?to a list object.? + * + *---------------------------------------------------------------------- + */ + +Tcl_Obj * +TclArithSeriesObjReverse( + Tcl_Obj *arithSeriesPtr) /* List object to reverse. */ +{ + ArithSeries *arithSeriesRepPtr; + Tcl_Obj *startObj, *endObj, *stepObj; + Tcl_Obj *resultObj; + Tcl_WideInt start, end, step, len; + double dstart, dend, dstep; + int isDouble; + + ArithSeriesGetInternalRep(arithSeriesPtr, arithSeriesRepPtr); + + isDouble = arithSeriesRepPtr->isDouble; + len = arithSeriesRepPtr->len; + + TclArithSeriesObjIndex(arithSeriesPtr, (len-1), &startObj); + TclArithSeriesObjIndex(arithSeriesPtr, 0, &endObj); + TclArithSeriesObjStep(arithSeriesPtr, &stepObj); + + if (isDouble) { + Tcl_GetDoubleFromObj(NULL, startObj, &dstart); + Tcl_GetDoubleFromObj(NULL, endObj, &dend); + Tcl_GetDoubleFromObj(NULL, stepObj, &dstep); + dstep = -dstep; + TclSetDoubleObj(stepObj, dstep); + } else { + Tcl_GetWideIntFromObj(NULL, startObj, &start); + Tcl_GetWideIntFromObj(NULL, endObj, &end); + Tcl_GetWideIntFromObj(NULL, stepObj, &step); + step = -step; + TclSetIntObj(stepObj, step); + } + + if (Tcl_IsShared(arithSeriesPtr) || + ((arithSeriesPtr->refCount > 1))) { + Tcl_Obj *lenObj = Tcl_NewWideIntObj(len); + resultObj = TclNewArithSeriesObj(isDouble, + startObj, endObj, stepObj, lenObj); + Tcl_DecrRefCount(lenObj); + } else { + + /* + * In-place is possible. + */ + + TclInvalidateStringRep(arithSeriesPtr); + + if (isDouble) { + ArithSeriesDbl *arithSeriesDblRepPtr = + (ArithSeriesDbl*)arithSeriesRepPtr; + arithSeriesDblRepPtr->start = dstart; + arithSeriesDblRepPtr->end = dend; + arithSeriesDblRepPtr->step = dstep; + } else { + arithSeriesRepPtr->start = start; + arithSeriesRepPtr->end = end; + arithSeriesRepPtr->step = step; + } + if (arithSeriesRepPtr->elements) { + Tcl_WideInt i; + for (i=0; i<len; i++) { + Tcl_DecrRefCount(arithSeriesRepPtr->elements[i]); + } + ckfree((char*)arithSeriesRepPtr->elements); + } + arithSeriesRepPtr->elements = NULL; + + resultObj = arithSeriesPtr; + } + + Tcl_DecrRefCount(startObj); + Tcl_DecrRefCount(endObj); + Tcl_DecrRefCount(stepObj); + + return resultObj; +} diff --git a/generic/tclArithSeries.h b/generic/tclArithSeries.h new file mode 100644 index 0000000..5d014d0 --- /dev/null +++ b/generic/tclArithSeries.h @@ -0,0 +1,54 @@ +/* + * tclArithSeries.h -- + * + * This file contains the ArithSeries concrete abstract list + * implementation. It implements the inner workings of the lseq command. + * + * Copyright © 2022 Brian S. Griffin. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +/* + * The structure used for the AirthSeries internal representation. + * Note that the len can in theory be always computed by start,end,step + * but it's faster to cache it inside the internal representation. + */ +typedef struct ArithSeries { + Tcl_WideInt start; + Tcl_WideInt end; + Tcl_WideInt step; + Tcl_WideInt len; + Tcl_Obj **elements; + int isDouble; +} ArithSeries; +typedef struct ArithSeriesDbl { + double start; + double end; + double step; + Tcl_WideInt len; + Tcl_Obj **elements; + int isDouble; +} ArithSeriesDbl; + + +MODULE_SCOPE Tcl_Obj * TclArithSeriesObjCopy(Tcl_Interp *interp, + Tcl_Obj *arithSeriesPtr); +MODULE_SCOPE int TclArithSeriesObjStep(Tcl_Obj *arithSeriesPtr, + Tcl_Obj **stepObj); +MODULE_SCOPE int TclArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, + Tcl_WideInt index, Tcl_Obj **elementObj); +MODULE_SCOPE Tcl_WideInt TclArithSeriesObjLength(Tcl_Obj *arithSeriesPtr); +MODULE_SCOPE Tcl_Obj * TclArithSeriesObjRange(Tcl_Obj *arithSeriesPtr, + int fromIdx, int toIdx); +MODULE_SCOPE Tcl_Obj * TclArithSeriesObjReverse(Tcl_Obj *arithSeriesPtr); +MODULE_SCOPE int TclArithSeriesGetElements(Tcl_Interp *interp, + Tcl_Obj *objPtr, int *objcPtr, Tcl_Obj ***objvPtr); +MODULE_SCOPE Tcl_Obj * TclNewArithSeriesInt(Tcl_WideInt start, + Tcl_WideInt end, Tcl_WideInt step, + Tcl_WideInt len); +MODULE_SCOPE Tcl_Obj * TclNewArithSeriesDbl(double start, double end, + double step, Tcl_WideInt len); +MODULE_SCOPE Tcl_Obj * TclNewArithSeriesObj(int useDoubles, Tcl_Obj *startObj, + Tcl_Obj *endObj, Tcl_Obj *stepObj, Tcl_Obj *lenObj); diff --git a/generic/tclCmdAH.c b/generic/tclCmdAH.c index adb4044..eb9c337 100644 --- a/generic/tclCmdAH.c +++ b/generic/tclCmdAH.c @@ -15,6 +15,7 @@ #ifdef _WIN32 # include "tclWinInt.h" #endif +#include "tclArithSeries.h" /* * The state structure used by [foreach]. Note that the actual structure has diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c index 77a8ffc..bcee3ca 100644 --- a/generic/tclCmdIL.c +++ b/generic/tclCmdIL.c @@ -20,6 +20,7 @@ #include "tclInt.h" #include "tclRegexp.h" #include <math.h> +#include "tclArithSeries.h" /* * During execution of the "lsort" command, structures of the following type @@ -101,7 +102,7 @@ static const char *const seq_operations[] = { "..", "to", "count", "by", NULL }; typedef enum Sequence_Operators { - RANGE_DOTS, RANGE_TO, RANGE_COUNT, RANGE_BY + LSEQ_DOTS, LSEQ_TO, LSEQ_COUNT, LSEQ_BY } SequenceOperators; static const char *const seq_step_keywords[] = {"by", NULL}; typedef enum Step_Operators { @@ -4131,21 +4132,21 @@ SequenceIdentifyArgument( * Enumerated possible argument patterns: * * 1: - * range n + * lseq n * 2: - * range n n + * lseq n n * 3: - * range n n n - * range n 'to' n - * range n 'count' n - * range n 'by' n + * lseq n n n + * lseq n 'to' n + * lseq n 'count' n + * lseq n 'by' n * 4: - * range n 'to' n n - * range n n 'by' n - * range n 'count' n n + * lseq n 'to' n n + * lseq n n 'by' n + * lseq n 'count' n n * 5: - * range n 'to' n 'by' n - * range n 'count' n 'by' n + * lseq n 'to' n 'by' n + * lseq n 'count' n 'by' n * * Results: * A standard Tcl object result. @@ -4269,17 +4270,17 @@ Tcl_LseqObjCmd( case 121: opmode = (SequenceOperators)values[1]; switch (opmode) { - case RANGE_DOTS: - case RANGE_TO: + case LSEQ_DOTS: + case LSEQ_TO: start = numValues[0]; end = numValues[2]; break; - case RANGE_BY: + case LSEQ_BY: start = zero; elementCount = numValues[0]; step = numValues[2]; break; - case RANGE_COUNT: + case LSEQ_COUNT: start = numValues[0]; elementCount = numValues[2]; step = one; @@ -4295,18 +4296,18 @@ Tcl_LseqObjCmd( case 1211: opmode = (SequenceOperators)values[1]; switch (opmode) { - case RANGE_DOTS: - case RANGE_TO: + case LSEQ_DOTS: + case LSEQ_TO: start = numValues[0]; end = numValues[2]; step = numValues[3]; break; - case RANGE_COUNT: + case LSEQ_COUNT: start = numValues[0]; elementCount = numValues[2]; step = numValues[3]; break; - case RANGE_BY: + case LSEQ_BY: /* Error case */ status = TCL_ERROR; goto done; @@ -4324,12 +4325,12 @@ Tcl_LseqObjCmd( end = numValues[1]; opmode = (SequenceOperators)values[2]; switch (opmode) { - case RANGE_BY: + case LSEQ_BY: step = numValues[3]; break; - case RANGE_DOTS: - case RANGE_TO: - case RANGE_COUNT: + case LSEQ_DOTS: + case LSEQ_TO: + case LSEQ_COUNT: default: status = TCL_ERROR; goto done; @@ -4343,7 +4344,7 @@ Tcl_LseqObjCmd( start = numValues[0]; opmode = (SequenceOperators)values[3]; switch (opmode) { - case RANGE_BY: + case LSEQ_BY: step = numValues[4]; break; default: @@ -4353,12 +4354,12 @@ Tcl_LseqObjCmd( } opmode = (SequenceOperators)values[1]; switch (opmode) { - case RANGE_DOTS: - case RANGE_TO: + case LSEQ_DOTS: + case LSEQ_TO: start = numValues[0]; end = numValues[2]; break; - case RANGE_COUNT: + case LSEQ_COUNT: start = numValues[0]; elementCount = numValues[2]; break; @@ -4379,16 +4380,16 @@ Tcl_LseqObjCmd( KeywordError: status = TCL_ERROR; switch (opmode) { - case RANGE_DOTS: - case RANGE_TO: + case LSEQ_DOTS: + case LSEQ_TO: Tcl_SetObjResult(interp, Tcl_ObjPrintf( "missing \"to\" value.")); break; - case RANGE_COUNT: + case LSEQ_COUNT: Tcl_SetObjResult(interp, Tcl_ObjPrintf( "missing \"count\" value.")); break; - case RANGE_BY: + case LSEQ_BY: Tcl_SetObjResult(interp, Tcl_ObjPrintf( "missing \"by\" value.")); break; diff --git a/generic/tclExecute.c b/generic/tclExecute.c index d4bba5e..2df2611 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -19,6 +19,7 @@ #include "tclCompile.h" #include "tclOOInt.h" #include "tclTomMath.h" +#include "tclArithSeries.h" #include <math.h> #include <assert.h> @@ -4868,15 +4869,17 @@ TEBCresume( /* special case for ArithSeries */ if (TclHasInternalRep(valuePtr,&tclArithSeriesType)) { - ArithSeries *arithSeriesRepPtr = - (ArithSeries*) valuePtr->internalRep.twoPtrValue.ptr1; - length = arithSeriesRepPtr->len; + length = TclArithSeriesObjLength(valuePtr); if (TclGetIntForIndexM(interp, value2Ptr, length-1, &index)!=TCL_OK) { CACHE_STACK_INFO(); TRACE_ERROR(interp); goto gotError; } - objResultPtr = Tcl_NewWideIntObj(ArithSeriesIndexM(arithSeriesRepPtr, index)); + if (TclArithSeriesObjIndex(valuePtr, index, &objResultPtr) != TCL_OK) { + CACHE_STACK_INFO(); + TRACE_ERROR(interp); + goto gotError; + } goto lindexDone; } @@ -4928,9 +4931,7 @@ TEBCresume( /* special case for ArithSeries */ if (TclHasInternalRep(valuePtr,&tclArithSeriesType)) { - ArithSeries *arithSeriesRepPtr = - (ArithSeries*) valuePtr->internalRep.twoPtrValue.ptr1; - length = arithSeriesRepPtr->len; + length = TclArithSeriesObjLength(valuePtr); /* Decode end-offset index values. */ @@ -4938,7 +4939,11 @@ TEBCresume( /* Compute value @ index */ if (index >= 0 && index < length) { - objResultPtr = Tcl_NewWideIntObj(ArithSeriesIndexM(arithSeriesRepPtr, index)); + if (TclArithSeriesObjIndex(valuePtr, index, &objResultPtr) != TCL_OK) { + CACHE_STACK_INFO(); + TRACE_ERROR(interp); + goto gotError; + } } else { TclNewObj(objResultPtr); } diff --git a/generic/tclInt.h b/generic/tclInt.h index 95abe4c..eebf7ea 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -2480,44 +2480,6 @@ typedef struct List { (((listPtr)->typePtr == &tclListType) ? ListObjIsCanonical((listPtr)) : 0) /* - * The structure used for the AirthSeries internal representation. - * Note that the len can in theory be always computed by start,end,step - * but it's faster to cache it inside the internal representation. - */ -typedef struct ArithSeries { - Tcl_WideInt start; - Tcl_WideInt end; - Tcl_WideInt step; - Tcl_WideInt len; - Tcl_Obj **elements; - int isDouble; -} ArithSeries; -typedef struct ArithSeriesDbl { - double start; - double end; - double step; - Tcl_WideInt len; - Tcl_Obj **elements; - int isDouble; -} ArithSeriesDbl; - -#define ArithSeriesRepPtr(arithSeriesObjPtr) \ - (ArithSeries *) ((arithSeriesObjPtr)->internalRep.twoPtrValue.ptr1) - -#define ArithSeriesIndexM(arithSeriesRepPtr, index) \ - ((arithSeriesRepPtr)->isDouble ? \ - (((ArithSeriesDbl*)(arithSeriesRepPtr))->start+((index) * ((ArithSeriesDbl*)(arithSeriesRepPtr))->step)) \ - : \ - ((arithSeriesRepPtr)->start+((index) * arithSeriesRepPtr->step))) - -#define ArithSeriesStepM(arithSeriesRepPtr) \ - ((arithSeriesRepPtr)->isDouble ? \ - ((ArithSeriesDbl*)(arithSeriesRepPtr))->step \ - : \ - (arithSeriesRepPtr)->step) - - -/* * Modes for collecting (or not) in the implementations of TclNRForeachCmd, * TclNRLmapCmd and their compilations. */ @@ -2959,25 +2921,6 @@ MODULE_SCOPE void TclArgumentBCRelease(Tcl_Interp *interp, CmdFrame *cfPtr); MODULE_SCOPE void TclArgumentGet(Tcl_Interp *interp, Tcl_Obj *obj, CmdFrame **cfPtrPtr, int *wordPtr); -MODULE_SCOPE Tcl_Obj * TclArithSeriesObjCopy(Tcl_Interp *interp, - Tcl_Obj *arithSeriesPtr); -MODULE_SCOPE int TclArithSeriesObjStep(Tcl_Obj *arithSeriesPtr, - Tcl_Obj **stepObj); -MODULE_SCOPE int TclArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, - Tcl_WideInt index, Tcl_Obj **elementObj); -MODULE_SCOPE Tcl_WideInt TclArithSeriesObjLength(Tcl_Obj *arithSeriesPtr); -MODULE_SCOPE Tcl_Obj * TclArithSeriesObjRange(Tcl_Obj *arithSeriesPtr, - int fromIdx, int toIdx); -MODULE_SCOPE Tcl_Obj * TclArithSeriesObjReverse(Tcl_Obj *arithSeriesPtr); -MODULE_SCOPE int TclArithSeriesGetElements(Tcl_Interp *interp, - Tcl_Obj *objPtr, int *objcPtr, Tcl_Obj ***objvPtr); -MODULE_SCOPE Tcl_Obj * TclNewArithSeriesInt(Tcl_WideInt start, - Tcl_WideInt end, Tcl_WideInt step, - Tcl_WideInt len); -MODULE_SCOPE Tcl_Obj * TclNewArithSeriesDbl(double start, double end, - double step, Tcl_WideInt len); -MODULE_SCOPE Tcl_Obj * TclNewArithSeriesObj(int useDoubles, Tcl_Obj *startObj, - Tcl_Obj *endObj, Tcl_Obj *stepObj, Tcl_Obj *lenObj); MODULE_SCOPE int TclAsyncNotifier(int sigNumber, Tcl_ThreadId threadId, ClientData clientData, int *flagPtr, int value); MODULE_SCOPE void TclAsyncMarkFromNotifier(void); diff --git a/generic/tclListObj.c b/generic/tclListObj.c index 4366782..74b3a29 100644 --- a/generic/tclListObj.c +++ b/generic/tclListObj.c @@ -11,8 +11,9 @@ * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ -#include "tclInt.h" #include <assert.h> +#include "tclInt.h" +#include "tclArithSeries.h" /* * Prototypes for functions defined later in this file: @@ -68,14 +69,6 @@ const Tcl_ObjType tclListType = { #define ListResetInternalRep(objPtr, listRepPtr) \ TclFetchInternalRep((objPtr), &tclListType)->twoPtrValue.ptr1 = (listRepPtr) -#define ArithSeriesGetInternalRep(objPtr, arithRepPtr) \ - do { \ - const Tcl_ObjInternalRep *irPtr; \ - irPtr = TclFetchInternalRep((objPtr), &tclArithSeriesType); \ - (arithRepPtr) = irPtr ? (ArithSeries *)irPtr->twoPtrValue.ptr1 : NULL; \ - } while (0) - - #ifndef TCL_MIN_ELEMENT_GROWTH #define TCL_MIN_ELEMENT_GROWTH TCL_MIN_GROWTH/sizeof(Tcl_Obj *) #endif @@ -2050,16 +2043,15 @@ SetListFromAny( * and may occur frequently. */ Tcl_WideInt wideLen = TclArithSeriesObjLength(objPtr), j; - ArithSeries *arithSeriesRepPtr = (ArithSeries*) - objPtr->internalRep.twoPtrValue.ptr1; listRepPtr = AttemptNewList(interp, wideLen, NULL); if (listRepPtr == NULL) { return TCL_ERROR; } elemPtrs = listRepPtr->elements; for (j = 0; j < wideLen; j++) { - elemPtrs[j] = Tcl_NewWideIntObj( - ArithSeriesIndexM(arithSeriesRepPtr, j)); //->start+(j*arithSeriesRepPtr->step)); + if (TclArithSeriesObjIndex(objPtr, j, &elemPtrs[j]) != TCL_OK) { + return TCL_ERROR; + } Tcl_IncrRefCount(elemPtrs[j]);/* Since list now holds ref to it. */ } listRepPtr->elemCount = wideLen; @@ -2239,919 +2231,6 @@ UpdateStringOfList( ckfree(flagPtr); } } -/* -------------------------- ArithSeries object ---------------------------- */ - -/* - * Prototypes for procedures defined later in this file: - */ - -static void DupArithSeriesInternalRep (Tcl_Obj *srcPtr, Tcl_Obj *copyPtr); -static void FreeArithSeriesInternalRep (Tcl_Obj *listPtr); -static int SetArithSeriesFromAny (Tcl_Interp *interp, Tcl_Obj *objPtr); -static void UpdateStringOfArithSeries (Tcl_Obj *listPtr); - -/* - * The structure below defines the arithmetic series Tcl object type by - * means of procedures that can be invoked by generic object code. - * - * The arithmetic series object is a special case of Tcl list representing - * an interval of an arithmetic series in constant space. - * - * The arithmetic series is internally represented with three integers, - * *start*, *end*, and *step*, Where the length is calculated with - * the following algorithm: - * - * if RANGE == 0 THEN - * ERROR - * if RANGE > 0 - * LEN is (((END-START)-1)/STEP) + 1 - * else if RANGE < 0 - * LEN is (((END-START)-1)/STEP) - 1 - * - * And where the equivalent's list I-th element is calculated - * as: - * - * LIST[i] = START+(STEP*i) - * - * Zero elements ranges, like in the case of START=10 END=10 STEP=1 - * are valid and will be equivalent to the empty list. - */ - -const Tcl_ObjType tclArithSeriesType = { - "arithseries", /* name */ - FreeArithSeriesInternalRep, /* freeIntRepProc */ - DupArithSeriesInternalRep, /* dupIntRepProc */ - UpdateStringOfArithSeries, /* updateStringProc */ - SetArithSeriesFromAny /* setFromAnyProc */ -}; - -/* - *---------------------------------------------------------------------- - * - * ArithSeriesLen -- - * - * Compute the length of the equivalent list where - * every element is generated starting from *start*, - * and adding *step* to generate every successive element - * that's < *end* for positive steps, or > *end* for negative - * steps. - * - * Results: - * - * The length of the list generated by the given range, - * that may be zero. - * The function returns -1 if the list is of length infiite. - * - * Side effects: - * - * None. - * - *---------------------------------------------------------------------- - */ -static Tcl_WideInt -ArithSeriesLen(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step) -{ - Tcl_WideInt len; - - if (step == 0) return 0; - len = (step ? (1 + (((end-start))/step)) : 0); - return (len < 0) ? -1 : len; -} - -/* - *---------------------------------------------------------------------- - * - * TclNewArithSeriesInt -- - * - * Creates a new ArithSeries object. The returned object has - * refcount = 0. - * - * Results: - * - * A Tcl_Obj pointer to the created ArithSeries object. - * A NULL pointer of the range is invalid. - * - * Side Effects: - * - * None. - *---------------------------------------------------------------------- - */ -Tcl_Obj * -TclNewArithSeriesInt(Tcl_WideInt start, Tcl_WideInt end, Tcl_WideInt step, Tcl_WideInt len) -{ - Tcl_WideInt length = (len>=0 ? len : ArithSeriesLen(start, end, step)); - Tcl_Obj *arithSeriesPtr; - ArithSeries *arithSeriesRepPtr; - - TclNewObj(arithSeriesPtr); - - if (length <= 0) { - return arithSeriesPtr; - } - - arithSeriesRepPtr = (ArithSeries*) ckalloc(sizeof (ArithSeries)); - arithSeriesRepPtr->isDouble = 0; - arithSeriesRepPtr->start = start; - arithSeriesRepPtr->end = end; - arithSeriesRepPtr->step = step; - arithSeriesRepPtr->len = length; - arithSeriesRepPtr->elements = NULL; - arithSeriesPtr->internalRep.twoPtrValue.ptr1 = arithSeriesRepPtr; - arithSeriesPtr->internalRep.twoPtrValue.ptr2 = NULL; - arithSeriesPtr->typePtr = &tclArithSeriesType; - if (length > 0) - Tcl_InvalidateStringRep(arithSeriesPtr); - - return arithSeriesPtr; -} - -/* - *---------------------------------------------------------------------- - * - * TclNewArithSeriesDbl -- - * - * Creates a new ArithSeries object with doubles. The returned object has - * refcount = 0. - * - * Results: - * - * A Tcl_Obj pointer to the created ArithSeries object. - * A NULL pointer of the range is invalid. - * - * Side Effects: - * - * None. - *---------------------------------------------------------------------- - */ -Tcl_Obj * -TclNewArithSeriesDbl(double start, double end, double step, Tcl_WideInt len) -{ - Tcl_WideInt length = (len>=0 ? len : ArithSeriesLen(start, end, step)); - Tcl_Obj *arithSeriesPtr; - ArithSeriesDbl *arithSeriesRepPtr; - - TclNewObj(arithSeriesPtr); - - if (length <= 0) { - return arithSeriesPtr; - } - - arithSeriesRepPtr = (ArithSeriesDbl*) ckalloc(sizeof (ArithSeriesDbl)); - arithSeriesRepPtr->isDouble = 1; - arithSeriesRepPtr->start = start; - arithSeriesRepPtr->end = end; - arithSeriesRepPtr->step = step; - arithSeriesRepPtr->len = length; - arithSeriesRepPtr->elements = NULL; - arithSeriesPtr->internalRep.twoPtrValue.ptr1 = arithSeriesRepPtr; - arithSeriesPtr->internalRep.twoPtrValue.ptr2 = NULL; - arithSeriesPtr->typePtr = &tclArithSeriesType; - if (length > 0) - Tcl_InvalidateStringRep(arithSeriesPtr); - - return arithSeriesPtr; -} - -/* - *---------------------------------------------------------------------- - * - * assignNumber -- - * - * Create the approprite Tcl_Obj value for the given numeric values. - * Used locally only for decoding [lseq] numeric arguments. - * refcount = 0. - * - * Results: - * - * A Tcl_Obj pointer. - * No assignment on error. - * - * Side Effects: - * - * None. - *---------------------------------------------------------------------- - */ -static void -assignNumber(int useDoubles, Tcl_WideInt *intNumberPtr, double *dblNumberPtr, Tcl_Obj *numberObj) -{ - union { - double d; - Tcl_WideInt i; - } *number; - int tcl_number_type; - - if (TclGetNumberFromObj(NULL, numberObj, (ClientData*)&number, &tcl_number_type) != TCL_OK) { - return; - } - if (useDoubles) { - if (tcl_number_type == TCL_NUMBER_DOUBLE) { - *dblNumberPtr = number->d; - } else { - *dblNumberPtr = (double)number->i; - } - } else { - if (tcl_number_type == TCL_NUMBER_INT) { - *intNumberPtr = number->i; - } else { - *intNumberPtr = (Tcl_WideInt)number->d; - } - } -} - -/* - *---------------------------------------------------------------------- - * - * TclNewArithSeriesObj -- - * - * Creates a new ArithSeries object. Some arguments may be NULL and will - * be computed based on the other given arguments. - * refcount = 0. - * - * Results: - * - * A Tcl_Obj pointer to the created ArithSeries object. - * An empty Tcl_Obj if the range is invalid. - * - * Side Effects: - * - * None. - *---------------------------------------------------------------------- - */ -Tcl_Obj * -TclNewArithSeriesObj(int useDoubles, Tcl_Obj *startObj, Tcl_Obj *endObj, Tcl_Obj *stepObj, Tcl_Obj *lenObj) -{ - double dstart, dend, dstep; - Tcl_WideInt start, end, step, len; - - if (startObj) { - assignNumber(useDoubles, &start, &dstart, startObj); - } else { - start = 0; - dstart = start; - } - if (stepObj) { - assignNumber(useDoubles, &step, &dstep, stepObj); - if (useDoubles) { - step = dstep; - } else { - dstep = step; - } - if (dstep == 0) { - return Tcl_NewObj(); - } - } - if (endObj) { - assignNumber(useDoubles, &end, &dend, endObj); - } - if (lenObj) { - Tcl_GetWideIntFromObj(NULL, lenObj, &len); - } - - if (startObj && endObj) { - if (!stepObj) { - if (useDoubles) { - dstep = (dstart < dend) ? 1.0 : -1.0; - step = dstep; - } else { - step = (start < end) ? 1 : -1; - dstep = step; - } - } - assert(dstep!=0); - if (!lenObj) { - if (useDoubles) { - len = (dend - dstart + dstep)/dstep; - } else { - len = (end - start + step)/step; - } - } - } - - if (!endObj) { - if (useDoubles) { - dend = dstart + (dstep * (len-1)); - end = dend; - } else { - end = start + (step * (len-1)); - dend = end; - } - } - - if (useDoubles) { - return TclNewArithSeriesDbl(dstart, dend, dstep, len); - } else { - return TclNewArithSeriesInt(start, end, step, len); - } -} - -/* - *---------------------------------------------------------------------- - * - * TclArithSeriesObjStep -- - * - * Return a Tcl_Obj with the step value from the give ArithSeries Obj. - * refcount = 0. - * - * Results: - * - * A Tcl_Obj pointer to the created ArithSeries object. - * A NULL pointer of the range is invalid. - * - * Side Effects: - * - * None. - *---------------------------------------------------------------------- - */ -/* - * TclArithSeriesObjStep -- - */ -int -TclArithSeriesObjStep( - Tcl_Obj *arithSeriesPtr, - Tcl_Obj **stepObj) -{ - ArithSeries *arithSeriesRepPtr; - - if (arithSeriesPtr->typePtr != &tclArithSeriesType) { - Tcl_Panic("TclArithSeriesObjIndex called with a not ArithSeries Obj."); - } - arithSeriesRepPtr = ArithSeriesRepPtr(arithSeriesPtr); - if (arithSeriesRepPtr->isDouble) { - *stepObj = Tcl_NewDoubleObj(((ArithSeriesDbl*)(arithSeriesRepPtr))->step); - } else { - *stepObj = Tcl_NewWideIntObj(arithSeriesRepPtr->step); - } - return TCL_OK; -} - - -/* - *---------------------------------------------------------------------- - * - * TclArithSeriesObjIndex -- - * - * Returns the element with the specified index in the list - * represented by the specified Arithmentic Sequence object. - * If the index is out of range, TCL_ERROR is returned, - * otherwise TCL_OK is returned and the integer value of the - * element is stored in *element. - * - * Results: - * - * TCL_OK on succes, TCL_ERROR on index out of range. - * - * Side Effects: - * - * On success, the integer pointed by *element is modified. - * - *---------------------------------------------------------------------- - */ - -int -TclArithSeriesObjIndex(Tcl_Obj *arithSeriesPtr, Tcl_WideInt index, Tcl_Obj **elementObj) -{ - ArithSeries *arithSeriesRepPtr; - - if (arithSeriesPtr->typePtr != &tclArithSeriesType) { - Tcl_Panic("TclArithSeriesObjIndex called with a not ArithSeries Obj."); - } - arithSeriesRepPtr = ArithSeriesRepPtr(arithSeriesPtr); - if (index < 0 || index >= arithSeriesRepPtr->len) { - return TCL_ERROR; - } - /* List[i] = Start + (Step * index) */ - if (arithSeriesRepPtr->isDouble) { - *elementObj = Tcl_NewDoubleObj(ArithSeriesIndexM(arithSeriesRepPtr, index)); - } else { - *elementObj = Tcl_NewWideIntObj(ArithSeriesIndexM(arithSeriesRepPtr, index)); - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * TclArithSeriesObjLength - * - * Returns the length of the arithmentic series. - * - * Results: - * - * The length of the series as Tcl_WideInt. - * - * Side Effects: - * - * None. - * - *---------------------------------------------------------------------- - */ -Tcl_WideInt TclArithSeriesObjLength(Tcl_Obj *arithSeriesPtr) -{ - ArithSeries *arithSeriesRepPtr = (ArithSeries*) - arithSeriesPtr->internalRep.twoPtrValue.ptr1; - return arithSeriesRepPtr->len; -} - -/* - *---------------------------------------------------------------------- - * - * FreeArithSeriesInternalRep -- - * - * Deallocate the storage associated with an arithseries object's - * internal representation. - * - * Results: - * None. - * - * Side effects: - * Frees arithSeriesPtr's ArithSeries* internal representation and - * sets listPtr's internalRep.twoPtrValue.ptr1 to NULL. - * - *---------------------------------------------------------------------- - */ - -static void -FreeArithSeriesInternalRep(Tcl_Obj *arithSeriesPtr) -{ - ArithSeries *arithSeriesRepPtr = - (ArithSeries *) arithSeriesPtr->internalRep.twoPtrValue.ptr1; - if (arithSeriesRepPtr->elements) { - Tcl_WideInt i; - Tcl_Obj**elmts = arithSeriesRepPtr->elements; - for(i=0; i<arithSeriesRepPtr->len; i++) { - if (elmts[i]) { - Tcl_DecrRefCount(elmts[i]); - } - } - ckfree((char *) arithSeriesRepPtr->elements); - } - ckfree((char *) arithSeriesRepPtr); - arithSeriesPtr->internalRep.twoPtrValue.ptr1 = NULL; -} - -/* - *---------------------------------------------------------------------- - * - * DupArithSeriesInternalRep -- - * - * Initialize the internal representation of a arithseries Tcl_Obj to a - * copy of the internal representation of an existing arithseries object. - * - * Results: - * None. - * - * Side effects: - * We set "copyPtr"s internal rep to a pointer to a - * newly allocated ArithSeries structure. - *---------------------------------------------------------------------- - */ - -static void -DupArithSeriesInternalRep(srcPtr, copyPtr) - Tcl_Obj *srcPtr; /* Object with internal rep to copy. */ - Tcl_Obj *copyPtr; /* Object with internal rep to set. */ -{ - ArithSeries *srcArithSeriesRepPtr = - (ArithSeries *) srcPtr->internalRep.twoPtrValue.ptr1; - ArithSeries *copyArithSeriesRepPtr; - - /* - * Allocate a new ArithSeries structure. */ - - copyArithSeriesRepPtr = (ArithSeries*) ckalloc(sizeof(ArithSeries)); - *copyArithSeriesRepPtr = *srcArithSeriesRepPtr; - copyArithSeriesRepPtr->elements = NULL; - copyPtr->internalRep.twoPtrValue.ptr1 = copyArithSeriesRepPtr; - copyPtr->internalRep.twoPtrValue.ptr2 = NULL; - copyPtr->typePtr = &tclArithSeriesType; -} - -/* - *---------------------------------------------------------------------- - * - * UpdateStringOfArithSeries -- - * - * Update the string representation for an arithseries 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 list-to-string conversion. This string will be empty if the - * list has no elements. The list internal representation - * should not be NULL and we assume it is not NULL. - * - * Notes: - * At the cost of overallocation it's possible to estimate - * the length of the string representation and make this procedure - * much faster. Because the programmer shouldn't expect the - * string conversion of a big arithmetic sequence to be fast - * this version takes more care of space than time. - * - *---------------------------------------------------------------------- - */ - -static void -UpdateStringOfArithSeries(Tcl_Obj *arithSeriesPtr) -{ - ArithSeries *arithSeriesRepPtr = - (ArithSeries*) arithSeriesPtr->internalRep.twoPtrValue.ptr1; - char *elem, *p; - Tcl_Obj *elemObj; - Tcl_WideInt i; - Tcl_WideInt length = 0; - int slen; - - /* - * Pass 1: estimate space. - */ - for (i = 0; i < arithSeriesRepPtr->len; i++) { - TclArithSeriesObjIndex(arithSeriesPtr, i, &elemObj); - elem = TclGetStringFromObj(elemObj, &slen); - Tcl_DecrRefCount(elemObj); - slen += 1; /* + 1 is for the space or the nul-term */ - length += slen; - } - - /* - * Pass 2: generate the string repr. - */ - - p = Tcl_InitStringRep(arithSeriesPtr, NULL, length); - for (i = 0; i < arithSeriesRepPtr->len; i++) { - TclArithSeriesObjIndex(arithSeriesPtr, i, &elemObj); - elem = TclGetStringFromObj(elemObj, &slen); - strcpy(p, elem); - p[slen] = ' '; - p += slen+1; - Tcl_DecrRefCount(elemObj); - } - if (length > 0) arithSeriesPtr->bytes[length-1] = '\0'; - arithSeriesPtr->length = length-1; -} - -/* - *---------------------------------------------------------------------- - * - * SetArithSeriesFromAny -- - * - * The Arithmetic Series object is just an way to optimize - * Lists space complexity, so no one should try to convert - * a string to an Arithmetic Series object. - * - * This function is here just to populate the Type structure. - * - * Results: - * - * The result is always TCL_ERROR. But see Side Effects. - * - * Side effects: - * - * Tcl Panic if called. - * - *---------------------------------------------------------------------- - */ - -static int -SetArithSeriesFromAny(interp, objPtr) - Tcl_Interp *interp; /* Used for error reporting if not NULL. */ - Tcl_Obj *objPtr; /* The object to convert. */ -{ - (void)interp; - (void)objPtr; - Tcl_Panic("SetArithSeriesFromAny: should never be called"); - return TCL_ERROR; -} - -/* - *---------------------------------------------------------------------- - * - * TclArithSeriesObjCopy -- - * - * Makes a "pure arithSeries" copy of an ArithSeries value. This provides for the C - * level a counterpart of the [lrange $list 0 end] command, while using - * internals details to be as efficient as possible. - * - * Results: - * - * Normally returns a pointer to a new Tcl_Obj, that contains the same - * arithSeries value as *arithSeriesPtr does. The returned Tcl_Obj has a - * refCount of zero. If *arithSeriesPtr does not hold an arithSeries, - * NULL is returned, and if interp is non-NULL, an error message is - * recorded there. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclArithSeriesObjCopy( - Tcl_Interp *interp, /* Used to report errors if not NULL. */ - Tcl_Obj *arithSeriesPtr) /* List object for which an element array is - * to be returned. */ -{ - Tcl_Obj *copyPtr; - ArithSeries *arithSeriesRepPtr; - - ArithSeriesGetInternalRep(arithSeriesPtr, arithSeriesRepPtr); - if (NULL == arithSeriesRepPtr) { - if (SetArithSeriesFromAny(interp, arithSeriesPtr) != TCL_OK) { - /* We know this is going to panic, but it's the message we want */ - return NULL; - } - } - - TclNewObj(copyPtr); - TclInvalidateStringRep(copyPtr); - DupArithSeriesInternalRep(arithSeriesPtr, copyPtr); - return copyPtr; -} - -/* - *---------------------------------------------------------------------- - * - * TclArithSeriesObjRange -- - * - * Makes a slice of an ArithSeries value. - * *arithSeriesPtr must be known to be a valid list. - * - * Results: - * Returns a pointer to the sliced series. - * This may be a new object or the same object if not shared. - * - * Side effects: - * ?The possible conversion of the object referenced by listPtr? - * ?to a list object.? - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclArithSeriesObjRange( - Tcl_Obj *arithSeriesPtr, /* List object to take a range from. */ - int fromIdx, /* Index of first element to include. */ - int toIdx) /* Index of last element to include. */ -{ - ArithSeries *arithSeriesRepPtr; - Tcl_Obj *startObj, *endObj, *stepObj; - - ArithSeriesGetInternalRep(arithSeriesPtr, arithSeriesRepPtr); - - if (fromIdx < 0) { - fromIdx = 0; - } - if (fromIdx > toIdx) { - Tcl_Obj *obj; - TclNewObj(obj); - return obj; - } - - TclArithSeriesObjIndex(arithSeriesPtr, fromIdx, &startObj); - Tcl_IncrRefCount(startObj); - TclArithSeriesObjIndex(arithSeriesPtr, toIdx, &endObj); - Tcl_IncrRefCount(endObj); - TclArithSeriesObjStep(arithSeriesPtr, &stepObj); - Tcl_IncrRefCount(stepObj); - - if (Tcl_IsShared(arithSeriesPtr) || - ((arithSeriesPtr->refCount > 1))) { - Tcl_Obj *newSlicePtr = TclNewArithSeriesObj(arithSeriesRepPtr->isDouble, - startObj, endObj, stepObj, NULL); - Tcl_DecrRefCount(startObj); - Tcl_DecrRefCount(endObj); - Tcl_DecrRefCount(stepObj); - return newSlicePtr; - } - - /* - * In-place is possible. - */ - - /* - * Even if nothing below cause any changes, we still want the - * string-canonizing effect of [lrange 0 end]. - */ - - TclInvalidateStringRep(arithSeriesPtr); - - if (arithSeriesRepPtr->isDouble) { - ArithSeriesDbl *arithSeriesDblRepPtr = (ArithSeriesDbl*)arithSeriesPtr; - double start, end, step; - Tcl_GetDoubleFromObj(NULL, startObj, &start); - Tcl_GetDoubleFromObj(NULL, endObj, &end); - Tcl_GetDoubleFromObj(NULL, stepObj, &step); - arithSeriesDblRepPtr->start = start; - arithSeriesDblRepPtr->end = end; - arithSeriesDblRepPtr->step = step; - arithSeriesDblRepPtr->len = (end-start+step)/step; - arithSeriesDblRepPtr->elements = NULL; - - } else { - Tcl_WideInt start, end, step; - Tcl_GetWideIntFromObj(NULL, startObj, &start); - Tcl_GetWideIntFromObj(NULL, endObj, &end); - Tcl_GetWideIntFromObj(NULL, stepObj, &step); - arithSeriesRepPtr->start = start; - arithSeriesRepPtr->end = end; - arithSeriesRepPtr->step = step; - arithSeriesRepPtr->len = (end-start+step)/step; - arithSeriesRepPtr->elements = NULL; - } - - Tcl_DecrRefCount(startObj); - Tcl_DecrRefCount(endObj); - Tcl_DecrRefCount(stepObj); - - return arithSeriesPtr; -} - -/* - *---------------------------------------------------------------------- - * - * TclArithSeriesGetElements -- - * - * This function returns an (objc,objv) array of the elements in a list - * object. - * - * Results: - * The return value is normally TCL_OK; in this case *objcPtr is set to - * the count of list elements and *objvPtr is set to a pointer to an - * array of (*objcPtr) pointers to each list element. If listPtr does not - * refer to an Abstract List object and the object can not be converted - * to one, TCL_ERROR is returned and an error message will be left in the - * interpreter's result if interp is not NULL. - * - * The objects referenced by the returned array should be treated as - * readonly and their ref counts are _not_ incremented; the caller must - * do that if it holds on to a reference. Furthermore, the pointer and - * length returned by this function may change as soon as any function is - * called on the list object; be careful about retaining the pointer in a - * local data structure. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -TclArithSeriesGetElements( - Tcl_Interp *interp, /* Used to report errors if not NULL. */ - Tcl_Obj *objPtr, /* AbstractList object for which an element - * array is to be returned. */ - int *objcPtr, /* Where to store the count of objects - * referenced by objv. */ - Tcl_Obj ***objvPtr) /* Where to store the pointer to an array of - * pointers to the list's objects. */ -{ - if (TclHasInternalRep(objPtr,&tclArithSeriesType)) { - ArithSeries *arithSeriesRepPtr; - Tcl_Obj **objv; - int i, objc; - - ArithSeriesGetInternalRep(objPtr, arithSeriesRepPtr); - objc = arithSeriesRepPtr->len; - if (objc > 0) { - if (arithSeriesRepPtr->elements) { - /* If this exists, it has already been populated */ - objv = arithSeriesRepPtr->elements; - } else { - /* Construct the elements array */ - objv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj*) * objc); - if (objv == NULL) { - if (interp) { - Tcl_SetObjResult( - interp, - Tcl_NewStringObj("max length of a Tcl list exceeded", -1)); - Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); - } - return TCL_ERROR; - } - arithSeriesRepPtr->elements = objv; - for (i = 0; i < objc; i++) { - Tcl_WideInt wi = ArithSeriesIndexM(arithSeriesRepPtr, (Tcl_WideInt)i); - objv[i] = Tcl_NewWideIntObj(wi); - Tcl_IncrRefCount(objv[i]); - } - } - } else { - objv = NULL; - } - *objvPtr = objv; - *objcPtr = objc; - } else { - if (interp != NULL) { - Tcl_SetObjResult( - interp, - Tcl_ObjPrintf("value is not an arithseries")); - Tcl_SetErrorCode(interp, "TCL", "VALUE", "UNKNOWN", NULL); - } - return TCL_ERROR; - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * TclArithSeriesObjReverse -- - * - * Reverse the order of the ArithSeries value. - * *arithSeriesPtr must be known to be a valid list. - * - * Results: - * Returns a pointer to the reordered series. - * This may be a new object or the same object if not shared. - * - * Side effects: - * ?The possible conversion of the object referenced by listPtr? - * ?to a list object.? - * - *---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclArithSeriesObjReverse( - Tcl_Obj *arithSeriesPtr) /* List object to reverse. */ -{ - ArithSeries *arithSeriesRepPtr; - Tcl_Obj *startObj, *endObj, *stepObj; - Tcl_Obj *resultObj; - Tcl_WideInt start, end, step, len; - double dstart, dend, dstep; - int isDouble; - - ArithSeriesGetInternalRep(arithSeriesPtr, arithSeriesRepPtr); - - isDouble = arithSeriesRepPtr->isDouble; - len = arithSeriesRepPtr->len; - - TclArithSeriesObjIndex(arithSeriesPtr, (len-1), &startObj); - TclArithSeriesObjIndex(arithSeriesPtr, 0, &endObj); - TclArithSeriesObjStep(arithSeriesPtr, &stepObj); - - if (isDouble) { - Tcl_GetDoubleFromObj(NULL, startObj, &dstart); - Tcl_GetDoubleFromObj(NULL, endObj, &dend); - Tcl_GetDoubleFromObj(NULL, stepObj, &dstep); - dstep = -dstep; - TclSetDoubleObj(stepObj, dstep); - } else { - Tcl_GetWideIntFromObj(NULL, startObj, &start); - Tcl_GetWideIntFromObj(NULL, endObj, &end); - Tcl_GetWideIntFromObj(NULL, stepObj, &step); - step = -step; - TclSetIntObj(stepObj, step); - } - - if (Tcl_IsShared(arithSeriesPtr) || - ((arithSeriesPtr->refCount > 1))) { - Tcl_Obj *lenObj = Tcl_NewWideIntObj(len); - resultObj = TclNewArithSeriesObj(isDouble, - startObj, endObj, stepObj, lenObj); - Tcl_DecrRefCount(lenObj); - } else { - - /* - * In-place is possible. - */ - - TclInvalidateStringRep(arithSeriesPtr); - - if (isDouble) { - ArithSeriesDbl *arithSeriesDblRepPtr = - (ArithSeriesDbl*)arithSeriesRepPtr; - arithSeriesDblRepPtr->start = dstart; - arithSeriesDblRepPtr->end = dend; - arithSeriesDblRepPtr->step = dstep; - } else { - arithSeriesRepPtr->start = start; - arithSeriesRepPtr->end = end; - arithSeriesRepPtr->step = step; - } - if (arithSeriesRepPtr->elements) { - Tcl_WideInt i; - for (i=0; i<len; i++) { - Tcl_DecrRefCount(arithSeriesRepPtr->elements[i]); - } - ckfree((char*)arithSeriesRepPtr->elements); - } - arithSeriesRepPtr->elements = NULL; - - resultObj = arithSeriesPtr; - } - - Tcl_DecrRefCount(startObj); - Tcl_DecrRefCount(endObj); - Tcl_DecrRefCount(stepObj); - - return resultObj; -} /* diff --git a/unix/Makefile.in b/unix/Makefile.in index 30d9462..1769aa4 100644 --- a/unix/Makefile.in +++ b/unix/Makefile.in @@ -299,8 +299,8 @@ XTTEST_OBJS = xtTestInit.o tclTest.o tclTestObj.o tclTestProcBodyObj.o \ tclThreadTest.o tclUnixTest.o tclXtNotify.o tclXtTest.o GENERIC_OBJS = regcomp.o regexec.o regfree.o regerror.o tclAlloc.o \ - tclAssembly.o tclAsync.o tclBasic.o tclBinary.o tclCkalloc.o \ - tclClock.o tclCmdAH.o tclCmdIL.o tclCmdMZ.o \ + tclArithSeries.o tclAssembly.o tclAsync.o tclBasic.o tclBinary.o \ + tclCkalloc.o tclClock.o tclCmdAH.o tclCmdIL.o tclCmdMZ.o \ tclCompCmds.o tclCompCmdsGR.o tclCompCmdsSZ.o tclCompExpr.o \ tclCompile.o tclConfig.o tclDate.o tclDictObj.o tclDisassemble.o \ tclEncoding.o tclEnsemble.o \ @@ -393,7 +393,8 @@ GENERIC_HDRS = \ $(GENERIC_DIR)/tclPatch.h \ $(GENERIC_DIR)/tclPlatDecls.h \ $(GENERIC_DIR)/tclPort.h \ - $(GENERIC_DIR)/tclRegexp.h + $(GENERIC_DIR)/tclRegexp.h \ + $(GENERIC_DIR)/tclArithSeries.h GENERIC_SRCS = \ $(GENERIC_DIR)/regcomp.c \ @@ -401,6 +402,7 @@ GENERIC_SRCS = \ $(GENERIC_DIR)/regfree.c \ $(GENERIC_DIR)/regerror.c \ $(GENERIC_DIR)/tclAlloc.c \ + $(GENERIC_DIR)/tclArithSeries.c \ $(GENERIC_DIR)/tclAssembly.c \ $(GENERIC_DIR)/tclAsync.c \ $(GENERIC_DIR)/tclBasic.c \ @@ -1250,6 +1252,9 @@ tclAppInit.o: $(UNIX_DIR)/tclAppInit.c tclAlloc.o: $(GENERIC_DIR)/tclAlloc.c $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclAlloc.c +tclArithSeries.o: $(GENERIC_DIR)/tclArithSeries.c $(COMPILEHDR) + $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclArithSeries.c + tclAssembly.o: $(GENERIC_DIR)/tclAssembly.c $(COMPILEHDR) $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tclAssembly.c diff --git a/win/makefile.vc b/win/makefile.vc index 7c61580..f9c9242 100644 --- a/win/makefile.vc +++ b/win/makefile.vc @@ -238,6 +238,7 @@ COREOBJS = \ $(TMP_DIR)\regexec.obj \
$(TMP_DIR)\regfree.obj \
$(TMP_DIR)\tclAlloc.obj \
+ $(TMP_DIR)\tclArithSeries.obj \
$(TMP_DIR)\tclAssembly.obj \
$(TMP_DIR)\tclAsync.obj \
$(TMP_DIR)\tclBasic.obj \
|