]> www.wagner.pp.ru Git - oss/ck.git/blob - ckPreserve.c
Ck console graphics toolkit
[oss/ck.git] / ckPreserve.c
1 /* 
2  * ckPreserve.c --
3  *
4  *      This file contains a collection of procedures that are used
5  *      to make sure that widget records and other data structures
6  *      aren't reallocated when there are nested procedures that
7  *      depend on their existence.
8  *
9  * Copyright (c) 1990-1994 The Regents of the University of California.
10  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
11  * Copyright (c) 1995 Christian Werner
12  *
13  * See the file "license.terms" for information on usage and redistribution
14  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15  */
16
17 #include "ckPort.h"
18 #include "ck.h"
19
20 #if (TCL_MAJOR_VERSION == 7) && (TCL_MINOR_VERSION <= 4)
21
22 /*
23  * The following data structure is used to keep track of all the
24  * Ck_Preserve calls that are still in effect.  It grows as needed
25  * to accommodate any number of calls in effect.
26  */
27
28 typedef struct {
29     ClientData clientData;      /* Address of preserved block. */
30     int refCount;               /* Number of Ck_Preserve calls in effect
31                                  * for block. */
32     int mustFree;               /* Non-zero means Ck_EventuallyFree was
33                                  * called while a Ck_Preserve call was in
34                                  * effect, so the structure must be freed
35                                  * when refCount becomes zero. */
36     Ck_FreeProc *freeProc;      /* Procedure to call to free. */
37 } Reference;
38
39 static Reference *refArray;     /* First in array of references. */
40 static int spaceAvl = 0;        /* Total number of structures available
41                                  * at *firstRefPtr. */
42 static int inUse = 0;           /* Count of structures currently in use
43                                  * in refArray. */
44 #define INITIAL_SIZE 2
45 \f
46 /*
47  *----------------------------------------------------------------------
48  *
49  * Ck_Preserve --
50  *
51  *      This procedure is used by a procedure to declare its interest
52  *      in a particular block of memory, so that the block will not be
53  *      reallocated until a matching call to Ck_Release has been made.
54  *
55  * Results:
56  *      None.
57  *
58  * Side effects:
59  *      Information is retained so that the block of memory will
60  *      not be freed until at least the matching call to Ck_Release.
61  *
62  *----------------------------------------------------------------------
63  */
64
65 void
66 Ck_Preserve(clientData)
67     ClientData clientData;      /* Pointer to malloc'ed block of memory. */
68 {
69     register Reference *refPtr;
70     int i;
71
72     /*
73      * See if there is already a reference for this pointer.  If so,
74      * just increment its reference count.
75      */
76
77     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
78         if (refPtr->clientData == clientData) {
79             refPtr->refCount++;
80             return;
81         }
82     }
83
84     /*
85      * Make a reference array if it doesn't already exist, or make it
86      * bigger if it is full.
87      */
88
89     if (inUse == spaceAvl) {
90         if (spaceAvl == 0) {
91             refArray = (Reference *) ckalloc((unsigned)
92                     (INITIAL_SIZE*sizeof(Reference)));
93             spaceAvl = INITIAL_SIZE;
94         } else {
95             Reference *new;
96
97             new = (Reference *) ckalloc((unsigned)
98                     (2*spaceAvl*sizeof(Reference)));
99             memcpy((VOID *) new, (VOID *) refArray, spaceAvl*sizeof(Reference));
100             ckfree((char *) refArray);
101             refArray = new;
102             spaceAvl *= 2;
103         }
104     }
105
106     /*
107      * Make a new entry for the new reference.
108      */
109
110     refPtr = &refArray[inUse];
111     refPtr->clientData = clientData;
112     refPtr->refCount = 1;
113     refPtr->mustFree = 0;
114     inUse += 1;
115 }
116 \f
117 /*
118  *----------------------------------------------------------------------
119  *
120  * Ck_Release --
121  *
122  *      This procedure is called to cancel a previous call to
123  *      Ck_Preserve, thereby allowing a block of memory to be
124  *      freed (if no one else cares about it).
125  *
126  * Results:
127  *      None.
128  *
129  * Side effects:
130  *      If Ck_EventuallyFree has been called for clientData, and if
131  *      no other call to Ck_Preserve is still in effect, the block of
132  *      memory is freed.
133  *
134  *----------------------------------------------------------------------
135  */
136
137 void
138 Ck_Release(clientData)
139     ClientData clientData;      /* Pointer to malloc'ed block of memory. */
140 {
141     register Reference *refPtr;
142     int i;
143
144     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
145         if (refPtr->clientData != clientData) {
146             continue;
147         }
148         refPtr->refCount--;
149         if (refPtr->refCount == 0) {
150             if (refPtr->mustFree) {
151                 if (refPtr->freeProc == (Ck_FreeProc *) free) {
152                     ckfree((char *) refPtr->clientData);
153                 } else {
154                     (*refPtr->freeProc)(refPtr->clientData);
155                 }
156             }
157
158             /*
159              * Copy down the last reference in the array to fill the
160              * hole left by the unused reference.
161              */
162
163             inUse--;
164             if (i < inUse) {
165                 refArray[i] = refArray[inUse];
166             }
167         }
168         return;
169     }
170
171     /*
172      * Reference not found.  This is a bug in the caller.
173      */
174
175     panic("Ck_Release couldn't find reference for 0x%x", clientData);
176 }
177 \f
178 /*
179  *----------------------------------------------------------------------
180  *
181  * Ck_EventuallyFree --
182  *
183  *      Free up a block of memory, unless a call to Ck_Preserve is in
184  *      effect for that block.  In this case, defer the free until all
185  *      calls to Ck_Preserve have been undone by matching calls to
186  *      Ck_Release.
187  *
188  * Results:
189  *      None.
190  *
191  * Side effects:
192  *      Ptr may be released by calling free().
193  *
194  *----------------------------------------------------------------------
195  */
196
197 void
198 Ck_EventuallyFree(clientData, freeProc)
199     ClientData clientData;      /* Pointer to malloc'ed block of memory. */
200     Ck_FreeProc *freeProc;      /* Procedure to actually do free. */
201 {
202     register Reference *refPtr;
203     int i;
204
205     /*
206      * See if there is a reference for this pointer.  If so, set its
207      * "mustFree" flag (the flag had better not be set already!).
208      */
209
210     for (i = 0, refPtr = refArray; i < inUse; i++, refPtr++) {
211         if (refPtr->clientData != clientData) {
212             continue;
213         }
214         if (refPtr->mustFree) {
215             panic("Ck_EventuallyFree called twice for 0x%x\n", clientData);
216         }
217         refPtr->mustFree = 1;
218         refPtr->freeProc = freeProc;
219         return;
220     }
221
222     /*
223      * No reference for this block.  Free it now.
224      */
225
226     if (freeProc == (Ck_FreeProc *) free) {
227         ckfree((char *) clientData);
228     } else {
229         (*freeProc)(clientData);
230     }
231 }
232
233 #endif /* TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION <= 4 */