xref: /Universal-ctags/main/keyword.c (revision 9a224e2e11fb14f4a942e6e34ad6e36ca26660fc)
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