Mercurial > hg > early-roguelike
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; |
