1 /*
2  * nsfFunPtrHashTable.c --
3  *
4  *      Provide a custom Tcl hashtable type, using function pointers
5  *      as hash keys, and a slim wrapper around Tcl's hashtable
6  *      API to manage them.
7  *
8  * Copyright (C) 2016-2018 Gustaf Neumann
9  * Copyright (C) 2016 Stefan Sobernig
10  *
11  * Vienna University of Economics and Business
12  * Institute of Information Systems and New Media
13  * A-1020, Welthandelsplatz 1
14  * Vienna, Austria
15  *
16  * This work is licensed under the MIT License https://www.opensource.org/licenses/MIT
17  *
18  * Copyright:
19  *
20  * Permission is hereby granted, free of charge, to any person obtaining a
21  * copy of this software and associated documentation files (the "Software"),
22  * to deal in the Software without restriction, including without limitation
23  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
24  * and/or sell copies of the Software, and to permit persons to whom the
25  * Software is furnished to do so, subject to the following conditions:
26  *
27  * The above copyright notice and this permission notice shall be included in
28  * all copies or substantial portions of the Software.
29  *
30  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
35  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
36  * DEALINGS IN THE SOFTWARE.
37  *
38  */
39 
40 
41 #include "nsfInt.h"
42 
43 /*
44  * Definitions for HashType funPtr.
45  *
46  * Background: since it is not guaranteed that sizeof(function
47  * pointer) == sizeof(data pointer) (or sizeof(function pointer) <=
48  * sizeof(data pointer)), passing function pointers via data pointers
49  * - which is what default Tcl hash types do - is potentially
50  * dangerous. Therefore, and on top, it is not allowed under ISO
51  * C. So, we define our own type that allows one to hash on function
52  * pointers safely.
53  *
54  */
55 static unsigned int FunPtrKey(Tcl_HashTable *tablePtr, void *keyPtr);
56 static int CompareFunPtrKeys(void *keyPtr, Tcl_HashEntry *hPtr);
57 static Tcl_HashEntry *AllocFunPtrEntry(Tcl_HashTable *tablePtr, void *keyPtr);
58 
59 
60 typedef struct funPtrEntry_t {
61   Nsf_AnyFun *funPtr;
62 } funPtrEntry_t;
63 
64 static Tcl_HashKeyType funPtrHashKeyType = {
65   1,                 /* version*/
66   0,                 /* flags */
67   FunPtrKey,         /* hashKeyProc*/
68   CompareFunPtrKeys, /* compareKeysProc */
69   AllocFunPtrEntry,  /* allocEntryProc */
70   NULL               /* freeEntryProc */
71 };
72 
73 /*
74  *----------------------------------------------------------------------
75  *
76  * FunPtrKey --
77  *
78  *	Computes an unsigned int hash value from a function pointer.
79  *
80  * Results:
81  *	Returns the computed hash.
82  *
83  * Side effects:
84  *	None.
85  *
86  *----------------------------------------------------------------------
87  */
88 
89 static unsigned int
FunPtrKey(Tcl_HashTable * UNUSED (tablePtr),void * keyPtr)90 FunPtrKey(
91     Tcl_HashTable *UNUSED(tablePtr),      /* Hash table. */
92     void *keyPtr                          /* Key from which to compute hash value. */
93 ) {
94   funPtrEntry_t *e = (funPtrEntry_t *)keyPtr;
95   Nsf_AnyFun    *value  = e->funPtr;
96 
97   /*
98    * This is a very simple approach for obtaining a hash value. Maybe one
99    * needs a more sophisticated approach with weird endians machines.
100    */
101   return PTR2UINT(value);
102 
103 }
104 
105 /*
106  *----------------------------------------------------------------------
107  *
108  * CompareFunPtrKeys --
109  *
110  *	Compares two function pointer keys.
111  *
112  * Results:
113  *	The return value is 0 if they are different and 1 if they are the
114  *	same.
115  *
116  * Side effects:
117  *	None.
118  *
119  *----------------------------------------------------------------------
120  */
121 
122 static int
CompareFunPtrKeys(void * keyPtr,Tcl_HashEntry * hPtr)123 CompareFunPtrKeys(
124     void *keyPtr,		/* New key to compare. */
125     Tcl_HashEntry *hPtr         /* Existing key to compare. */
126 ) {
127   funPtrEntry_t *e = (funPtrEntry_t *)keyPtr;
128   Nsf_AnyFun    *existingValue;
129 
130   memcpy(&existingValue, &hPtr->key.oneWordValue, sizeof(Nsf_AnyFun *));
131 
132   return e->funPtr == existingValue;
133 }
134 
135 /*
136  *----------------------------------------------------------------------
137  *
138  * AllocFunPtrEntry --
139  *
140  *	Allocate space for a Tcl_HashEntry containing the function pointer.
141  *
142  * Results:
143  *	The return value is a pointer to the created entry.
144  *
145  * Side effects:
146  *	None.
147  *
148  *----------------------------------------------------------------------
149  */
150 static Tcl_HashEntry *
AllocFunPtrEntry(Tcl_HashTable * UNUSED (tablePtr),void * keyPtr)151 AllocFunPtrEntry(
152     Tcl_HashTable *UNUSED(tablePtr),	/* Hash table. */
153     void *keyPtr			/* Key to store in the hash table entry. */
154 ) {
155   funPtrEntry_t  *e = (funPtrEntry_t *)keyPtr;
156   Tcl_HashEntry  *hPtr;
157   unsigned int    size;
158   Nsf_AnyFun     *value = e->funPtr;
159 
160   size = sizeof(Tcl_HashEntry) + (sizeof(Nsf_AnyFun *)) - sizeof(hPtr->key);
161   if (size < sizeof(Tcl_HashEntry)) {
162     size = sizeof(Tcl_HashEntry);
163   }
164   hPtr = (Tcl_HashEntry *) ckalloc(size);
165 
166   memcpy(&hPtr->key.oneWordValue, &value, sizeof(Nsf_AnyFun *));
167 
168   hPtr->clientData = 0;
169 
170   return hPtr;
171 }
172 
173 
174 /*
175  *----------------------------------------------------------------------
176  * Nsf_InitFunPtrHashTable --
177  *
178  *    Initializes a hash table structure providing for function
179  *    pointers as hash keys. It is a slim wrapper around
180  *    Tcl_InitCustomHashTable().
181  *
182  * Results:
183  *    None.
184  *
185  * Side effects:
186  *    None.
187  *
188  *----------------------------------------------------------------------
189  */
190 void
Nsf_InitFunPtrHashTable(Tcl_HashTable * tablePtr)191 Nsf_InitFunPtrHashTable(Tcl_HashTable *tablePtr) {
192 
193   nonnull_assert(tablePtr != NULL);
194 
195   Tcl_InitCustomHashTable(tablePtr, TCL_CUSTOM_PTR_KEYS, &funPtrHashKeyType);
196 
197 }
198 
199 /*
200  *----------------------------------------------------------------------
201  * Nsf_CreateFunPtrHashEntry --
202  *
203  *    Creates or finds an entry based on a given function-pointer
204  *    key. It is a slim wrapper around Tcl_CreateHashEntry().
205  *
206  * Results:
207  *    Returns a pointer to the matching entry.
208  *
209  * Side effects:
210  *    A new entry may be stored in the hash table.
211  *
212  *----------------------------------------------------------------------
213  */
214 
215 Tcl_HashEntry *
Nsf_CreateFunPtrHashEntry(Tcl_HashTable * tablePtr,Nsf_AnyFun * key,int * isNew)216 Nsf_CreateFunPtrHashEntry(
217     Tcl_HashTable *tablePtr,
218     Nsf_AnyFun *key,
219     int *isNew
220 ) {
221   Tcl_HashEntry *hPtr;
222   funPtrEntry_t  entry;
223 
224   nonnull_assert(tablePtr != NULL);
225   nonnull_assert(key != NULL);
226 
227   entry.funPtr = key;
228   hPtr = Tcl_CreateHashEntry(tablePtr, (const char *)&entry, isNew);
229 
230   return hPtr;
231 }
232 
233 /*
234  *----------------------------------------------------------------------
235  * Nsf_FindFunPtrHashEntry --
236  *
237  *    Finds the entry with a matching function-pointer key in a given
238  *    table. It is a slim wrapper around Tcl_FindHashEntry().
239  *
240  * Results:
241  *    Returns a pointer to the matching entry, or NULL.
242  *
243  * Side effects:
244  *    None.
245  *
246  *----------------------------------------------------------------------
247  */
248 
249 Tcl_HashEntry *
Nsf_FindFunPtrHashEntry(Tcl_HashTable * tablePtr,Nsf_AnyFun * key)250 Nsf_FindFunPtrHashEntry(Tcl_HashTable *tablePtr, Nsf_AnyFun *key) {
251   Tcl_HashEntry *hPtr;
252   funPtrEntry_t  entry;
253 
254   nonnull_assert(tablePtr != NULL);
255   nonnull_assert(key != NULL);
256 
257   entry.funPtr = key;
258   hPtr = Tcl_FindHashEntry(tablePtr, (const char *)&entry);
259   return hPtr;
260 
261 }
262 
263 /*
264  * Local Variables:
265  * mode: c
266  * c-basic-offset: 2
267  * fill-column: 78
268  * indent-tabs-mode: nil
269  * eval: (c-guess)
270  * End:
271  */
272