~ubuntu-branches/ubuntu/utopic/critcl/utopic

« back to all changes in this revision

Viewing changes to examples/queue/queuec.tcl

  • Committer: Package Import Robot
  • Author(s): Andrew Shadura
  • Date: 2013-05-11 00:08:06 UTC
  • Revision ID: package-import@ubuntu.com-20130511000806-7hq1zc3fnn0gat79
Tags: upstream-3.1.9
ImportĀ upstreamĀ versionĀ 3.1.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# queuec.tcl --
 
2
#
 
3
#       Implementation of a queue data structure for Tcl.
 
4
#       This code based on critcl v3.1, API compatible to the PTI [x].
 
5
#       [x] Pure Tcl Implementation.
 
6
#
 
7
# Mainly demonstrates the utility package for the creation of classes
 
8
# and objects in C, with both claaes and their instances represented
 
9
# as Tcl commands. In contrast to the stackc demo this does not use a
 
10
# separate data structure package, nor separately written method
 
11
# implementations.
 
12
#
 
13
# Copyright (c) 2012 Andreas Kupries <andreas_kupries@users.sourceforge.net>
 
14
#
 
15
# See the file "license.terms" for information on usage and redistribution
 
16
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 
17
#
 
18
# RCS: @(#) $Id: queuec.tcl,v 1.1 2008/06/19 23:03:35 andreas_kupries Exp $
 
19
 
 
20
package require Tcl 8.4
 
21
package require critcl 3.1
 
22
 
 
23
critcl::buildrequirement {
 
24
    package require critcl::class ; # DSL, easy spec of Tcl class/object commands.
 
25
}
 
26
 
 
27
# # ## ### ##### ######## ############# #####################
 
28
## Administrivia
 
29
 
 
30
critcl::license {Andreas Kupries} {BSD licensed}
 
31
 
 
32
critcl::summary {Queue objects for Tcl.}
 
33
 
 
34
critcl::description {
 
35
    This package implements queue objects
 
36
    for Tcl.
 
37
}
 
38
 
 
39
critcl::subject queue
 
40
critcl::subject {data structure}
 
41
critcl::subject structure
 
42
critcl::subject {abstract data structure}
 
43
critcl::subject {generic data structure}
 
44
 
 
45
# # ## ### ##### ######## ############# #####################
 
46
## Configuration and implementation.
 
47
 
 
48
critcl::cheaders util.h
 
49
 
 
50
critcl::class::define ::queuec {
 
51
    include util.h
 
52
 
 
53
    constructor {
 
54
        if (objc > 0) {
 
55
            Tcl_AppendResult (interp, "wrong\#args for constructor, expected none", NULL);
 
56
            goto error;
 
57
        }
 
58
    }
 
59
 
 
60
    method_introspection
 
61
 
 
62
    # # ## ### ##### ######## ############# #####################
 
63
    insvariable Tcl_Obj* unget {
 
64
        List object holding unget'ted elements.
 
65
    } {
 
66
        instance->unget  = Tcl_NewListObj (0,NULL);
 
67
        Tcl_IncrRefCount (instance->unget); 
 
68
    } {
 
69
        Tcl_DecrRefCount (instance->unget);
 
70
    }
 
71
 
 
72
    # # ## ### ##### ######## ############# #####################
 
73
    insvariable Tcl_Obj* queue {
 
74
        List object holding the main queue.
 
75
    } {
 
76
        instance->queue  = Tcl_NewListObj (0,NULL);
 
77
        Tcl_IncrRefCount (instance->queue); 
 
78
    } {
 
79
        Tcl_DecrRefCount (instance->queue);
 
80
    }
 
81
 
 
82
    # # ## ### ##### ######## ############# #####################
 
83
    insvariable Tcl_Obj* append {
 
84
        List object holding new elements
 
85
    } {
 
86
        instance->append = Tcl_NewListObj (0,NULL);
 
87
        Tcl_IncrRefCount (instance->append);
 
88
    } {
 
89
        Tcl_DecrRefCount (instance->append);
 
90
    }
 
91
 
 
92
    # # ## ### ##### ######## ############# #####################
 
93
    insvariable int at {
 
94
        Index of next element to return from the main queue.
 
95
        (variable: queue).
 
96
    } {
 
97
        instance->at = 0;
 
98
    } ; # no need for a destructor
 
99
 
 
100
    # # ## ### ##### ######## ############# #####################
 
101
    method clear proc {} void {
 
102
        /*
 
103
         * Delete and recreate the queue memory. A combination of delete/new,
 
104
         * except the main structure is left unchanged
 
105
         */
 
106
 
 
107
        Tcl_DecrRefCount (instance->unget);
 
108
        Tcl_DecrRefCount (instance->queue);
 
109
        Tcl_DecrRefCount (instance->append);
 
110
 
 
111
        instance->at     = 0;
 
112
        instance->unget  = Tcl_NewListObj (0,NULL);
 
113
        instance->queue  = Tcl_NewListObj (0,NULL);
 
114
        instance->append = Tcl_NewListObj (0,NULL);
 
115
 
 
116
        Tcl_IncrRefCount (instance->unget); 
 
117
        Tcl_IncrRefCount (instance->queue); 
 
118
        Tcl_IncrRefCount (instance->append);
 
119
    }
 
120
 
 
121
    # # ## ### ##### ######## ############# #####################
 
122
    method get  as QueueRetrieve 1
 
123
    method peek as QueueRetrieve 0
 
124
 
 
125
    # # ## ### ##### ######## ############# #####################
 
126
    method put command {
 
127
        item... = objv[2]...
 
128
    } {
 
129
        int i;
 
130
 
 
131
        if (objc < 3) {
 
132
            Tcl_WrongNumArgs (interp, 2, objv, "item ?item ...?");
 
133
            return TCL_ERROR;
 
134
        }
 
135
 
 
136
        for (i = 2; i < objc; i++) {
 
137
            Tcl_ListObjAppendElement (interp, instance->append, objv[i]);
 
138
        }
 
139
 
 
140
        return TCL_OK;
 
141
    }
 
142
 
 
143
    # # ## ### ##### ######## ############# #####################
 
144
    method size proc {} int {
 
145
        return QueueSize (instance, NULL, NULL, NULL);
 
146
    }
 
147
 
 
148
    # # ## ### ##### ######## ############# #####################
 
149
    method unget proc {Tcl_Obj* item} ok {
 
150
        if (instance->at == 0) {
 
151
            /* Need the unget stack */
 
152
            Tcl_ListObjAppendElement (interp, instance->unget, item);
 
153
        } else {
 
154
            /*
 
155
             * We have room in the return buffer, so splice directly instead of
 
156
             * using the unget stack.
 
157
             */
 
158
 
 
159
            int queuec = 0;
 
160
            Tcl_ListObjLength (NULL, instance->queue,  &queuec);
 
161
 
 
162
            instance->at --;
 
163
            ASSERT_BOUNDS(instance->at,queuec);
 
164
            Tcl_ListObjReplace (interp, instance->queue, instance->at, 1, 1, &item);
 
165
        }
 
166
 
 
167
        return TCL_OK;
 
168
    }
 
169
 
 
170
    # # ## ### ##### ######## ############# #####################
 
171
    support {
 
172
        static int
 
173
        QueueSize (@instancetype@ q, int* u, int* r, int* a)
 
174
        {
 
175
            int ungetc  = 0;
 
176
            int queuec  = 0;
 
177
            int appendc = 0;
 
178
 
 
179
            Tcl_ListObjLength (NULL, q->unget,  &ungetc);
 
180
            Tcl_ListObjLength (NULL, q->queue,  &queuec);
 
181
            Tcl_ListObjLength (NULL, q->append, &appendc);
 
182
 
 
183
            if (u) *u = ungetc;
 
184
            if (r) *r = queuec;
 
185
            if (a) *a = appendc;
 
186
 
 
187
            return ungetc + queuec + appendc - q->at;
 
188
        }
 
189
 
 
190
        static void
 
191
        QueueShift (@instancetype@ q)
 
192
        {
 
193
            int queuec = 0;
 
194
            int appendc = 0;
 
195
 
 
196
            /* The queue is not done yet, no shift */
 
197
            Tcl_ListObjLength (NULL, q->queue, &queuec);
 
198
            if (q->at < queuec) return;
 
199
 
 
200
            /* The queue is done, however there is nothing
 
201
             * to shift into it, so we don't
 
202
             */
 
203
            Tcl_ListObjLength (NULL, q->append, &appendc);
 
204
            if (!appendc) return;
 
205
 
 
206
            q->at = 0;
 
207
            Tcl_DecrRefCount (q->queue);
 
208
            q->queue  = q->append;
 
209
            q->append = Tcl_NewListObj (0,NULL);
 
210
            Tcl_IncrRefCount (q->append);
 
211
        }
 
212
 
 
213
        static int
 
214
        QueueRetrieve (@instancetype@  instance,
 
215
                       Tcl_Interp*     interp,
 
216
                       int             objc,
 
217
                       Tcl_Obj* CONST* objv,
 
218
                       int             get)
 
219
        {
 
220
            /* Syntax: queue peek|get ?n?
 
221
             *         [0]  [1]       [2]
 
222
             */
 
223
 
 
224
            int       listc = 0;
 
225
            Tcl_Obj** listv;
 
226
            Tcl_Obj*  r;
 
227
            int       n = 1;
 
228
            int       ungetc;
 
229
            int       queuec;
 
230
            int       appendc;
 
231
 
 
232
            if ((objc != 2) && (objc != 3)) {
 
233
                Tcl_WrongNumArgs (interp, 2, objv, "?n?");
 
234
                return TCL_ERROR;
 
235
            }
 
236
 
 
237
            if (objc == 3) {
 
238
                if (Tcl_GetIntFromObj(interp, objv[2], &n) != TCL_OK) {
 
239
                    return TCL_ERROR;
 
240
                } else if (n < 1) {
 
241
                    Tcl_AppendResult (interp, "invalid item count ",
 
242
                                      Tcl_GetString (objv[2]),
 
243
                                      NULL);
 
244
                    return TCL_ERROR;
 
245
                }
 
246
            }
 
247
 
 
248
            if (n > QueueSize(instance, &ungetc, &queuec, &appendc)) {
 
249
                Tcl_AppendResult (interp,
 
250
                                  "insufficient items in queue to fill request",
 
251
                                  NULL);
 
252
                return TCL_ERROR;
 
253
            }
 
254
 
 
255
            /* 1. We have item on the unget stack
 
256
             *    a. Enough to satisfy request.
 
257
             *    b. Not enough.
 
258
             * 2. We have items in the return buffer.
 
259
             *    a. Enough to satisfy request.
 
260
             *    b. Not enough.
 
261
             * 3. We have items in the append buffer.
 
262
             *    a. Enough to satisfy request.
 
263
             *    b. Not enough.
 
264
             *
 
265
             * Case 3. can assume 2b, because an empty return buffer will be filled
 
266
             * from the append buffer before looking at either. Case 3. cannot happen
 
267
             * for n==1, the return buffer will contain at least one element.
 
268
             *
 
269
             * We distinguish between single and multi-element requests.
 
270
             *
 
271
             * XXX AK optimizations - If we can return everything from a single
 
272
             * buffer, be it queue, or append, just return the buffer object, do not
 
273
             * create something new.
 
274
             */
 
275
 
 
276
            if (n == 1) {
 
277
                if (ungetc) {
 
278
                    /* Pull from unget stack */
 
279
                    Tcl_ListObjGetElements (interp, instance->unget, &listc, &listv);
 
280
                    r = listv [listc-1];
 
281
                    Tcl_SetObjResult (interp, r);
 
282
                    if (get) {
 
283
                        /* XXX AK : Should maintain max size info, and proper index, for discard. */
 
284
                        Tcl_ListObjReplace (interp, instance->unget, listc-1, 1, 0, NULL);
 
285
                    }
 
286
                } else {
 
287
                    QueueShift (instance);
 
288
                    Tcl_ListObjGetElements (interp, instance->queue, &listc, &listv);
 
289
                    ASSERT_BOUNDS(instance->at,listc);
 
290
                    r = listv [instance->at];
 
291
                    Tcl_SetObjResult (interp, r);
 
292
                    /*
 
293
                     * Note: Doing the SetObj now is important. It increments the
 
294
                     * refcount of 'r', allowing it to survive if the 'QueueShift' below
 
295
                     * kills the internal list (instance->queue) holding it.
 
296
                     */
 
297
                    if (get) {
 
298
                        instance->at ++;
 
299
                        QueueShift (instance);
 
300
                    }
 
301
                }
 
302
            } else {
 
303
                /*
 
304
                 * Allocate buffer for result, then fill it using the various data
 
305
                 * sources.
 
306
                 */
 
307
 
 
308
                int i = 0, j;
 
309
                Tcl_Obj** resv = NALLOC(n,Tcl_Obj*);
 
310
 
 
311
                if (ungetc) {
 
312
                    Tcl_ListObjGetElements (interp, instance->unget, &listc, &listv);
 
313
                    /*
 
314
                     * Note how we are iterating backward in listv. unget is managed
 
315
                     * as a stack, avoiding mem-copy operations and both push and pop.
 
316
                     */
 
317
                    for (j = listc-1;
 
318
                         j >= 0 && i < n;
 
319
                         j--, i++) {
 
320
                                    ASSERT_BOUNDS(i,n);
 
321
                                    ASSERT_BOUNDS(j,listc);
 
322
                                    resv[i] = listv[j];
 
323
                                    Tcl_IncrRefCount (resv[i]);
 
324
                                }
 
325
                    if (get) {
 
326
                        /* XXX AK : Should maintain max size info, and proper index, for discard. */
 
327
                        Tcl_ListObjReplace (interp, instance->unget, j, i, 0, NULL);
 
328
                        /* XXX CHECK index calcs. */
 
329
                    }
 
330
                }
 
331
                if (i < n) {
 
332
                    QueueShift (instance);
 
333
                    Tcl_ListObjGetElements (interp, instance->queue, &listc, &listv);
 
334
                    for (j = instance->at;
 
335
                         j < listc && i < n; 
 
336
                         j++, i++) {
 
337
                                    ASSERT_BOUNDS(i,n);
 
338
                                    ASSERT_BOUNDS(j,listc);
 
339
                                    resv[i] = listv[j];
 
340
                                    Tcl_IncrRefCount (resv[i]);
 
341
                                }
 
342
 
 
343
                    if (get) {
 
344
                        instance->at = j;
 
345
                        QueueShift (instance);
 
346
                    } else if (i < n) {
 
347
                        /* XX */
 
348
                        Tcl_ListObjGetElements (interp, instance->append, &listc, &listv);
 
349
                        for (j = 0;
 
350
                             j < listc && i < n; 
 
351
                             j++, i++) {
 
352
                                        ASSERT_BOUNDS(i,n);
 
353
                                        ASSERT_BOUNDS(j,listc);
 
354
                                        resv[i] = listv[j];
 
355
                                        Tcl_IncrRefCount (resv[i]);
 
356
                                    }
 
357
                    }
 
358
                }
 
359
 
 
360
                /*
 
361
                 * This can happen if and only if we have to pull data from append,
 
362
                 * and get is set. Without get XX would have run and filled the result
 
363
                 * to completion.
 
364
                 */
 
365
 
 
366
                if (i < n) {
 
367
                    ASSERT(get,"Impossible 2nd return pull witohut get");
 
368
                    QueueShift (instance);
 
369
                    Tcl_ListObjGetElements (interp, instance->queue, &listc, &listv);
 
370
                    for (j = instance->at;
 
371
                         j < listc && i < n; 
 
372
                         j++, i++) {
 
373
                            ASSERT_BOUNDS(i,n);
 
374
                            ASSERT_BOUNDS(j,listc);
 
375
                            resv[i] = listv[j];
 
376
                            Tcl_IncrRefCount (resv[i]);
 
377
                    }
 
378
                    instance->at = j;
 
379
                    QueueShift (instance);
 
380
                }
 
381
 
 
382
                r = Tcl_NewListObj (n, resv);
 
383
                Tcl_SetObjResult (interp, r);
 
384
 
 
385
                for (i=0;i<n;i++) {
 
386
                   Tcl_DecrRefCount (resv[i]);
 
387
                }
 
388
                ckfree((char*)resv);
 
389
            }
 
390
 
 
391
            return TCL_OK;
 
392
        }
 
393
    }
 
394
}
 
395
 
 
396
# ### ### ### ######### ######### #########
 
397
## Ready
 
398
package provide queuec 1