summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgriffin <briang42@easystreet.net>2022-08-28 22:55:17 (GMT)
committergriffin <briang42@easystreet.net>2022-08-28 22:55:17 (GMT)
commit858a9e55021e040d64d6183b62ca8e8559f4538c (patch)
tree8abdec021760033e8fef7ab21721546ce81015d9
parentaddfa7b662b1382b33181c3c2f1dc945e42da4e1 (diff)
downloadtcl-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.c955
-rw-r--r--generic/tclArithSeries.h54
-rw-r--r--generic/tclCmdAH.c1
-rw-r--r--generic/tclCmdIL.c65
-rw-r--r--generic/tclExecute.c21
-rw-r--r--generic/tclInt.h57
-rw-r--r--generic/tclListObj.c931
-rw-r--r--unix/Makefile.in11
-rw-r--r--win/makefile.vc1
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 \