5 * Tcl handles. Provides a mechanism for managing expandable tables that are
6 * addressed by textual handles.
7 *-----------------------------------------------------------------------------
8 * Copyright 1992 Karl Lehenbauer and Mark Diekhans.
10 * Permission to use, copy, modify, and distribute this software and its
11 * documentation for any purpose and without fee is hereby granted, provided
12 * that the above copyright notice appear in all copies. Karl Lehenbauer and
13 * Mark Diekhans make no representations about the suitability of this
14 * software for any purpose. It is provided "as is" without express or
16 *-----------------------------------------------------------------------------
17 * $Id: tclXhandles.c,v 2.0 1992/10/16 04:50:49 markd Rel $
18 *-----------------------------------------------------------------------------
24 * This is the table header. It is separately allocated from the table body,
25 * since it must keep track of a table body that might move. Each entry in the
26 * table is preceded with a header which has the free list link, which is a
27 * entry index of the next free entry. Special values keep track of allocated
32 #define ALLOCATED_IDX -2
34 typedef unsigned char ubyte_t
;
35 typedef ubyte_t
*ubyte_pt
;
38 int useCount
; /* Keeps track of the number sharing */
39 int entrySize
; /* Entry size in bytes, including overhead */
40 int tableSize
; /* Current number of entries in the table */
41 int freeHeadIdx
; /* Index of first free entry in the table */
42 ubyte_pt bodyP
; /* Pointer to table body */
43 int baseLength
; /* Length of handleBase. */
44 char handleBase
[1]; /* Base handle name. MUST BE LAST FIELD! */
46 typedef tblHeader_t
*tblHeader_pt
;
51 typedef entryHeader_t
*entryHeader_pt
;
54 * This macro is used to return a pointer to an entry, given its index.
56 #define TBL_INDEX(hdrP, idx) \
57 ((entryHeader_pt) (hdrP->bodyP + (hdrP->entrySize * idx)))
60 * This macros to convert between pointers to the user and header area of
63 #define USER_AREA(entryPtr) \
64 (void_pt) (((ubyte_pt) entryPtr) + sizeof (entryHeader_t));
65 #define HEADER_AREA(entryPtr) \
66 (entryHeader_pt) (((ubyte_pt) entryPtr) - sizeof (entryHeader_t));
69 * Prototypes of internal functions.
72 LinkInNewEntries
_ANSI_ARGS_((tblHeader_pt tblHdrPtr
,
77 ExpandTable
_ANSI_ARGS_((tblHeader_pt tblHdrPtr
,
81 AllocEntry
_ANSI_ARGS_((tblHeader_pt tblHdrPtr
,
85 HandleDecode
_ANSI_ARGS_((Tcl_Interp
*interp
,
86 tblHeader_pt tblHdrPtr
,
89 /*=============================================================================
91 * Build free links through the newly allocated part of a table.
94 * o tblHdrPtr (I) - A pointer to the table header.
95 * o newIdx (I) - Index of the first new entry.
96 * o numEntries (I) - The number of new entries.
97 *-----------------------------------------------------------------------------
100 LinkInNewEntries (tblHdrPtr
, newIdx
, numEntries
)
101 tblHeader_pt tblHdrPtr
;
106 entryHeader_pt entryPtr
;
108 lastIdx
= newIdx
+ numEntries
- 1;
110 for (entIdx
= newIdx
; entIdx
< lastIdx
; entIdx
++) {
111 entryPtr
= TBL_INDEX (tblHdrPtr
, entIdx
);
112 entryPtr
->freeLink
= entIdx
+ 1;
114 entryPtr
= TBL_INDEX (tblHdrPtr
, lastIdx
);
115 entryPtr
->freeLink
= tblHdrPtr
->freeHeadIdx
;
116 tblHdrPtr
->freeHeadIdx
= newIdx
;
118 } /* LinkInNewEntries */
120 /*=============================================================================
122 * Expand a handle table, doubling its size.
124 * o tblHdrPtr (I) - A pointer to the table header.
125 * o neededIdx (I) - If positive, then the table will be expanded so that
126 * this entry is available. If -1, then just expand by the number of
127 * entries specified on table creation. MUST be smaller than this size.
128 *-----------------------------------------------------------------------------
131 ExpandTable (tblHdrPtr
, neededIdx
)
132 tblHeader_pt tblHdrPtr
;
135 ubyte_pt oldBodyP
= tblHdrPtr
->bodyP
;
140 numNewEntries
= tblHdrPtr
->tableSize
;
142 numNewEntries
= (neededIdx
- tblHdrPtr
->tableSize
) + 1;
143 newSize
= (tblHdrPtr
->tableSize
+ numNewEntries
) * tblHdrPtr
->entrySize
;
145 tblHdrPtr
->bodyP
= (ubyte_pt
) ckalloc (newSize
);
146 memcpy (tblHdrPtr
->bodyP
, oldBodyP
, newSize
);
147 LinkInNewEntries (tblHdrPtr
, tblHdrPtr
->tableSize
, numNewEntries
);
148 tblHdrPtr
->tableSize
+= numNewEntries
;
153 /*=============================================================================
155 * Allocate a table entry, expanding if necessary.
158 * o tblHdrPtr (I) - A pointer to the table header.
159 * o entryIdxPtr (O) - The index of the table entry is returned here.
161 * The a pointer to the entry.
162 *-----------------------------------------------------------------------------
164 static entryHeader_pt
165 AllocEntry (tblHdrPtr
, entryIdxPtr
)
166 tblHeader_pt tblHdrPtr
;
170 entryHeader_pt entryPtr
;
172 if (tblHdrPtr
->freeHeadIdx
== NULL_IDX
)
173 ExpandTable (tblHdrPtr
, -1);
175 entryIdx
= tblHdrPtr
->freeHeadIdx
;
176 entryPtr
= TBL_INDEX (tblHdrPtr
, entryIdx
);
177 tblHdrPtr
->freeHeadIdx
= entryPtr
->freeLink
;
178 entryPtr
->freeLink
= ALLOCATED_IDX
;
180 *entryIdxPtr
= entryIdx
;
185 /*=============================================================================
187 * Decode handle into an entry number.
190 * o interp (I) - A error message may be returned in result.
191 * o tblHdrPtr (I) - A pointer to the table header.
192 * o handle (I) - Handle to decode.
194 * The entry index decoded from the handle, or a negative number if an error
196 *-----------------------------------------------------------------------------
199 HandleDecode (interp
, tblHdrPtr
, handle
)
201 tblHeader_pt tblHdrPtr
;
206 if ((strncmp (tblHdrPtr
->handleBase
, (char *) handle
,
207 tblHdrPtr
->baseLength
) != 0) ||
208 !Tcl_StrToUnsigned (&handle
[tblHdrPtr
->baseLength
], 10,
210 Tcl_AppendResult (interp
, "invalid ", tblHdrPtr
->handleBase
,
211 " handle: ", handle
, (char *) NULL
);
218 /*=============================================================================
219 * Tcl_HandleTblInit --
220 * Create and initialize a Tcl dynamic handle table. The use count on the
221 * table is set to one.
223 * o handleBase(I) - The base name of the handle, the handle will be returned
224 * in the form "baseNN", where NN is the table entry number.
225 * o entrySize (I) - The size of an entry, in bytes.
226 * o initEntries (I) - Initial size of the table, in entries.
228 * A pointer to the table header.
229 *-----------------------------------------------------------------------------
232 Tcl_HandleTblInit (handleBase
, entrySize
, initEntries
)
233 CONST
char *handleBase
;
237 tblHeader_pt tblHdrPtr
;
238 int baseLength
= strlen ((char *) handleBase
);
240 tblHdrPtr
= (tblHeader_pt
) ckalloc (sizeof (tblHeader_t
) + baseLength
+ 1);
242 tblHdrPtr
->useCount
= 1;
243 tblHdrPtr
->baseLength
= baseLength
;
244 strcpy (tblHdrPtr
->handleBase
, (char *) handleBase
);
247 * Calculate entry size, including header, rounded up to sizeof (int).
249 tblHdrPtr
->entrySize
= entrySize
+ sizeof (entryHeader_t
);
250 tblHdrPtr
->entrySize
= ((tblHdrPtr
->entrySize
+ sizeof (int) - 1) /
251 sizeof (int)) * sizeof (int);
252 tblHdrPtr
->freeHeadIdx
= NULL_IDX
;
253 tblHdrPtr
->tableSize
= initEntries
;
254 tblHdrPtr
->bodyP
= (ubyte_pt
) ckalloc (initEntries
* tblHdrPtr
->entrySize
);
255 LinkInNewEntries (tblHdrPtr
, 0, initEntries
);
257 return (void_pt
) tblHdrPtr
;
259 } /* Tcl_HandleTblInit */
261 /*=============================================================================
262 * Tcl_HandleTblUseCount --
263 * Alter the handle table use count by the specified amount, which can be
264 * positive or negative. Amount may be zero to retrieve the use count.
266 * o headerPtr (I) - Pointer to the table header.
267 * o amount (I) - The amount to alter the use count by.
269 * The resulting use count.
270 *-----------------------------------------------------------------------------
273 Tcl_HandleTblUseCount (headerPtr
, amount
)
277 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
279 tblHdrPtr
->useCount
+= amount
;
280 return tblHdrPtr
->useCount
;
283 /*=============================================================================
284 * Tcl_HandleTblRelease --
285 * Decrement the use count on a Tcl dynamic handle table. If the count
286 * goes to zero or negative, then release the table. It is designed to be
287 * called when a command is released.
289 * o headerPtr (I) - Pointer to the table header.
290 *-----------------------------------------------------------------------------
293 Tcl_HandleTblRelease (headerPtr
)
296 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
298 tblHdrPtr
->useCount
--;
299 if (tblHdrPtr
->useCount
<= 0) {
300 ckfree (tblHdrPtr
->bodyP
);
301 ckfree ((char *) tblHdrPtr
);
305 /*=============================================================================
307 * Allocate an entry and associate a handle with it.
310 * o headerPtr (I) - A pointer to the table header.
311 * o handlePtr (O) - Buffer to return handle in. It must be big enough to
314 * A pointer to the allocated entry (user part).
315 *-----------------------------------------------------------------------------
318 Tcl_HandleAlloc (headerPtr
, handlePtr
)
322 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
323 entryHeader_pt entryPtr
;
326 entryPtr
= AllocEntry ((tblHeader_pt
) headerPtr
, &entryIdx
);
327 sprintf (handlePtr
, "%s%d", tblHdrPtr
->handleBase
, entryIdx
);
329 return USER_AREA (entryPtr
);
331 } /* Tcl_HandleAlloc */
333 /*=============================================================================
335 * Translate a handle to a entry pointer.
338 * o interp (I) - A error message may be returned in result.
339 * o headerPtr (I) - A pointer to the table header.
340 * o handle (I) - The handle assigned to the entry.
342 * A pointer to the entry, or NULL if an error occured.
343 *-----------------------------------------------------------------------------
346 Tcl_HandleXlate (interp
, headerPtr
, handle
)
351 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
352 entryHeader_pt entryPtr
;
355 if ((entryIdx
= HandleDecode (interp
, tblHdrPtr
, handle
)) < 0)
357 entryPtr
= TBL_INDEX (tblHdrPtr
, entryIdx
);
359 if ((entryIdx
>= tblHdrPtr
->tableSize
) ||
360 (entryPtr
->freeLink
!= ALLOCATED_IDX
)) {
361 Tcl_AppendResult (interp
, tblHdrPtr
->handleBase
, " is not open",
366 return USER_AREA (entryPtr
);
368 } /* Tcl_HandleXlate */
370 /*=============================================================================
372 * Walk through and find every allocated entry in a table. Entries may
373 * be deallocated during a walk, but should not be allocated.
376 * o headerPtr (I) - A pointer to the table header.
377 * o walkKeyPtr (I/O) - Pointer to a variable to use to keep track of the
378 * place in the table. The variable should be initialized to -1 before
381 * A pointer to the next allocated entry, or NULL if there are not more.
382 *-----------------------------------------------------------------------------
385 Tcl_HandleWalk (headerPtr
, walkKeyPtr
)
389 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
391 entryHeader_pt entryPtr
;
393 if (*walkKeyPtr
== -1)
396 entryIdx
= *walkKeyPtr
+ 1;
398 while (entryIdx
< tblHdrPtr
->tableSize
) {
399 entryPtr
= TBL_INDEX (tblHdrPtr
, entryIdx
);
400 if (entryPtr
->freeLink
== ALLOCATED_IDX
) {
401 *walkKeyPtr
= entryIdx
;
402 return USER_AREA (entryPtr
);
408 } /* Tcl_HandleWalk */
410 /*=============================================================================
411 * Tcl_WalkKeyToHandle --
412 * Convert a walk key, as returned from a call to Tcl_HandleWalk into a
413 * handle. The Tcl_HandleWalk must have succeeded.
415 * o headerPtr (I) - A pointer to the table header.
416 * o walkKey (I) - The walk key.
417 * o handlePtr (O) - Buffer to return handle in. It must be big enough to
419 *-----------------------------------------------------------------------------
422 Tcl_WalkKeyToHandle (headerPtr
, walkKey
, handlePtr
)
427 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
429 sprintf (handlePtr
, "%s%d", tblHdrPtr
->handleBase
, walkKey
);
431 } /* Tcl_WalkKeyToHandle */
433 /*=============================================================================
435 * Frees a handle table entry.
438 * o headerPtr (I) - A pointer to the table header.
439 * o entryPtr (I) - Entry to free.
440 *-----------------------------------------------------------------------------
443 Tcl_HandleFree (headerPtr
, entryPtr
)
447 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
448 entryHeader_pt freeentryPtr
;
450 freeentryPtr
= HEADER_AREA (entryPtr
);
451 freeentryPtr
->freeLink
= tblHdrPtr
->freeHeadIdx
;
452 tblHdrPtr
->freeHeadIdx
= (((ubyte_pt
) entryPtr
) - tblHdrPtr
->bodyP
) /
453 tblHdrPtr
->entrySize
;
455 } /* Tcl_HandleFree */