1 /*
2 * Copyright (c) 1998-2002, Darren Hiebert
3 *
4 * This source code is released for free distribution under the terms of the
5 * GNU General Public License version 2 or (at your option) any later version.
6 *
7 * Manages a keyword hash.
8 */
9
10 /*
11 * INCLUDE FILES
12 */
13 #include "general.h" /* must always come first */
14
15 #include <string.h>
16 #include <ctype.h>
17
18 #include "debug.h"
19 #include "keyword.h"
20 #include "keyword_p.h"
21 #include "parse.h"
22 #include "routines.h"
23
24 /*
25 * DATA DECLARATIONS
26 */
27 typedef struct sHashEntry {
28 struct sHashEntry *next;
29 const char *string;
30 langType language;
31 int value;
32 } hashEntry;
33
34 /*
35 * DATA DEFINITIONS
36 */
37 static const unsigned int TableSize = 2039; /* prime */
38 static hashEntry **HashTable = NULL;
39
40 /*
41 * FUNCTION DEFINITIONS
42 */
43
getHashTable(void)44 static hashEntry **getHashTable (void)
45 {
46 static bool allocated = false;
47
48 if (! allocated)
49 {
50 unsigned int i;
51
52 HashTable = xMalloc (TableSize, hashEntry*);
53
54 for (i = 0 ; i < TableSize ; ++i)
55 HashTable [i] = NULL;
56
57 allocated = true;
58 }
59 return HashTable;
60 }
61
getHashTableEntry(unsigned long hashedValue)62 static hashEntry *getHashTableEntry (unsigned long hashedValue)
63 {
64 hashEntry **const table = getHashTable ();
65 hashEntry *entry;
66
67 Assert (hashedValue < TableSize);
68 entry = table [hashedValue];
69
70 return entry;
71 }
72
hashValue(const char * const string,langType language)73 static unsigned int hashValue (const char *const string, langType language)
74 {
75 const signed char *p;
76 unsigned int h = 5381;
77
78 Assert (string != NULL);
79
80 /* "djb" hash as used in g_str_hash() in glib */
81 for (p = (const signed char *)string; *p != '\0'; p++)
82 h = (h << 5) + h + tolower (*p);
83
84 /* consider language as an extra "character" and add it to the hash */
85 h = (h << 5) + h + language;
86
87 return h;
88 }
89
newEntry(const char * const string,langType language,int value)90 static hashEntry *newEntry (
91 const char *const string, langType language, int value)
92 {
93 hashEntry *const entry = xMalloc (1, hashEntry);
94
95 entry->next = NULL;
96 entry->string = string;
97 entry->language = language;
98 entry->value = value;
99
100 return entry;
101 }
102
103 /* Note that it is assumed that a "value" of zero means an undefined keyword
104 * and clients of this function should observe this. Also, all keywords added
105 * should be added in lower case. If we encounter a case-sensitive language
106 * whose keywords are in upper case, we will need to redesign this.
107 */
addKeyword(const char * const string,langType language,int value)108 extern void addKeyword (const char *const string, langType language, int value)
109 {
110 const unsigned int index = hashValue (string, language) % TableSize;
111 hashEntry *entry = getHashTableEntry (index);
112
113 if (entry == NULL)
114 {
115 hashEntry **const table = getHashTable ();
116 table [index] = newEntry (string, language, value);
117 }
118 else
119 {
120 hashEntry *prev = NULL;
121
122 while (entry != NULL)
123 {
124 if (language == entry->language &&
125 strcmp (string, entry->string) == 0)
126 {
127 Assert (("Already in table" == NULL));
128 }
129 prev = entry;
130 entry = entry->next;
131 }
132 if (entry == NULL)
133 {
134 Assert (prev != NULL);
135 prev->next = newEntry (string, language, value);
136 }
137 }
138 }
139
lookupKeywordFull(const char * const string,bool caseSensitive,langType language)140 static int lookupKeywordFull (const char *const string, bool caseSensitive, langType language)
141 {
142 const unsigned int index = hashValue (string, language) % TableSize;
143 hashEntry *entry = getHashTableEntry (index);
144 int result = KEYWORD_NONE;
145
146 while (entry != NULL)
147 {
148 if (language == entry->language &&
149 ((caseSensitive && strcmp (string, entry->string) == 0) ||
150 (!caseSensitive && strcasecmp (string, entry->string) == 0)))
151 {
152 result = entry->value;
153 break;
154 }
155 entry = entry->next;
156 }
157 return result;
158 }
159
lookupKeyword(const char * const string,langType language)160 extern int lookupKeyword (const char *const string, langType language)
161 {
162 return lookupKeywordFull (string, true, language);
163 }
164
lookupCaseKeyword(const char * const string,langType language)165 extern int lookupCaseKeyword (const char *const string, langType language)
166 {
167 return lookupKeywordFull (string, false, language);
168 }
169
freeKeywordTable(void)170 extern void freeKeywordTable (void)
171 {
172 if (HashTable != NULL)
173 {
174 unsigned int i;
175
176 for (i = 0 ; i < TableSize ; ++i)
177 {
178 hashEntry *entry = HashTable [i];
179
180 while (entry != NULL)
181 {
182 hashEntry *next = entry->next;
183 eFree (entry);
184 entry = next;
185 }
186 }
187 eFree (HashTable);
188 }
189 }
190
191 #ifdef DEBUG
192
printEntry(const hashEntry * const entry)193 static void printEntry (const hashEntry *const entry)
194 {
195 printf (" %-15s %-7s\n", entry->string, getLanguageName (entry->language));
196 }
197
printBucket(const unsigned int i)198 static unsigned int printBucket (const unsigned int i)
199 {
200 hashEntry **const table = getHashTable ();
201 hashEntry *entry = table [i];
202 unsigned int measure = 1;
203 bool first = true;
204
205 printf ("%2d:", i);
206 if (entry == NULL)
207 printf ("\n");
208 else while (entry != NULL)
209 {
210 if (! first)
211 printf (" ");
212 else
213 {
214 printf (" ");
215 first = false;
216 }
217 printEntry (entry);
218 entry = entry->next;
219 measure = 2 * measure;
220 }
221 return measure - 1;
222 }
223
printKeywordTable(void)224 extern void printKeywordTable (void)
225 {
226 unsigned long emptyBucketCount = 0;
227 unsigned long measure = 0;
228 unsigned int i;
229
230 for (i = 0 ; i < TableSize ; ++i)
231 {
232 const unsigned int pass = printBucket (i);
233
234 measure += pass;
235 if (pass == 0)
236 ++emptyBucketCount;
237 }
238
239 printf ("spread measure = %ld\n", measure);
240 printf ("%ld empty buckets\n", emptyBucketCount);
241 }
242
243 #endif
244
dumpKeywordTable(FILE * fp)245 extern void dumpKeywordTable (FILE *fp)
246 {
247 unsigned int i;
248 for (i = 0 ; i < TableSize ; ++i)
249 {
250 hashEntry **const table = getHashTable ();
251 hashEntry *entry = table [i];
252 while (entry != NULL)
253 {
254 fprintf(fp, "%s %s\n", entry->string, getLanguageName (entry->language));
255 entry = entry->next;
256 }
257 }
258 }
259
addKeywordGroup(const struct keywordGroup * const groupdef,langType language)260 extern void addKeywordGroup (const struct keywordGroup *const groupdef,
261 langType language)
262 {
263 for (int i = 0; groupdef->keywords[i]; i++)
264 {
265 if (groupdef->addingUnlessExisting)
266 {
267 if (lookupKeyword (groupdef->keywords[i],
268 language) != KEYWORD_NONE)
269 continue; /* already added */
270 }
271 else
272 Assert (lookupKeyword (groupdef->keywords[i],
273 language) == KEYWORD_NONE);
274 addKeyword (groupdef->keywords[i], language, groupdef->value);
275 }
276 }
277