xref: /PHP-7.3/ext/mbstring/oniguruma/src/st.c (revision d48b2339)
1 /* This is a public domain general purpose hash table package written by Peter Moore @ UCB. */
2 
3 /* static	char	sccsid[] = "@(#) st.c 5.1 89/12/14 Crucible"; */
4 
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 
9 #ifdef _WIN32
10 #include <malloc.h>
11 #endif
12 
13 #include "regint.h"
14 #include "st.h"
15 
16 typedef struct st_table_entry st_table_entry;
17 
18 struct st_table_entry {
19     unsigned int hash;
20     st_data_t key;
21     st_data_t record;
22     st_table_entry *next;
23 };
24 
25 #define ST_DEFAULT_MAX_DENSITY 5
26 #define ST_DEFAULT_INIT_TABLE_SIZE 11
27 
28     /*
29      * DEFAULT_MAX_DENSITY is the default for the largest we allow the
30      * average number of items per bin before increasing the number of
31      * bins
32      *
33      * DEFAULT_INIT_TABLE_SIZE is the default for the number of bins
34      * allocated initially
35      *
36      */
37 
38 static int numcmp(long, long);
39 static int numhash(long);
40 static struct st_hash_type type_numhash = {
41     numcmp,
42     numhash,
43 };
44 
45 /* extern int strcmp(const char *, const char *); */
46 static int strhash(const char *);
47 static struct st_hash_type type_strhash = {
48     strcmp,
49     strhash,
50 };
51 
52 static void rehash(st_table *);
53 
54 #define alloc(type) (type*)xmalloc((unsigned)sizeof(type))
55 #define Calloc(n,s) (char*)xcalloc((n),(s))
56 
57 #define EQUAL(table,x,y) ((x)==(y) || (*table->type->compare)((x),(y)) == 0)
58 
59 #define do_hash(key,table) (unsigned int)(*(table)->type->hash)((key))
60 #define do_hash_bin(key,table) (do_hash(key, table)%(table)->num_bins)
61 
62 /*
63  * MINSIZE is the minimum size of a dictionary.
64  */
65 
66 #define MINSIZE 8
67 
68 /*
69 Table of prime numbers 2^n+a, 2<=n<=30.
70 */
71 static const long primes[] = {
72 	8 + 3,
73 	16 + 3,
74 	32 + 5,
75 	64 + 3,
76 	128 + 3,
77 	256 + 27,
78 	512 + 9,
79 	1024 + 9,
80 	2048 + 5,
81 	4096 + 3,
82 	8192 + 27,
83 	16384 + 43,
84 	32768 + 3,
85 	65536 + 45,
86 	131072 + 29,
87 	262144 + 3,
88 	524288 + 21,
89 	1048576 + 7,
90 	2097152 + 17,
91 	4194304 + 15,
92 	8388608 + 9,
93 	16777216 + 43,
94 	33554432 + 35,
95 	67108864 + 15,
96 	134217728 + 29,
97 	268435456 + 3,
98 	536870912 + 11,
99 	1073741824 + 85,
100 	0
101 };
102 
103 static int
new_size(size)104 new_size(size)
105     int size;
106 {
107     int i;
108 
109 #if 0
110     for (i=3; i<31; i++) {
111       if ((1<<i) > size) return 1<<i;
112     }
113     return -1;
114 #else
115     int newsize;
116 
117     for (i = 0, newsize = MINSIZE;
118          i < (int )(sizeof(primes)/sizeof(primes[0]));
119          i++, newsize <<= 1) {
120       if (newsize > size) return primes[i];
121     }
122     /* Ran out of polynomials */
123     return -1;			/* should raise exception */
124 #endif
125 }
126 
127 #ifdef HASH_LOG
128 static int collision = 0;
129 static int init_st = 0;
130 
131 static void
stat_col(void)132 stat_col(void)
133 {
134   FILE *f = fopen("/tmp/col", "w");
135   if (f == 0) return ;
136 
137   (void) fprintf(f, "collision: %d\n", collision);
138   (void) fclose(f);
139 }
140 #endif
141 
142 st_table*
st_init_table_with_size(type,size)143 st_init_table_with_size(type, size)
144     struct st_hash_type *type;
145     int size;
146 {
147   st_table *tbl;
148 
149 #ifdef HASH_LOG
150   if (init_st == 0) {
151     init_st = 1;
152     atexit(stat_col);
153   }
154 #endif
155 
156   size = new_size(size);	/* round up to prime number */
157 
158   tbl = alloc(st_table);
159   if (tbl == 0) return 0;
160 
161   tbl->type = type;
162   tbl->num_entries = 0;
163   tbl->num_bins = size;
164   tbl->bins = (st_table_entry **)Calloc(size, sizeof(st_table_entry*));
165   if (tbl->bins == 0) {
166     free(tbl);
167     return 0;
168   }
169 
170   return tbl;
171 }
172 
173 st_table*
st_init_table(type)174 st_init_table(type)
175     struct st_hash_type *type;
176 {
177   return st_init_table_with_size(type, 0);
178 }
179 
180 st_table*
st_init_numtable(void)181 st_init_numtable(void)
182 {
183   return st_init_table(&type_numhash);
184 }
185 
186 st_table*
st_init_numtable_with_size(size)187 st_init_numtable_with_size(size)
188     int size;
189 {
190   return st_init_table_with_size(&type_numhash, size);
191 }
192 
193 st_table*
st_init_strtable(void)194 st_init_strtable(void)
195 {
196   return st_init_table(&type_strhash);
197 }
198 
199 st_table*
st_init_strtable_with_size(size)200 st_init_strtable_with_size(size)
201     int size;
202 {
203   return st_init_table_with_size(&type_strhash, size);
204 }
205 
206 void
st_free_table(table)207 st_free_table(table)
208     st_table *table;
209 {
210   register st_table_entry *ptr, *next;
211   int i;
212 
213   for(i = 0; i < table->num_bins; i++) {
214     ptr = table->bins[i];
215     while (ptr != 0) {
216 	    next = ptr->next;
217 	    free(ptr);
218 	    ptr = next;
219     }
220   }
221   free(table->bins);
222   free(table);
223 }
224 
225 #define PTR_NOT_EQUAL(table, ptr, hash_val, key) \
226 ((ptr) != 0 && (ptr->hash != (hash_val) || !EQUAL((table), (key), (ptr)->key)))
227 
228 #ifdef HASH_LOG
229 #define COLLISION collision++
230 #else
231 #define COLLISION
232 #endif
233 
234 #define FIND_ENTRY(table, ptr, hash_val, bin_pos) do {\
235     bin_pos = hash_val%(table)->num_bins;\
236     ptr = (table)->bins[bin_pos];\
237     if (PTR_NOT_EQUAL(table, ptr, hash_val, key)) {\
238       COLLISION;\
239       while (PTR_NOT_EQUAL(table, ptr->next, hash_val, key)) {\
240         ptr = ptr->next;\
241       }\
242       ptr = ptr->next;\
243     }\
244 } while (0)
245 
246 int
st_lookup(table,key,value)247 st_lookup(table, key, value)
248      st_table *table;
249      register st_data_t key;
250      st_data_t *value;
251 {
252   unsigned int hash_val, bin_pos;
253   register st_table_entry *ptr;
254 
255   hash_val = do_hash(key, table);
256   FIND_ENTRY(table, ptr, hash_val, bin_pos);
257 
258   if (ptr == 0) {
259     return 0;
260   }
261   else {
262     if (value != 0)  *value = ptr->record;
263     return 1;
264   }
265 }
266 
267 #define ADD_DIRECT(table, key, value, hash_val, bin_pos, ret) \
268 do {\
269   st_table_entry *entry;\
270   if (table->num_entries/(table->num_bins) > ST_DEFAULT_MAX_DENSITY) {\
271     rehash(table);\
272     bin_pos = hash_val % table->num_bins;\
273   }\
274   entry = alloc(st_table_entry);\
275   if (IS_NULL(entry)) return ret;\
276   entry->hash = hash_val;\
277   entry->key = key;\
278   entry->record = value;\
279   entry->next = table->bins[bin_pos];\
280   table->bins[bin_pos] = entry;\
281   table->num_entries++;\
282 } while (0)
283 
284 int
st_insert(table,key,value)285 st_insert(table, key, value)
286      register st_table *table;
287      register st_data_t key;
288      st_data_t value;
289 {
290   unsigned int hash_val, bin_pos;
291   register st_table_entry *ptr;
292 
293   hash_val = do_hash(key, table);
294   FIND_ENTRY(table, ptr, hash_val, bin_pos);
295 
296   if (ptr == 0) {
297     ADD_DIRECT(table, key, value, hash_val, bin_pos, ONIGERR_MEMORY);
298     return 0;
299   }
300   else {
301     ptr->record = value;
302     return 1;
303   }
304 }
305 
306 void
st_add_direct(table,key,value)307 st_add_direct(table, key, value)
308      st_table *table;
309      st_data_t key;
310      st_data_t value;
311 {
312   unsigned int hash_val, bin_pos;
313 
314   hash_val = do_hash(key, table);
315   bin_pos = hash_val % table->num_bins;
316   ADD_DIRECT(table, key, value, hash_val, bin_pos,);
317 }
318 
319 static void
rehash(table)320 rehash(table)
321      register st_table *table;
322 {
323   register st_table_entry *ptr, *next, **new_bins;
324   int i, old_num_bins = table->num_bins, new_num_bins;
325   unsigned int hash_val;
326 
327   new_num_bins = new_size(old_num_bins+1);
328   new_bins = (st_table_entry**)Calloc(new_num_bins, sizeof(st_table_entry*));
329   if (new_bins == 0) {
330     return ;
331   }
332 
333   for(i = 0; i < old_num_bins; i++) {
334     ptr = table->bins[i];
335     while (ptr != 0) {
336 	    next = ptr->next;
337 	    hash_val = ptr->hash % new_num_bins;
338 	    ptr->next = new_bins[hash_val];
339 	    new_bins[hash_val] = ptr;
340 	    ptr = next;
341     }
342   }
343   free(table->bins);
344   table->num_bins = new_num_bins;
345   table->bins = new_bins;
346 }
347 
348 st_table*
st_copy(old_table)349 st_copy(old_table)
350      st_table *old_table;
351 {
352   st_table *new_table;
353   st_table_entry *ptr, *entry;
354   int i, num_bins = old_table->num_bins;
355 
356   new_table = alloc(st_table);
357   if (new_table == 0) {
358     return 0;
359   }
360 
361   *new_table = *old_table;
362   new_table->bins = (st_table_entry**)
363     Calloc((unsigned)num_bins, sizeof(st_table_entry*));
364 
365   if (new_table->bins == 0) {
366     free(new_table);
367     return 0;
368   }
369 
370   for(i = 0; i < num_bins; i++) {
371     new_table->bins[i] = 0;
372     ptr = old_table->bins[i];
373     while (ptr != 0) {
374 	    entry = alloc(st_table_entry);
375 	    if (entry == 0) {
376         free(new_table->bins);
377         free(new_table);
378         return 0;
379 	    }
380 	    *entry = *ptr;
381 	    entry->next = new_table->bins[i];
382 	    new_table->bins[i] = entry;
383 	    ptr = ptr->next;
384     }
385   }
386   return new_table;
387 }
388 
389 int
st_delete(table,key,value)390 st_delete(table, key, value)
391      register st_table *table;
392      register st_data_t *key;
393      st_data_t *value;
394 {
395   unsigned int hash_val;
396   st_table_entry *tmp;
397   register st_table_entry *ptr;
398 
399   hash_val = do_hash_bin(*key, table);
400   ptr = table->bins[hash_val];
401 
402   if (ptr == 0) {
403     if (value != 0) *value = 0;
404     return 0;
405   }
406 
407   if (EQUAL(table, *key, ptr->key)) {
408     table->bins[hash_val] = ptr->next;
409     table->num_entries--;
410     if (value != 0) *value = ptr->record;
411     *key = ptr->key;
412     free(ptr);
413     return 1;
414   }
415 
416   for(; ptr->next != 0; ptr = ptr->next) {
417     if (EQUAL(table, ptr->next->key, *key)) {
418 	    tmp = ptr->next;
419 	    ptr->next = ptr->next->next;
420 	    table->num_entries--;
421 	    if (value != 0) *value = tmp->record;
422 	    *key = tmp->key;
423 	    free(tmp);
424 	    return 1;
425     }
426   }
427 
428   return 0;
429 }
430 
431 int
st_delete_safe(table,key,value,never)432 st_delete_safe(table, key, value, never)
433      register st_table *table;
434      register st_data_t *key;
435      st_data_t *value;
436      st_data_t never;
437 {
438   unsigned int hash_val;
439   register st_table_entry *ptr;
440 
441   hash_val = do_hash_bin(*key, table);
442   ptr = table->bins[hash_val];
443 
444   if (ptr == 0) {
445     if (value != 0) *value = 0;
446     return 0;
447   }
448 
449   for(; ptr != 0; ptr = ptr->next) {
450     if ((ptr->key != never) && EQUAL(table, ptr->key, *key)) {
451 	    table->num_entries--;
452 	    *key = ptr->key;
453 	    if (value != 0) *value = ptr->record;
454 	    ptr->key = ptr->record = never;
455 	    return 1;
456     }
457   }
458 
459   return 0;
460 }
461 
462 static int
463 #if defined(__GNUC__)
delete_never(st_data_t key,st_data_t value,st_data_t never)464 delete_never(st_data_t key __attribute__ ((unused)), st_data_t value,
465 	     st_data_t never)
466 #else
467 delete_never(key, value, never)
468     st_data_t key, value, never;
469 #endif
470 {
471     if (value == never) return ST_DELETE;
472     return ST_CONTINUE;
473 }
474 
475 void
st_cleanup_safe(table,never)476 st_cleanup_safe(table, never)
477      st_table *table;
478      st_data_t never;
479 {
480   int num_entries = table->num_entries;
481 
482   st_foreach(table, delete_never, never);
483   table->num_entries = num_entries;
484 }
485 
486 int
st_foreach(table,func,arg)487 st_foreach(table, func, arg)
488      st_table *table;
489      int (*func)();
490      st_data_t arg;
491 {
492   st_table_entry *ptr, *last, *tmp;
493   enum st_retval retval;
494   int i;
495 
496   for(i = 0; i < table->num_bins; i++) {
497     last = 0;
498     for(ptr = table->bins[i]; ptr != 0;) {
499 	    retval = (*func)(ptr->key, ptr->record, arg);
500 	    switch (retval) {
501 	    case ST_CHECK:	/* check if hash is modified during iteration */
502         tmp = 0;
503         if (i < table->num_bins) {
504           for (tmp = table->bins[i]; tmp; tmp=tmp->next) {
505             if (tmp == ptr) break;
506           }
507         }
508         if (!tmp) {
509           /* call func with error notice */
510           return 1;
511         }
512         /* fall through */
513 	    case ST_CONTINUE:
514         last = ptr;
515         ptr = ptr->next;
516         break;
517 	    case ST_STOP:
518         return 0;
519 	    case ST_DELETE:
520         tmp = ptr;
521         if (last == 0) {
522           table->bins[i] = ptr->next;
523         }
524         else {
525           last->next = ptr->next;
526         }
527         ptr = ptr->next;
528         free(tmp);
529         table->num_entries--;
530 	    }
531     }
532   }
533   return 0;
534 }
535 
536 static int
strhash(string)537 strhash(string)
538      register const char *string;
539 {
540   register int c;
541 
542 #ifdef HASH_ELFHASH
543   register unsigned int h = 0, g;
544 
545   while ((c = *string++) != '\0') {
546     h = ( h << 4 ) + c;
547     if ( g = h & 0xF0000000 )
548 	    h ^= g >> 24;
549     h &= ~g;
550   }
551   return h;
552 #elif HASH_PERL
553   register int val = 0;
554 
555   while ((c = *string++) != '\0') {
556     val += c;
557     val += (val << 10);
558     val ^= (val >> 6);
559   }
560   val += (val << 3);
561   val ^= (val >> 11);
562 
563   return val + (val << 15);
564 #else
565   register int val = 0;
566 
567   while ((c = *string++) != '\0') {
568     val = val*997 + c;
569   }
570 
571   return val + (val>>5);
572 #endif
573 }
574 
575 static int
numcmp(x,y)576 numcmp(x, y)
577      long x, y;
578 {
579   return x != y;
580 }
581 
582 static int
numhash(n)583 numhash(n)
584      long n;
585 {
586   return n;
587 }
588