/* struct::queue - critcl - layer 3 definitions. * * -> Method functions. * Implementations for all queue methods. */ #include "util.h" #include "m.h" #include "q.h" #include "ms.h" static int qsize (Q* q, int* u, int* r, int* a); static void qshift (Q* q); #undef QUEUE_DUMP /*#define QUEUE_DUMP 1*/ #if QUEUE_DUMP static void qdump (Q* q); #else #define qdump(q) /* Ignore */ #endif /* .................................................. */ /* *--------------------------------------------------------------------------- * * qum_CLEAR -- * * Removes all elements currently on the queue. I.e empties the queue. * * Results: * A standard Tcl result code. * * Side effects: * Only internal, memory allocation changes ... * *--------------------------------------------------------------------------- */ int qum_CLEAR (Q* q, Tcl_Interp* interp, int objc, Tcl_Obj* CONST* objv) { /* Syntax: queue clear * [0] [1] */ if (objc != 2) { Tcl_WrongNumArgs (interp, 2, objv, NULL); return TCL_ERROR; } /* * Delete and recreate the queue memory. A combination of delete/new, * except the main structure is left unchanged */ Tcl_DecrRefCount (q->unget); Tcl_DecrRefCount (q->queue); Tcl_DecrRefCount (q->append); q->at = 0; q->unget = Tcl_NewListObj (0,NULL); q->queue = Tcl_NewListObj (0,NULL); q->append = Tcl_NewListObj (0,NULL); Tcl_IncrRefCount (q->unget); Tcl_IncrRefCount (q->queue); Tcl_IncrRefCount (q->append); return TCL_OK; } /* *--------------------------------------------------------------------------- * * qum_DESTROY -- * * Destroys the whole queue object. * * Results: * A standard Tcl result code. * * Side effects: * Releases memory. * *--------------------------------------------------------------------------- */ int qum_DESTROY (Q* q, Tcl_Interp* interp, int objc, Tcl_Obj* CONST* objv) { /* Syntax: queue destroy * [0] [1] */ if (objc != 2) { Tcl_WrongNumArgs (interp, 2, objv, NULL); return TCL_ERROR; } Tcl_DeleteCommandFromToken(interp, q->cmd); return TCL_OK; } /* *--------------------------------------------------------------------------- * * qum_PEEK/GET -- * * (Non-)destructively retrieves one or more elements from the top of the * queue. * * Results: * A standard Tcl result code. * * Side effects: * Only internal, memory allocation changes ... * *--------------------------------------------------------------------------- */ int qum_PEEK (Q* q, Tcl_Interp* interp, int objc, Tcl_Obj* CONST* objv, int get) { /* Syntax: queue peek|get ?n? * [0] [1] [2] */ int listc = 0; Tcl_Obj** listv; Tcl_Obj* r; int n = 1; int ungetc; int queuec; int appendc; if ((objc != 2) && (objc != 3)) { Tcl_WrongNumArgs (interp, 2, objv, "?n?"); return TCL_ERROR; } if (objc == 3) { if (Tcl_GetIntFromObj(interp, objv[2], &n) != TCL_OK) { return TCL_ERROR; } else if (n < 1) { Tcl_AppendResult (interp, "invalid item count ", Tcl_GetString (objv[2]), NULL); return TCL_ERROR; } } if (n > qsize(q, &ungetc, &queuec, &appendc)) { Tcl_AppendResult (interp, "insufficient items in queue to fill request", NULL); return TCL_ERROR; } /* 1. We have item on the unget stack * a. Enough to satisfy request. * b. Not enough. * 2. We have items in the return buffer. * a. Enough to satisfy request. * b. Not enough. * 3. We have items in the append buffer. * a. Enough to satisfy request. * b. Not enough. * * Case 3. can assume 2b, because an empty return buffer will be filled * from the append buffer before looking at either. Case 3. cannot happen * for n==1, the return buffer will contain at least one element. * * We distinguish between single and multi-element requests. * * XXX AK optimizations - If we can return everything from a single * buffer, be it queue, or append, just return the buffer object, do not * create something new. */ if (n == 1) { if (ungetc) { /* Pull from unget stack */ Tcl_ListObjGetElements (interp, q->unget, &listc, &listv); r = listv [listc-1]; Tcl_SetObjResult (interp, r); if (get) { /* XXX AK : Should maintain max size info, and proper index, for discard. */ Tcl_ListObjReplace (interp, q->unget, listc-1, 1, 0, NULL); } } else { qshift (q); Tcl_ListObjGetElements (interp, q->queue, &listc, &listv); ASSERT_BOUNDS(q->at,listc); r = listv [q->at]; Tcl_SetObjResult (interp, r); /* * Note: Doing the SetObj now is important. It increments the * refcount of 'r', allowing it to survive if the 'qshift' below * kills the internal list (q->queue) holding it. */ if (get) { q->at ++; qshift (q); } } } else { /* * Allocate buffer for result, then fill it using the various data * sources. */ int i = 0, j; Tcl_Obj** resv = NALLOC(n,Tcl_Obj*); if (ungetc) { Tcl_ListObjGetElements (interp, q->unget, &listc, &listv); /* * Note how we are iterating backward in listv. unget is managed * as a stack, avoiding mem-copy operations and both push and pop. */ for (j = listc-1; j >= 0 && i < n; j--, i++) { ASSERT_BOUNDS(i,n); ASSERT_BOUNDS(j,listc); resv[i] = listv[j]; Tcl_IncrRefCount (resv[i]); } if (get) { /* XXX AK : Should maintain max size info, and proper index, for discard. */ Tcl_ListObjReplace (interp, q->unget, j, i, 0, NULL); /* XXX CHECK index calcs. */ } } if (i < n) { qshift (q); Tcl_ListObjGetElements (interp, q->queue, &listc, &listv); for (j = q->at; j < listc && i < n; j++, i++) { ASSERT_BOUNDS(i,n); ASSERT_BOUNDS(j,listc); resv[i] = listv[j]; Tcl_IncrRefCount (resv[i]); } if (get) { q->at = j; qshift (q); } else if (i < n) { /* XX */ Tcl_ListObjGetElements (interp, q->append, &listc, &listv); for (j = 0; j < listc && i < n; j++, i++) { ASSERT_BOUNDS(i,n); ASSERT_BOUNDS(j,listc); resv[i] = listv[j]; Tcl_IncrRefCount (resv[i]); } } } /* * This can happend if and only if we have to pull data from append, * and get is set. Without get XX would have run and filled the result * to completion. */ if (i < n) { ASSERT(get,"Impossible 2nd return pull witohut get"); qshift (q); Tcl_ListObjGetElements (interp, q->queue, &listc, &listv); for (j = q->at; j < listc && i < n; j++, i++) { ASSERT_BOUNDS(i,n); ASSERT_BOUNDS(j,listc); resv[i] = listv[j]; Tcl_IncrRefCount (resv[i]); } q->at = j; qshift (q); } r = Tcl_NewListObj (n, resv); Tcl_SetObjResult (interp, r); for (i=0;iappend, objv[i]); } return TCL_OK; } /* *--------------------------------------------------------------------------- * * qum_UNGET -- * * Pushes an element back into the queue. * * Results: * A standard Tcl result code. * * Side effects: * May release and allocate memory. * *--------------------------------------------------------------------------- */ int qum_UNGET (Q* q, Tcl_Interp* interp, int objc, Tcl_Obj* CONST* objv) { /* Syntax: queue unget item * [0] [1] [2] */ int listc = 0; Tcl_Obj** listv = NULL; if (objc != 3) { Tcl_WrongNumArgs (interp, 2, objv, "item"); return TCL_ERROR; } if (q->at == 0) { /* Need the unget stack */ Tcl_ListObjAppendElement (interp, q->unget, objv[2]); } else { /* * We have room in the return buffer, so splice directly instead of * using the unget stack. */ q->at --; ASSERT_BOUNDS(q->at,listc); Tcl_ListObjReplace (interp, q->queue, q->at, 1, 1, &objv[2]); } return TCL_OK; } /* *--------------------------------------------------------------------------- * * qum_SIZE -- * * Returns the number of elements currently held by the queue. * * Results: * A standard Tcl result code. * * Side effects: * None. * *--------------------------------------------------------------------------- */ int qum_SIZE (Q* q, Tcl_Interp* interp, int objc, Tcl_Obj* CONST* objv) { /* Syntax: queue size * [0] [1] */ if ((objc != 2)) { Tcl_WrongNumArgs (interp, 2, objv, NULL); return TCL_ERROR; } Tcl_SetObjResult (interp, Tcl_NewIntObj (qsize (q, NULL, NULL, NULL))); return TCL_OK; } static int qsize (Q* q, int* u, int* r, int* a) { int ungetc = 0; int queuec = 0; int appendc = 0; Tcl_ListObjLength (NULL, q->unget, &ungetc); Tcl_ListObjLength (NULL, q->queue, &queuec); Tcl_ListObjLength (NULL, q->append, &appendc); if (u) *u = ungetc; if (r) *r = queuec; if (a) *a = appendc; return ungetc + queuec + appendc - q->at; } static void qshift (Q* q) { int queuec = 0; int appendc = 0; qdump (q); /* The queue is not done yet, no shift */ Tcl_ListObjLength (NULL, q->queue, &queuec); if (q->at < queuec) return; /* The queue is done, however there is nothing * to shift into it, so we don't */ Tcl_ListObjLength (NULL, q->append, &appendc); if (!appendc) return; q->at = 0; Tcl_DecrRefCount (q->queue); q->queue = q->append; q->append = Tcl_NewListObj (0,NULL); Tcl_IncrRefCount (q->append); qdump (q); } #ifdef QUEUE_DUMP static void qdump (Q* q) { int k; int listc = 0; Tcl_Obj** listv; fprintf(stderr,"qdump (%p, @%d)\n", q, q->at);fflush(stderr); fprintf(stderr,"\tunget %p\n", q->unget);fflush(stderr); Tcl_ListObjGetElements (NULL, q->unget, &listc, &listv); for (k=0; k < listc; k++) { fprintf(stderr,"\tunget %p [%d] = %p '%s' /%d\n", q->unget, k, listv[k], Tcl_GetString(listv[k]), listv[k]->refCount);fflush(stderr); } fprintf(stderr,"\tqueue %p\n", q->queue);fflush(stderr); Tcl_ListObjGetElements (NULL, q->queue, &listc, &listv); for (k=0; k < listc; k++) { fprintf(stderr,"\tqueue %p [%d] = %p '%s' /%d\n", q->queue, k, listv[k], Tcl_GetString(listv[k]), listv[k]->refCount);fflush(stderr); } fprintf(stderr,"\tapp.. %p\n", q->append);fflush(stderr); Tcl_ListObjGetElements (NULL, q->append, &listc, &listv); for (k=0; k < listc; k++) { fprintf(stderr,"\tapp.. %p [%d] = %p '%s' /%d\n", q->append, k, listv[k], Tcl_GetString(listv[k]), listv[k]->refCount);fflush(stderr); } fprintf(stderr,"qdump/ ___________________\n");fflush(stderr); } #endif /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */