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 (tblHeader_pt tblHdrPtr
, int newIdx
, int numEntries
)
103 entryHeader_pt entryPtr
;
105 lastIdx
= newIdx
+ numEntries
- 1;
107 for (entIdx
= newIdx
; entIdx
< lastIdx
; entIdx
++) {
108 entryPtr
= TBL_INDEX (tblHdrPtr
, entIdx
);
109 entryPtr
->freeLink
= entIdx
+ 1;
111 entryPtr
= TBL_INDEX (tblHdrPtr
, lastIdx
);
112 entryPtr
->freeLink
= tblHdrPtr
->freeHeadIdx
;
113 tblHdrPtr
->freeHeadIdx
= newIdx
;
115 } /* LinkInNewEntries */
117 /*=============================================================================
119 * Expand a handle table, doubling its size.
121 * o tblHdrPtr (I) - A pointer to the table header.
122 * o neededIdx (I) - If positive, then the table will be expanded so that
123 * this entry is available. If -1, then just expand by the number of
124 * entries specified on table creation. MUST be smaller than this size.
125 *-----------------------------------------------------------------------------
128 ExpandTable (tblHeader_pt tblHdrPtr
, int neededIdx
)
130 ubyte_pt oldBodyP
= tblHdrPtr
->bodyP
;
135 numNewEntries
= tblHdrPtr
->tableSize
;
137 numNewEntries
= (neededIdx
- tblHdrPtr
->tableSize
) + 1;
138 newSize
= (tblHdrPtr
->tableSize
+ numNewEntries
) * tblHdrPtr
->entrySize
;
140 tblHdrPtr
->bodyP
= (ubyte_pt
) ckalloc (newSize
);
141 memcpy (tblHdrPtr
->bodyP
, oldBodyP
, newSize
);
142 LinkInNewEntries (tblHdrPtr
, tblHdrPtr
->tableSize
, numNewEntries
);
143 tblHdrPtr
->tableSize
+= numNewEntries
;
148 /*=============================================================================
150 * Allocate a table entry, expanding if necessary.
153 * o tblHdrPtr (I) - A pointer to the table header.
154 * o entryIdxPtr (O) - The index of the table entry is returned here.
156 * The a pointer to the entry.
157 *-----------------------------------------------------------------------------
159 static entryHeader_pt
160 AllocEntry (tblHeader_pt tblHdrPtr
, int *entryIdxPtr
)
163 entryHeader_pt entryPtr
;
165 if (tblHdrPtr
->freeHeadIdx
== NULL_IDX
)
166 ExpandTable (tblHdrPtr
, -1);
168 entryIdx
= tblHdrPtr
->freeHeadIdx
;
169 entryPtr
= TBL_INDEX (tblHdrPtr
, entryIdx
);
170 tblHdrPtr
->freeHeadIdx
= entryPtr
->freeLink
;
171 entryPtr
->freeLink
= ALLOCATED_IDX
;
173 *entryIdxPtr
= entryIdx
;
178 /*=============================================================================
180 * Decode handle into an entry number.
183 * o interp (I) - A error message may be returned in result.
184 * o tblHdrPtr (I) - A pointer to the table header.
185 * o handle (I) - Handle to decode.
187 * The entry index decoded from the handle, or a negative number if an error
189 *-----------------------------------------------------------------------------
192 HandleDecode (Tcl_Interp
*interp
, tblHeader_pt tblHdrPtr
, CONST
char *handle
)
196 if ((strncmp (tblHdrPtr
->handleBase
, (char *) handle
,
197 tblHdrPtr
->baseLength
) != 0) ||
198 !Tcl_StrToUnsigned (&handle
[tblHdrPtr
->baseLength
], 10,
200 Tcl_AppendResult (interp
, "invalid ", tblHdrPtr
->handleBase
,
201 " handle: ", handle
, (char *) NULL
);
208 /*=============================================================================
209 * Tcl_HandleTblInit --
210 * Create and initialize a Tcl dynamic handle table. The use count on the
211 * table is set to one.
213 * o handleBase(I) - The base name of the handle, the handle will be returned
214 * in the form "baseNN", where NN is the table entry number.
215 * o entrySize (I) - The size of an entry, in bytes.
216 * o initEntries (I) - Initial size of the table, in entries.
218 * A pointer to the table header.
219 *-----------------------------------------------------------------------------
222 Tcl_HandleTblInit (CONST
char *handleBase
, int entrySize
, int initEntries
)
224 tblHeader_pt tblHdrPtr
;
225 int baseLength
= strlen ((char *) handleBase
);
227 tblHdrPtr
= (tblHeader_pt
) ckalloc (sizeof (tblHeader_t
) + baseLength
+ 1);
229 tblHdrPtr
->useCount
= 1;
230 tblHdrPtr
->baseLength
= baseLength
;
231 strcpy (tblHdrPtr
->handleBase
, (char *) handleBase
);
234 * Calculate entry size, including header, rounded up to sizeof (int).
236 tblHdrPtr
->entrySize
= entrySize
+ sizeof (entryHeader_t
);
237 tblHdrPtr
->entrySize
= ((tblHdrPtr
->entrySize
+ sizeof (int) - 1) /
238 sizeof (int)) * sizeof (int);
239 tblHdrPtr
->freeHeadIdx
= NULL_IDX
;
240 tblHdrPtr
->tableSize
= initEntries
;
241 tblHdrPtr
->bodyP
= (ubyte_pt
) ckalloc (initEntries
* tblHdrPtr
->entrySize
);
242 LinkInNewEntries (tblHdrPtr
, 0, initEntries
);
244 return (void_pt
) tblHdrPtr
;
246 } /* Tcl_HandleTblInit */
248 /*=============================================================================
249 * Tcl_HandleTblUseCount --
250 * Alter the handle table use count by the specified amount, which can be
251 * positive or negative. Amount may be zero to retrieve the use count.
253 * o headerPtr (I) - Pointer to the table header.
254 * o amount (I) - The amount to alter the use count by.
256 * The resulting use count.
257 *-----------------------------------------------------------------------------
260 Tcl_HandleTblUseCount (void_pt headerPtr
, int amount
)
262 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
264 tblHdrPtr
->useCount
+= amount
;
265 return tblHdrPtr
->useCount
;
268 /*=============================================================================
269 * Tcl_HandleTblRelease --
270 * Decrement the use count on a Tcl dynamic handle table. If the count
271 * goes to zero or negative, then release the table. It is designed to be
272 * called when a command is released.
274 * o headerPtr (I) - Pointer to the table header.
275 *-----------------------------------------------------------------------------
278 Tcl_HandleTblRelease (void_pt headerPtr
)
280 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
282 tblHdrPtr
->useCount
--;
283 if (tblHdrPtr
->useCount
<= 0) {
284 ckfree (tblHdrPtr
->bodyP
);
285 ckfree ((char *) tblHdrPtr
);
289 /*=============================================================================
291 * Allocate an entry and associate a handle with it.
294 * o headerPtr (I) - A pointer to the table header.
295 * o handlePtr (O) - Buffer to return handle in. It must be big enough to
298 * A pointer to the allocated entry (user part).
299 *-----------------------------------------------------------------------------
302 Tcl_HandleAlloc (void_pt headerPtr
, char *handlePtr
)
304 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
305 entryHeader_pt entryPtr
;
308 entryPtr
= AllocEntry ((tblHeader_pt
) headerPtr
, &entryIdx
);
309 sprintf (handlePtr
, "%s%d", tblHdrPtr
->handleBase
, entryIdx
);
311 return USER_AREA (entryPtr
);
313 } /* Tcl_HandleAlloc */
315 /*=============================================================================
317 * Translate a handle to a entry pointer.
320 * o interp (I) - A error message may be returned in result.
321 * o headerPtr (I) - A pointer to the table header.
322 * o handle (I) - The handle assigned to the entry.
324 * A pointer to the entry, or NULL if an error occured.
325 *-----------------------------------------------------------------------------
328 Tcl_HandleXlate (Tcl_Interp
*interp
, void_pt headerPtr
, CONST
char *handle
)
330 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
331 entryHeader_pt entryPtr
;
334 if ((entryIdx
= HandleDecode (interp
, tblHdrPtr
, handle
)) < 0)
336 entryPtr
= TBL_INDEX (tblHdrPtr
, entryIdx
);
338 if ((entryIdx
>= tblHdrPtr
->tableSize
) ||
339 (entryPtr
->freeLink
!= ALLOCATED_IDX
)) {
340 Tcl_AppendResult (interp
, tblHdrPtr
->handleBase
, " is not open",
345 return USER_AREA (entryPtr
);
347 } /* Tcl_HandleXlate */
349 /*=============================================================================
351 * Walk through and find every allocated entry in a table. Entries may
352 * be deallocated during a walk, but should not be allocated.
355 * o headerPtr (I) - A pointer to the table header.
356 * o walkKeyPtr (I/O) - Pointer to a variable to use to keep track of the
357 * place in the table. The variable should be initialized to -1 before
360 * A pointer to the next allocated entry, or NULL if there are not more.
361 *-----------------------------------------------------------------------------
364 Tcl_HandleWalk (void_pt headerPtr
, int *walkKeyPtr
)
366 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
368 entryHeader_pt entryPtr
;
370 if (*walkKeyPtr
== -1)
373 entryIdx
= *walkKeyPtr
+ 1;
375 while (entryIdx
< tblHdrPtr
->tableSize
) {
376 entryPtr
= TBL_INDEX (tblHdrPtr
, entryIdx
);
377 if (entryPtr
->freeLink
== ALLOCATED_IDX
) {
378 *walkKeyPtr
= entryIdx
;
379 return USER_AREA (entryPtr
);
385 } /* Tcl_HandleWalk */
387 /*=============================================================================
388 * Tcl_WalkKeyToHandle --
389 * Convert a walk key, as returned from a call to Tcl_HandleWalk into a
390 * handle. The Tcl_HandleWalk must have succeeded.
392 * o headerPtr (I) - A pointer to the table header.
393 * o walkKey (I) - The walk key.
394 * o handlePtr (O) - Buffer to return handle in. It must be big enough to
396 *-----------------------------------------------------------------------------
399 Tcl_WalkKeyToHandle (void_pt headerPtr
, int walkKey
, char *handlePtr
)
401 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
403 sprintf (handlePtr
, "%s%d", tblHdrPtr
->handleBase
, walkKey
);
405 } /* Tcl_WalkKeyToHandle */
407 /*=============================================================================
409 * Frees a handle table entry.
412 * o headerPtr (I) - A pointer to the table header.
413 * o entryPtr (I) - Entry to free.
414 *-----------------------------------------------------------------------------
417 Tcl_HandleFree (void_pt headerPtr
, void_pt entryPtr
)
419 tblHeader_pt tblHdrPtr
= (tblHeader_pt
)headerPtr
;
420 entryHeader_pt freeentryPtr
;
422 freeentryPtr
= HEADER_AREA (entryPtr
);
423 freeentryPtr
->freeLink
= tblHdrPtr
->freeHeadIdx
;
424 tblHdrPtr
->freeHeadIdx
= (((ubyte_pt
) entryPtr
) - tblHdrPtr
->bodyP
) /
425 tblHdrPtr
->entrySize
;
427 } /* Tcl_HandleFree */