comparison urogue/dict.c @ 256:c495a4f288c6

Import UltraRogue from the Roguelike Restoration Project (r1490)
author John "Elwin" Edwards
date Tue, 31 Jan 2017 19:56:04 -0500
parents
children 13b482bd9e66
comparison
equal deleted inserted replaced
253:d9badb9c0179 256:c495a4f288c6
1 /*
2 dict.c
3
4 UltraRogue: The Ultimate Adventure in the Dungeons of Doom
5 Copyright (C) 1995 Herb Chong
6 All rights reserved.
7
8 See the file LICENSE.TXT for full copyright and licensing information.
9 */
10
11 /******************
12 ** Change history:
13 **
14 ** (AK:04/03/95) - In dict_create, initialize hook for extensions to dictionary structure.
15 ** (AK:04/04/95) - In dict_insert, only set any_ptr when a new string entry is created.
16 ** (AK:04/17/95) - Added dict_union and dict_merge.
17 ** (AK:04/18/95) - In dict_create, added code to create signature,
18 ** table of contents, and parameter array.
19 ** (AK:04/18/95) - Revised dict_load, dict_save
20 ** (AK:04/18/95) - Added dict_import
21 **
22 ******************/
23
24 static char sccsid[] = "%W% %G%";
25
26 #include <stdlib.h>
27 #include <string.h>
28 #if !defined(OS2) && !defined(_WIN32)
29 #include <unistd.h>
30 #endif
31
32 #include "dict.h"
33 #include "dictutil.h"
34
35 #define BUFLEN 300
36
37 /*************************************************************************
38 * Generic dictionary and hash table functions for word and
39 * string storage.
40 *
41 * Implements generic dictionary management by using a
42 * hash table with direct chaining. All terms are stored in a
43 * separate, unsorted, string table. When inserting into the
44 * hash table exceeds the current size, allocate a new hash table
45 * and rehash the old contents into the new table. When the string
46 * table fills, append more empty entries onto the old table and
47 * continue adding. Inserting a string that already exists increments
48 * the count for the string. Deleting a string only decrements the
49 * count to no less than 0. This will allow a 0 occurence string to be
50 * retrieved. To remove the 0 occurence string table entries, you must
51 * rebuild the dictionary. Merging from one dictionary to a new one will
52 * do this because it only copies nonzero occurences.
53 *
54 * Assumptions:
55 * unsigned long >= 32 bits
56 * int >= 16 bits
57 * char == 8 bits
58 * number of entries < 2^28
59 *************************************************************************/
60
61 /*************************************************************************
62 * hash_string: calculate hash value for string, modified
63 * version of hashpjw() from the new Dragon book.
64 *
65 * s - string to calculate hash for
66 *
67 * Returns: hash value
68 *************************************************************************/
69 static unsigned long hash_string(const char *s)
70 {
71 unsigned long h = 0;
72 unsigned long g = 0;
73
74 while (*s) {
75 h <<= 4;
76 h += *s++;
77 if ((g = h & 0xf0000000) != 0)
78 h ^= g >> 24;
79 }
80 return h ^ g;
81 }
82
83 /*************************************************************************
84 * dict_create: create a dictionary and initialize its structures
85 *
86 * toc_size - number of entries in table of contents ( >= 4 )
87 * initial_string_count - number of strings to hold as initial allocation
88 * initial_hash_chains - size of hash table, must be power of 4
89 * max_chain_length - max number of elements in hash chain before signalling growth
90 *
91 * Returns: pointer to dictionary
92 * NULL - error creating dictionary
93 * non-NULL - sucessful creation
94 *************************************************************************/
95
96 DICTIONARY *dict_create(
97 const long toc_size,
98 const long initial_string_count,
99 const long initial_hash_chains,
100 const long max_chain_length )
101 {
102 DICTIONARY *dtemp = NULL;
103 STRING_ENTRY *stemp = NULL;
104 long *ltemp = NULL;
105 char *sltemp;
106 long i, j, tocsize;
107
108 /* check for a power of 4 in number of initial hash entries */
109 switch(initial_hash_chains) {
110 case 0x00000004:
111 case 0x00000010:
112 case 0x00000040:
113 case 0x00000100:
114 case 0x00000400:
115 case 0x00001000:
116 case 0x00004000:
117 case 0x00010000:
118 case 0x00040000:
119 case 0x00100000:
120 case 0x00400000:
121 case 0x01000000:
122 case 0x04000000:
123 break;
124 default:
125 return NULL;
126 }
127
128 /* Allocate the dictionary structure */
129 if ((dtemp = (DICTIONARY *)malloc(sizeof(DICTIONARY))) == NULL)
130 goto err_exit;
131
132 /* string count has to be within range */
133 j = initial_string_count;
134 if (j > 0x04000000)
135 return NULL;
136
137 /* force a reasonable value */
138 if (j < 100)
139 j = 100;
140
141 /* Allocate the string table and string array */
142 if ((stemp = (STRING_ENTRY *)malloc(sizeof(STRING_ENTRY) * j)) == NULL)
143 goto err_exit;
144 if ((sltemp = (char *)malloc(8 * j)) == NULL)
145 goto err_exit;
146
147 /* Allocate the hash table */
148 if ((ltemp = (long *)malloc(sizeof(long) * initial_hash_chains)) == NULL)
149 goto err_exit;
150
151 /* Allocate the parameter array */
152 if ( (dtemp->parm = (DICT_PARM_ENTRY *)malloc(14*sizeof(DICT_PARM_ENTRY))) == NULL)
153 goto err_exit;
154
155 /* Allocate the signature structure */
156 if ( (dtemp->sig=(DICT_SIG*)malloc(sizeof(DICT_SIG))) == NULL )
157 goto err_exit;
158
159 /* Allocate the Table of Contents */
160 tocsize = toc_size;
161 if ( tocsize < 4 )
162 tocsize = 4;
163 dtemp->sig->toc_size = tocsize;
164 if ( (dtemp->toc=(DICT_TOC_ENTRY*)malloc(dtemp->sig->toc_size*sizeof(DICT_TOC_ENTRY))) == NULL )
165 goto err_exit;
166
167 dtemp->check_value = DICT_VALIDATE; /* validation value */
168 dtemp->flags = DICT_NONE; /* no flags set */
169 dtemp->entry_count = 0; /* nothing in either table */
170
171 dtemp->string_table = stemp; /* connect string table */
172 dtemp->string_max = j; /* size of string table */
173 dtemp->scan_string_index = DICT_ENTRY_NONE; /* not pointing to anything in scan */
174 dtemp->string_growth_count = 0; /* haven't grown any */
175
176 dtemp->string_array = sltemp; /* character array for strings */
177 dtemp->array_size = j * 8; /* how many bytes available */
178 dtemp->array_used = 0; /* nothing used yet */
179 dtemp->array_growth_count = 0; /* haven't grown any */
180
181 /* force maximum hash chain length to a reasonable value */
182 i = max_chain_length;
183 if (i < 1 || i > 200)
184 i = 200;
185 dtemp->chains = ltemp; /* connect hash table */
186 dtemp->longest_chain_length = 0; /* nothing in table yet */
187 dtemp->allowable_chain_length = i; /* chain length limit */
188 dtemp->table_size = initial_hash_chains; /* size of hash table */
189 dtemp->hash_mask = initial_hash_chains - 1; /* mask for mod() function */
190 dtemp->hash_growth_count = 0; /* haven't grown any */
191
192 /* string table is empty */
193 for (i = 0 ; i < j ; i++) {
194 dtemp->string_table[i].string_offset = DICT_ENTRY_NONE;
195 dtemp->string_table[i].count = 0;
196 dtemp->string_table[i].next = DICT_ENTRY_NONE;
197 dtemp->string_table[i].flags = 0;
198 dtemp->string_table[i].hash_value = 0;
199 }
200
201 /* no entries chained */
202 for (i = 0; i < initial_hash_chains; i++)
203 dtemp->chains[i] = DICT_ENTRY_NONE;
204
205 /* Initialize hook to extended dictionary structure */
206 dtemp->ext = NULL;
207
208 /* Set up the parameter array */
209 if ( dict_set_parm_ids(dtemp) == FALSE )
210 goto err_exit;
211 if ( dict_set_parm_values(dtemp) == FALSE )
212 goto err_exit;
213
214 /* Set up the Table of Contents */
215 strcpy( dtemp->toc[0].id , "PARM" );
216 dtemp->toc[0].size = dtemp->sig->nparms * sizeof(DICT_PARM_ENTRY);
217 dtemp->toc[0].ptr = dtemp->parm;
218 dtemp->toc[0].type = 0;
219
220 strcpy( dtemp->toc[1].id , "HASH" );
221 dtemp->toc[1].size = dtemp->table_size * sizeof(long);
222 dtemp->toc[1].ptr = dtemp->chains;
223 dtemp->toc[1].type = 0;
224
225 strcpy( dtemp->toc[2].id , "STTB" );
226 dtemp->toc[2].size = dtemp->string_max * sizeof(char);
227 dtemp->toc[2].ptr = dtemp->string_table;
228 dtemp->toc[2].type = 0;
229
230 strcpy( dtemp->toc[3].id , "STAR" );
231 dtemp->toc[3].size = dtemp->array_size * sizeof(STRING_ENTRY);
232 dtemp->toc[3].ptr = dtemp->string_array;
233 dtemp->toc[3].type = 0;
234
235 /* Set up the signature */
236 dtemp->sig->check_value = DICT_VALIDATE;
237 dtemp->sig->toc_size = tocsize;
238 dtemp->sig->nparms = 14;
239
240 return dtemp;
241
242 /* error exit - saves duplicate code */
243 err_exit:
244
245 dict_destroy( dtemp );
246 return NULL;
247 }
248
249 /*************************************************************************
250 * dict_destroy: discard a dictionary and its contents
251 * multiple calls to destroy the same dictionary is safe
252 *
253 * dict - pointer to a dictionary to discard
254 *
255 * Returns: status code
256 * TRUE - dictionary destroyed
257 * FALSE - error when destroying dictionary
258 * Note: Does free the dictionary structure itself.
259 *************************************************************************/
260 BOOLEANC dict_destroy(DICTIONARY *dict)
261 {
262 int i;
263
264 /* have to point to something */
265 if (dict == NULL)
266 return FALSE;
267
268 /* check value has to be OK */
269 if (dict->check_value != DICT_VALIDATE)
270 return FALSE;
271
272 if ( (dict->sig==NULL) || (dict->toc==NULL) || (dict->parm==NULL) )
273 return FALSE;
274
275 /* Free the type=0 tables */
276 for ( i = 0 ; i < dict->sig->toc_size ; i++ ) {
277 if ( dict->toc[i].ptr != NULL && dict->toc[i].type == 0 )
278 free( dict->toc[i].ptr );
279 } /* endfor */
280
281 /* Free the Table of Contents and signature */
282 free( dict->toc );
283 free( dict->sig );
284
285 /* Free the dictionary structure itself */
286 free( dict );
287
288 return TRUE;
289 }
290
291 /*************************************************************************
292 * dict_insert: add entries into the dictionary, growing as necessary
293 *
294 * dict - pointer to dictionary to insert into
295 * s - string to insert
296 * count - count of occurences of string
297 * flags - flag bits associated with the string
298 * number - pointer to long to return word number
299 *
300 * Returns: pointer to new entry in dictionary
301 * NULL - error during insert
302 * non-NULL - pointer to inserted or updated entry
303 *
304 * Notes: if the entry already exists, increment the count.
305 * If NULL is returned, the dictionary state can no longer
306 * be trusted. Terminating with an error code would be
307 * safest.
308 *************************************************************************/
309 STRING_ENTRY *dict_insert(DICTIONARY *dict, char *s,
310 const long occurences, const unsigned long flags,
311 void *any_ptr, long *number)
312 {
313 unsigned long hash_value;
314 long hash_index;
315 long string_index = -1;
316 long he2;
317 int chain_len;
318
319 /* have to point to something */
320 if (dict == NULL)
321 return FALSE;
322
323 /* check value has to be OK */
324 if (dict->check_value != DICT_VALIDATE)
325 return FALSE;
326
327 /* must have a string */
328 if (s == NULL) {
329 *number = -1;
330 return FALSE;
331 }
332
333 /* must be non-NULL */
334 if (s[0] == '\0') {
335 *number = -1;
336 return FALSE;
337 }
338
339 /* figure out which chain it should go into */
340 hash_value = hash_string(s);
341 hash_index = hash_value & dict->hash_mask;
342
343 /* look for the entry in the chain */
344 for (he2 = dict->chains[hash_index], chain_len = 0; he2 != DICT_ENTRY_NONE;
345 he2 = dict->string_table[he2].next, chain_len++) {
346 char *sa = &dict->string_array[dict->string_table[he2].string_offset];
347
348 /* compare hash_value and then string, in that order, for speed */
349 if (dict->string_table[he2].hash_value == hash_value && !strcmp(s, sa)) {
350 string_index = he2;