]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * | |
3 | * tclXhandles.c -- | |
4 | * | |
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. | |
9 | * | |
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 | |
15 | * implied warranty. | |
16 | *----------------------------------------------------------------------------- | |
17 | * $Id: tclXhandles.c,v 2.0 1992/10/16 04:50:49 markd Rel $ | |
18 | *----------------------------------------------------------------------------- | |
19 | */ | |
20 | ||
21 | #include "tclxint.h" | |
22 | ||
23 | /* | |
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 | |
28 | * entries. | |
29 | */ | |
30 | ||
31 | #define NULL_IDX -1 | |
32 | #define ALLOCATED_IDX -2 | |
33 | ||
34 | typedef unsigned char ubyte_t; | |
35 | typedef ubyte_t *ubyte_pt; | |
36 | ||
37 | typedef struct { | |
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! */ | |
45 | } tblHeader_t; | |
46 | typedef tblHeader_t *tblHeader_pt; | |
47 | ||
48 | typedef struct { | |
49 | int freeLink; | |
50 | } entryHeader_t; | |
51 | typedef entryHeader_t *entryHeader_pt; | |
52 | ||
53 | /* | |
54 | * This macro is used to return a pointer to an entry, given its index. | |
55 | */ | |
56 | #define TBL_INDEX(hdrP, idx) \ | |
57 | ((entryHeader_pt) (hdrP->bodyP + (hdrP->entrySize * idx))) | |
58 | ||
59 | /* | |
60 | * This macros to convert between pointers to the user and header area of | |
61 | * an table entry. | |
62 | */ | |
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)); | |
67 | ||
68 | /* | |
69 | * Prototypes of internal functions. | |
70 | */ | |
71 | static void | |
72 | LinkInNewEntries _ANSI_ARGS_((tblHeader_pt tblHdrPtr, | |
73 | int newIdx, | |
74 | int numEntries)); | |
75 | ||
76 | static void | |
77 | ExpandTable _ANSI_ARGS_((tblHeader_pt tblHdrPtr, | |
78 | int neededIdx)); | |
79 | ||
80 | static entryHeader_pt | |
81 | AllocEntry _ANSI_ARGS_((tblHeader_pt tblHdrPtr, | |
82 | int *entryIdxPtr)); | |
83 | ||
84 | static int | |
85 | HandleDecode _ANSI_ARGS_((Tcl_Interp *interp, | |
86 | tblHeader_pt tblHdrPtr, | |
87 | CONST char *handle)); | |
88 | \f | |
89 | /*============================================================================= | |
90 | * LinkInNewEntries -- | |
91 | * Build free links through the newly allocated part of a table. | |
92 | * | |
93 | * Parameters: | |
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 | *----------------------------------------------------------------------------- | |
98 | */ | |
99 | static void | |
100 | LinkInNewEntries (tblHdrPtr, newIdx, numEntries) | |
101 | tblHeader_pt tblHdrPtr; | |
102 | int newIdx; | |
103 | int numEntries; | |
104 | { | |
105 | int entIdx, lastIdx; | |
106 | entryHeader_pt entryPtr; | |
107 | ||
108 | lastIdx = newIdx + numEntries - 1; | |
109 | ||
110 | for (entIdx = newIdx; entIdx < lastIdx; entIdx++) { | |
111 | entryPtr = TBL_INDEX (tblHdrPtr, entIdx); | |
112 | entryPtr->freeLink = entIdx + 1; | |
113 | } | |
114 | entryPtr = TBL_INDEX (tblHdrPtr, lastIdx); | |
115 | entryPtr->freeLink = tblHdrPtr->freeHeadIdx; | |
116 | tblHdrPtr->freeHeadIdx = newIdx; | |
117 | ||
118 | } /* LinkInNewEntries */ | |
119 | \f | |
120 | /*============================================================================= | |
121 | * ExpandTable -- | |
122 | * Expand a handle table, doubling its size. | |
123 | * Parameters: | |
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 | *----------------------------------------------------------------------------- | |
129 | */ | |
130 | static void | |
131 | ExpandTable (tblHdrPtr, neededIdx) | |
132 | tblHeader_pt tblHdrPtr; | |
133 | int neededIdx; | |
134 | { | |
135 | ubyte_pt oldBodyP = tblHdrPtr->bodyP; | |
136 | int numNewEntries; | |
137 | int newSize; | |
138 | ||
139 | if (neededIdx < 0) | |
140 | numNewEntries = tblHdrPtr->tableSize; | |
141 | else | |
142 | numNewEntries = (neededIdx - tblHdrPtr->tableSize) + 1; | |
143 | newSize = (tblHdrPtr->tableSize + numNewEntries) * tblHdrPtr->entrySize; | |
144 | ||
145 | tblHdrPtr->bodyP = (ubyte_pt) ckalloc (newSize); | |
146 | memcpy (tblHdrPtr->bodyP, oldBodyP, newSize); | |
147 | LinkInNewEntries (tblHdrPtr, tblHdrPtr->tableSize, numNewEntries); | |
148 | tblHdrPtr->tableSize += numNewEntries; | |
149 | ckfree (oldBodyP); | |
150 | ||
151 | } /* ExpandTable */ | |
152 | \f | |
153 | /*============================================================================= | |
154 | * AllocEntry -- | |
155 | * Allocate a table entry, expanding if necessary. | |
156 | * | |
157 | * Parameters: | |
158 | * o tblHdrPtr (I) - A pointer to the table header. | |
159 | * o entryIdxPtr (O) - The index of the table entry is returned here. | |
160 | * Returns: | |
161 | * The a pointer to the entry. | |
162 | *----------------------------------------------------------------------------- | |
163 | */ | |
164 | static entryHeader_pt | |
165 | AllocEntry (tblHdrPtr, entryIdxPtr) | |
166 | tblHeader_pt tblHdrPtr; | |
167 | int *entryIdxPtr; | |
168 | { | |
169 | int entryIdx; | |
170 | entryHeader_pt entryPtr; | |
171 | ||
172 | if (tblHdrPtr->freeHeadIdx == NULL_IDX) | |
173 | ExpandTable (tblHdrPtr, -1); | |
174 | ||
175 | entryIdx = tblHdrPtr->freeHeadIdx; | |
176 | entryPtr = TBL_INDEX (tblHdrPtr, entryIdx); | |
177 | tblHdrPtr->freeHeadIdx = entryPtr->freeLink; | |
178 | entryPtr->freeLink = ALLOCATED_IDX; | |
179 | ||
180 | *entryIdxPtr = entryIdx; | |
181 | return entryPtr; | |
182 | ||
183 | } /* AllocEntry */ | |
184 | \f | |
185 | /*============================================================================= | |
186 | * HandleDecode -- | |
187 | * Decode handle into an entry number. | |
188 | * | |
189 | * Parameters: | |
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. | |
193 | * Returns: | |
194 | * The entry index decoded from the handle, or a negative number if an error | |
195 | * occured. | |
196 | *----------------------------------------------------------------------------- | |
197 | */ | |
198 | static int | |
199 | HandleDecode (interp, tblHdrPtr, handle) | |
200 | Tcl_Interp *interp; | |
201 | tblHeader_pt tblHdrPtr; | |
202 | CONST char *handle; | |
203 | { | |
204 | unsigned entryIdx; | |
205 | ||
206 | if ((strncmp (tblHdrPtr->handleBase, (char *) handle, | |
207 | tblHdrPtr->baseLength) != 0) || | |
208 | !Tcl_StrToUnsigned (&handle [tblHdrPtr->baseLength], 10, | |
209 | &entryIdx)) { | |
210 | Tcl_AppendResult (interp, "invalid ", tblHdrPtr->handleBase, | |
211 | " handle: ", handle, (char *) NULL); | |
212 | return -1; | |
213 | } | |
214 | return entryIdx; | |
215 | ||
216 | } /* HandleDecode */ | |
217 | \f | |
218 | /*============================================================================= | |
219 | * Tcl_HandleTblInit -- | |
220 | * Create and initialize a Tcl dynamic handle table. The use count on the | |
221 | * table is set to one. | |
222 | * Parameters: | |
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. | |
227 | * Returns: | |
228 | * A pointer to the table header. | |
229 | *----------------------------------------------------------------------------- | |
230 | */ | |
231 | void_pt | |
232 | Tcl_HandleTblInit (handleBase, entrySize, initEntries) | |
233 | CONST char *handleBase; | |
234 | int entrySize; | |
235 | int initEntries; | |
236 | { | |
237 | tblHeader_pt tblHdrPtr; | |
238 | int baseLength = strlen ((char *) handleBase); | |
239 | ||
240 | tblHdrPtr = (tblHeader_pt) ckalloc (sizeof (tblHeader_t) + baseLength + 1); | |
241 | ||
242 | tblHdrPtr->useCount = 1; | |
243 | tblHdrPtr->baseLength = baseLength; | |
244 | strcpy (tblHdrPtr->handleBase, (char *) handleBase); | |
245 | ||
246 | /* | |
247 | * Calculate entry size, including header, rounded up to sizeof (int). | |
248 | */ | |
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); | |
256 | ||
257 | return (void_pt) tblHdrPtr; | |
258 | ||
259 | } /* Tcl_HandleTblInit */ | |
260 | \f | |
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. | |
265 | * Parameters: | |
266 | * o headerPtr (I) - Pointer to the table header. | |
267 | * o amount (I) - The amount to alter the use count by. | |
268 | * Returns: | |
269 | * The resulting use count. | |
270 | *----------------------------------------------------------------------------- | |
271 | */ | |
272 | int | |
273 | Tcl_HandleTblUseCount (headerPtr, amount) | |
274 | void_pt headerPtr; | |
275 | int amount; | |
276 | { | |
277 | tblHeader_pt tblHdrPtr = (tblHeader_pt)headerPtr; | |
278 | ||
279 | tblHdrPtr->useCount += amount; | |
280 | return tblHdrPtr->useCount; | |
281 | } | |
282 | \f | |
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. | |
288 | * Parameters: | |
289 | * o headerPtr (I) - Pointer to the table header. | |
290 | *----------------------------------------------------------------------------- | |
291 | */ | |
292 | void | |
293 | Tcl_HandleTblRelease (headerPtr) | |
294 | void_pt headerPtr; | |
295 | { | |
296 | tblHeader_pt tblHdrPtr = (tblHeader_pt)headerPtr; | |
297 | ||
298 | tblHdrPtr->useCount--; | |
299 | if (tblHdrPtr->useCount <= 0) { | |
300 | ckfree (tblHdrPtr->bodyP); | |
301 | ckfree ((char *) tblHdrPtr); | |
302 | } | |
303 | } | |
304 | \f | |
305 | /*============================================================================= | |
306 | * Tcl_HandleAlloc -- | |
307 | * Allocate an entry and associate a handle with it. | |
308 | * | |
309 | * Parameters: | |
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 | |
312 | * hold the name. | |
313 | * Returns: | |
314 | * A pointer to the allocated entry (user part). | |
315 | *----------------------------------------------------------------------------- | |
316 | */ | |
317 | void_pt | |
318 | Tcl_HandleAlloc (headerPtr, handlePtr) | |
319 | void_pt headerPtr; | |
320 | char *handlePtr; | |
321 | { | |
322 | tblHeader_pt tblHdrPtr = (tblHeader_pt)headerPtr; | |
323 | entryHeader_pt entryPtr; | |
324 | int entryIdx; | |
325 | ||
326 | entryPtr = AllocEntry ((tblHeader_pt) headerPtr, &entryIdx); | |
327 | sprintf (handlePtr, "%s%d", tblHdrPtr->handleBase, entryIdx); | |
328 | ||
329 | return USER_AREA (entryPtr); | |
330 | ||
331 | } /* Tcl_HandleAlloc */ | |
332 | \f | |
333 | /*============================================================================= | |
334 | * Tcl_HandleXlate -- | |
335 | * Translate a handle to a entry pointer. | |
336 | * | |
337 | * Parameters: | |
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. | |
341 | * Returns: | |
342 | * A pointer to the entry, or NULL if an error occured. | |
343 | *----------------------------------------------------------------------------- | |
344 | */ | |
345 | void_pt | |
346 | Tcl_HandleXlate (interp, headerPtr, handle) | |
347 | Tcl_Interp *interp; | |
348 | void_pt headerPtr; | |
349 | CONST char *handle; | |
350 | { | |
351 | tblHeader_pt tblHdrPtr = (tblHeader_pt)headerPtr; | |
352 | entryHeader_pt entryPtr; | |
353 | int entryIdx; | |
354 | ||
355 | if ((entryIdx = HandleDecode (interp, tblHdrPtr, handle)) < 0) | |
356 | return NULL; | |
357 | entryPtr = TBL_INDEX (tblHdrPtr, entryIdx); | |
358 | ||
359 | if ((entryIdx >= tblHdrPtr->tableSize) || | |
360 | (entryPtr->freeLink != ALLOCATED_IDX)) { | |
361 | Tcl_AppendResult (interp, tblHdrPtr->handleBase, " is not open", | |
362 | (char *) NULL); | |
363 | return NULL; | |
364 | } | |
365 | ||
366 | return USER_AREA (entryPtr); | |
367 | ||
368 | } /* Tcl_HandleXlate */ | |
369 | \f | |
370 | /*============================================================================= | |
371 | * Tcl_HandleWalk -- | |
372 | * Walk through and find every allocated entry in a table. Entries may | |
373 | * be deallocated during a walk, but should not be allocated. | |
374 | * | |
375 | * Parameters: | |
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 | |
379 | * the first call. | |
380 | * Returns: | |
381 | * A pointer to the next allocated entry, or NULL if there are not more. | |
382 | *----------------------------------------------------------------------------- | |
383 | */ | |
384 | void_pt | |
385 | Tcl_HandleWalk (headerPtr, walkKeyPtr) | |
386 | void_pt headerPtr; | |
387 | int *walkKeyPtr; | |
388 | { | |
389 | tblHeader_pt tblHdrPtr = (tblHeader_pt)headerPtr; | |
390 | int entryIdx; | |
391 | entryHeader_pt entryPtr; | |
392 | ||
393 | if (*walkKeyPtr == -1) | |
394 | entryIdx = 0; | |
395 | else | |
396 | entryIdx = *walkKeyPtr + 1; | |
397 | ||
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); | |
403 | } | |
404 | entryIdx++; | |
405 | } | |
406 | return NULL; | |
407 | ||
408 | } /* Tcl_HandleWalk */ | |
409 | \f | |
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. | |
414 | * Parameters: | |
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 | |
418 | * hold the name. | |
419 | *----------------------------------------------------------------------------- | |
420 | */ | |
421 | void | |
422 | Tcl_WalkKeyToHandle (headerPtr, walkKey, handlePtr) | |
423 | void_pt headerPtr; | |
424 | int walkKey; | |
425 | char *handlePtr; | |
426 | { | |
427 | tblHeader_pt tblHdrPtr = (tblHeader_pt)headerPtr; | |
428 | ||
429 | sprintf (handlePtr, "%s%d", tblHdrPtr->handleBase, walkKey); | |
430 | ||
431 | } /* Tcl_WalkKeyToHandle */ | |
432 | \f | |
433 | /*============================================================================= | |
434 | * Tcl_HandleFree -- | |
435 | * Frees a handle table entry. | |
436 | * | |
437 | * Parameters: | |
438 | * o headerPtr (I) - A pointer to the table header. | |
439 | * o entryPtr (I) - Entry to free. | |
440 | *----------------------------------------------------------------------------- | |
441 | */ | |
442 | void | |
443 | Tcl_HandleFree (headerPtr, entryPtr) | |
444 | void_pt headerPtr; | |
445 | void_pt entryPtr; | |
446 | { | |
447 | tblHeader_pt tblHdrPtr = (tblHeader_pt)headerPtr; | |
448 | entryHeader_pt freeentryPtr; | |
449 | ||
450 | freeentryPtr = HEADER_AREA (entryPtr); | |
451 | freeentryPtr->freeLink = tblHdrPtr->freeHeadIdx; | |
452 | tblHdrPtr->freeHeadIdx = (((ubyte_pt) entryPtr) - tblHdrPtr->bodyP) / | |
453 | tblHdrPtr->entrySize; | |
454 | ||
455 | } /* Tcl_HandleFree */ | |
456 |