Release 980913
[wine/multimedia.git] / ipc / generic_hash.c
blob98d4e1728aa89285d275029142b815f6b7b5764c
1 /***************************************************************************
2 * Copyright 1995 Michael Veksler. mveksler@vnet.ibm.com
3 ***************************************************************************
4 * File: generic_hash.c
5 * Purpose : dynamically growing hash, may use shared or local memory.
6 ***************************************************************************
7 */
8 #ifdef CONFIG_IPC
10 #include <sys/types.h>
11 #include <stdlib.h>
12 #include <assert.h>
13 #include "generic_hash.h"
15 #define ROUND_UP4(num) (( (num)+3) & ~3)
17 #define FREE_ENTRY 0
18 #define DELETED_ENTRY ((DWORD)-1)
20 #define NO_OF_PRIMES 512
21 #define GET_ITEM(items,size,i)\
22 (*(HASH_ITEM*) \
23 ( ((char *)(items))+ \
24 (i)*(size)) )
26 static HASH_ITEM *locate_entry(HASH_CONTAINER* hash, DWORD key,
27 HASH_VAL *seeked_data, BOOL32 skip_deleted);
29 static void copy_hash_items(HASH_CONTAINER *hash, HASH_ITEM *old_items,
30 int old_n_items);
32 static BOOL32 arrays_initialized = FALSE;
33 static int primes[NO_OF_PRIMES];
34 static int best_primes[NO_OF_PRIMES];
35 static int no_of_primes;
36 static int no_of_best_primes;
37 static int max_num;
39 /* binary search for `num' in the `primes' array */
40 static BOOL32 prime_binary_search_found(int num)
42 int min_idx, max_idx, idx;
44 min_idx=0;
45 max_idx=no_of_primes-1;
47 while (min_idx <= max_idx) {
48 idx = (max_idx + min_idx) >> 1;
49 if (num == primes[idx])
50 return TRUE;
51 if (num < primes[idx])
52 max_idx = idx-1;
53 else
54 min_idx = idx+1;
56 return FALSE;
59 static BOOL32 is_prime(int num)
61 int i;
62 if ((num & 0x1) == 0) /* can be divided by 2 */
63 if (num == 2)
64 return TRUE;
65 else
66 return FALSE;
68 if (num <= primes[no_of_primes-1])
69 return prime_binary_search_found(num);
71 for (i=0 ; i < no_of_primes ; i++) {
72 if (num % primes[i] == 0)
73 return FALSE;
74 if (num < primes[i] * primes[i])
75 return TRUE;
77 return TRUE;
80 static void setup_primes()
82 int num;
84 primes[0]=2;
85 primes[1]=3;
86 no_of_primes=2;
88 /* count in modulo 6 to avoid numbers that divide by 2 or 3 */
89 for (num=5 ; ; num+=6) {
90 if (is_prime(num)) {
91 primes[no_of_primes++]=num;
92 if (no_of_primes >= NO_OF_PRIMES)
93 break;
95 if (is_prime(num+2)) {
96 primes[no_of_primes++]=num+2;
97 if (no_of_primes >= NO_OF_PRIMES)
98 break;
101 max_num= primes[no_of_primes-1] * primes[no_of_primes-1];
105 /* Find primes which are far "enough" from powers of two */
107 void setup_best_primes()
109 int i;
110 int num;
111 int pow2before, pow2after;
112 int min_range, max_range;
114 min_range=3;
115 max_range=3;
116 pow2before= 2;
117 pow2after= 4;
119 no_of_best_primes= 0;
120 for (i=0 ; i < no_of_primes ; i++){
121 num= primes[i];
123 if (num > pow2after) {
124 pow2before= pow2after;
125 pow2after <<=1;
126 min_range= pow2before+ (pow2before >> 3);
127 max_range= pow2after- (pow2before >> 2);
129 if (num > min_range && num < max_range)
130 best_primes[no_of_best_primes++]=num;
134 /* binary search for `num' in the `best_primes' array,
135 * Return smallest best_prime >= num.
137 static int best_prime_binary_search(int num)
139 int min_idx, max_idx, idx;
141 min_idx=0;
142 max_idx=no_of_best_primes-1;
144 while (1) {
145 idx = (max_idx + min_idx) >> 1;
146 if (num == best_primes[idx])
147 return num;
148 if (num < best_primes[idx]) {
149 max_idx = idx-1;
150 if (max_idx <= min_idx)
151 return best_primes[idx];
153 else {
154 min_idx = idx+1;
155 if (min_idx >= max_idx)
156 return best_primes[max_idx];
162 /* Find the best prime, near `num' (which can be any number) */
163 static int best_prime(int num)
165 int log2;
166 int pow2less, pow2more;
167 int min_range, max_range;
169 if (num < 11)
170 return 11;
172 if (num <= best_primes[no_of_best_primes-1])
173 return best_prime_binary_search(num);
175 assert( num < max_num );
177 for (log2=0 ; num >> log2 ; log2++)
180 pow2less= 1 << log2;
181 pow2more= 1 << (log2+1);
182 min_range= pow2less + (pow2less >> 3);
183 max_range= pow2more - (pow2more >> 3);
185 if (num < min_range)
186 num= min_range;
188 num |= 1; /* make sure num can't be divided by 2 */
190 while (1) {
191 if (num >= max_range) {
192 pow2less<<= 1;
193 pow2more<<= 1;
194 min_range= pow2less + (pow2less >> 3);
195 max_range= pow2more - (pow2more >> 3);
196 num= min_range | 1; /* make sure num can't be divided by 2 */
198 /* num should be here in the range: (min_range, max_range) */
199 if (is_prime(num))
200 return num;
201 num+=2;
205 /* FIXME: This can be done before compiling. (uning a script)*/
206 static void setup_arrays()
208 setup_primes();
209 setup_best_primes();
212 /* Discard all DELETED_ENTRYs moving the data to it's correct location.
213 * Done without a temporary buffer.
214 * May require some efficiency improvements ( currently it's o(N^2)
215 * or is it o(N^3) in the worst case ? In the avarege it seems to be
216 * something like o(N log (N)))
218 static void static_collect_garbge(HASH_CONTAINER *hash)
220 int i;
221 BOOL32 change;
222 HASH_ITEM *items;
223 HASH_ITEM *located;
224 HASH_ITEM *item;
225 int key;
227 items= hash->items;
229 do {
230 change= FALSE;
231 for (i=hash->shared->total_items-1 ; i >= 0 ; i--) {
232 item= &GET_ITEM(items,hash->bytes_per_item,i);
233 key= item->key;
234 if (key != DELETED_ENTRY && key != FREE_ENTRY) {
235 /* try to place the entry in a deleted location */
236 located= locate_entry(hash, key, &item->data,
237 0 /* no skip_deleted */);
238 if (located->key == DELETED_ENTRY) {
239 change= TRUE;
240 memcpy(&located, &item,
241 hash->bytes_per_item);
242 item->key= DELETED_ENTRY;
246 } while (change);
248 /* No change means that there is no need to go through a DELETED_ENTRY
249 * in order to reach an item, so DELETED_ENTRY looses it's special
250 * meaning, and it is the same as FREE_ENTRY.
252 for (i=hash->shared->total_items-1 ; i >= 0 ; i--)
253 if (GET_ITEM(items,hash->bytes_per_item,i).key == DELETED_ENTRY)
254 GET_ITEM(items,hash->bytes_per_item,i).key = FREE_ENTRY;
255 hash->shared->deleted_items=0;
258 static void collect_garbge(HASH_CONTAINER *hash)
260 HASH_SHARED *shared= hash->shared;
261 HASH_ITEM *temp_items;
262 int size;
264 size= shared->total_items * hash->bytes_per_item;
265 temp_items= (HASH_ITEM*)malloc(size);
266 if (temp_items==NULL) {
267 static_collect_garbge(hash);
268 } else {
269 memcpy(temp_items, hash->items, size);
270 copy_hash_items(hash, temp_items, shared->total_items);
275 static void copy_hash_items(HASH_CONTAINER *hash, HASH_ITEM *old_items,
276 int old_n_items)
278 HASH_SHARED *shared= hash->shared;
279 HASH_ITEM *item;
280 int i;
282 shared->deleted_items = 0;
283 shared->free_items= shared->total_items;
285 /* make all items free */
286 for (i= shared->total_items-1 ; i>=0 ; i--)
287 GET_ITEM(hash->items, hash->bytes_per_item, i).key = FREE_ENTRY;
289 /* copy items */
290 for (i=0 ; i <= old_n_items; i++) {
291 item= &GET_ITEM(old_items, hash->bytes_per_item,i);
292 if (item->key != FREE_ENTRY && item->key != DELETED_ENTRY)
293 hash_add_item(hash, item->key, &item->data);
298 static void reorder_hash(HASH_CONTAINER *hash)
300 HASH_SHARED *shared= hash->shared;
301 HASH_ITEM *items, *old_items;
302 HASH_PTR shared_items, old_shared_items;
303 int n_items, old_n_items;
304 int size;
306 if (shared->deleted_items > hash->min_free_items) {
307 collect_garbge(hash);
308 return;
310 n_items= best_prime(shared->total_items * HASH_REALLOC_JUMPS);
312 size= n_items *
313 (sizeof(items[0]) - sizeof(items[0].data) + hash->bytes_per_item);
315 shared_items= hash->allocate_mem(size);
316 items= hash->access_mem(shared_items);
318 if (items == NULL) {
319 collect_garbge(hash);
320 return;
322 old_shared_items = shared->items;
323 old_n_items= shared->total_items;
324 old_items= hash->items;
326 /* setup a new clean hash based on the parameters of the original one */
327 hash->items= items;
328 shared->total_items = n_items;
329 shared->items= shared_items;
330 set_hash_parameters(hash, hash->maximum_load);
331 copy_hash_items(hash, old_items, old_n_items);
333 hash->free_mem(old_shared_items);
334 hash->last_ptr_update= ++shared->ptr_updates;
337 /* low level: attach hash existing hash items, no checks are performed
338 * No complex calculations done.
340 static HASH_CONTAINER *attach_no_check(HASH_ITEM *items, int bytes_per_datum)
342 HASH_CONTAINER *hash;
343 int bytes_per_item;
344 HASH_ITEM dummy_item;
346 hash= (HASH_CONTAINER*) malloc(sizeof(HASH_CONTAINER) );
347 if (hash == NULL)
348 return NULL;
350 bytes_per_item= bytes_per_datum+
351 sizeof(dummy_item)-sizeof(dummy_item.data);
352 hash->bytes_per_item= ROUND_UP4(bytes_per_item);
353 hash->items= items;
354 hash->is_correct_item= NULL;
355 hash->allocate_mem= HASH_MEM_ALLOC;
356 hash->access_mem= HASH_MEM_ACCESS;
357 hash->free_mem= HASH_MEM_FREE;
358 set_hash_parameters(hash, HASH_LOAD);
361 return hash;
365 /* Attach existing & running remote (i.e. shared) hash.
366 * Attach the items using the data stored in "shared"
368 HASH_CONTAINER *attach_remote_hash(HASH_SHARED *shared, int bytes_per_datum,
369 HASH_ITEM *(*access_mem)(HASH_PTR))
371 HASH_CONTAINER *hash;
372 HASH_ITEM *items;
374 assert(access_mem != NULL);
375 if (! arrays_initialized)
376 setup_arrays();
378 items=access_mem(shared->items);
379 hash= attach_no_check(items, bytes_per_datum);
380 if (hash == NULL)
381 return NULL;
383 hash->shared_was_malloced = FALSE;
384 hash->shared= shared;
385 return (hash);
389 HASH_CONTAINER *create_remote_hash(HASH_SHARED *shared,
390 int bytes_per_datum,
391 int total_items,
392 HASH_PTR (*allocate_mem)(int size),
393 HASH_ITEM *(*access_mem)(HASH_PTR))
395 HASH_CONTAINER *hash;
396 int size;
397 int i;
399 assert(total_items >= 1);
400 assert(bytes_per_datum >=1);
401 assert(access_mem != NULL);
402 assert(allocate_mem != NULL);
403 assert(shared != NULL);
404 if (! arrays_initialized)
405 setup_arrays();
407 if (total_items < MIN_HASH)
408 total_items= MIN_HASH;
409 else
410 total_items= best_prime(total_items);
412 hash= attach_no_check(NULL, bytes_per_datum);
414 if (hash==NULL) {
415 free(hash);
416 return NULL;
419 shared->total_items= total_items;
420 hash->shared= shared;
421 hash->shared_was_malloced = FALSE;
423 size= total_items * hash->bytes_per_item;
425 shared->items = allocate_mem(size);
426 hash->items= access_mem(shared->items);
428 if (hash->items == NULL ) {
429 free(hash);
430 return NULL;
432 shared->items.ptr= hash->items;
434 /* make all items free */
435 for (i=0 ; i < total_items ; i++)
436 GET_ITEM(hash->items,hash->bytes_per_item,i).key = FREE_ENTRY;
438 shared->deleted_items= 0;
439 shared->free_items= total_items;
440 shared->ptr_updates= 0;
441 return hash;
445 /* hash constructor: create brand new hash */
446 HASH_CONTAINER *create_hash(int bytes_per_datum, int total_items)
448 HASH_CONTAINER *hash;
449 HASH_SHARED *shared;
452 shared= (HASH_SHARED*)malloc(sizeof(HASH_SHARED));
453 if (shared == NULL)
454 return NULL;
456 hash= create_remote_hash(shared, bytes_per_datum, total_items,
457 HASH_MEM_ALLOC, HASH_MEM_ACCESS);
459 if (hash == NULL) {
460 free(shared);
461 return NULL;
464 hash->shared_was_malloced = TRUE;
465 return hash;
468 /* set the extra handlers to non default values */
469 void set_hash_handlers(HASH_CONTAINER *hash,
470 HASH_ITEM_TEST *is_correct_item,
471 HASH_PTR (*allocate_mem)(int size),
472 void (*free_mem)(HASH_PTR),
473 HASH_ITEM *(*access_mem)(HASH_PTR))
475 assert(hash);
476 assert(allocate_mem);
477 assert(free_mem);
479 hash->free_mem = free_mem;
480 hash->allocate_mem = allocate_mem;
481 hash->access_mem = access_mem;
482 hash->is_correct_item = is_correct_item;
486 /* set extra parameters */
487 void set_hash_parameters(HASH_CONTAINER *hash, int load)
489 assert(hash);
490 assert(load>30); /* no sence to realloc with less than */
491 /* 50% load, limiting to 30% to be on */
492 /* the safe size */
493 assert(load<=100);
495 hash->maximum_load= load;
496 hash->min_free_items= (1.0 - load/100.0) * hash->shared->total_items + 1 ;
499 /* hash destructor: destroy anything related to the hash */
500 void destroy_hash(HASH_CONTAINER *hash)
502 assert(hash);
503 hash->free_mem(hash->shared->items);
504 if (hash->shared_was_malloced)
505 free(hash->shared);
506 free(hash);
509 /* hash destructor: just detach hash, without destroing it (makes */
510 /* sence in shared memory environment) */
511 void detach_hash(HASH_CONTAINER *hash)
513 assert(hash);
514 free(hash);
518 /********** Hash usage *************/
519 static __inline__ BOOL32
520 correct_entry(HASH_ITEM *item, int key, HASH_VAL *seeked_data,
521 HASH_ITEM_TEST *is_correct_item, BOOL32 skip_deleted)
523 switch(item->key) {
524 case FREE_ENTRY:
525 return TRUE;
527 case DELETED_ENTRY:
528 return skip_deleted ? FALSE : TRUE;
530 default:
531 if (item->key != key)
532 return FALSE;
533 if (is_correct_item != NULL)
534 return is_correct_item(&item->data, seeked_data);
535 else
536 return TRUE;
541 /* The algorithm of the hash (one of the 2 standard hash implementations):
542 * Iterate through the hash table until
543 * 1. The entry has been found.
544 * 2. A FREE entry has been found.
545 * 3. For insert operations only- A DELETED entry has been found.
546 * The difference between DELETED and FREE entires is that
547 * DELETED entry was one occupied, while FREE was never allocated.
548 * The idea behind this structure to keep other entries reachable.
551 static HASH_ITEM *locate_entry(HASH_CONTAINER* hash, DWORD key,
552 HASH_VAL *seeked_data, BOOL32 skip_deleted)
554 DWORD hash_idx, hash_leaps;
555 HASH_ITEM *item;
556 int i;
557 int total_items;
559 assert(hash);
561 total_items= hash->shared->total_items;
562 hash_idx= key % total_items;
564 item= &GET_ITEM(hash->items, hash->bytes_per_item, hash_idx);
566 if ( correct_entry( item, key, seeked_data,
567 hash->is_correct_item, skip_deleted) )
568 return item;
570 /* get the WORDs in different order in this DWORD to avoid clustering */
571 hash_leaps=((DWORD)MAKELONG(HIWORD(key), LOWORD(key))
572 % (total_items-1)) +1;
574 /* interate through the hash table using hash_leaps */
575 for (i= total_items ; i ; i--) {
576 hash_idx+= hash_leaps;
577 if (hash_idx > total_items)
578 hash_idx -= total_items;
580 item= &GET_ITEM(hash->items,hash->bytes_per_item, hash_idx);
581 if ( correct_entry( item, key, seeked_data,
582 hash->is_correct_item, skip_deleted) )
583 return item;
585 return NULL;
589 static __inline__ void sync_shared_hash(HASH_CONTAINER *hash)
591 HASH_SHARED *shared= hash->shared;
593 if (shared->ptr_updates == hash->last_ptr_update)
594 return;
596 assert(shared->ptr_updates >= hash->last_ptr_update);
597 hash->last_ptr_update= shared->ptr_updates;
598 hash->min_free_items= (1.0 - hash->maximum_load/100.0) *
599 shared->total_items + 1 ;
601 hash->items= hash->access_mem(shared->items);
604 HASH_VAL *hash_locate_item(HASH_CONTAINER* hash,
605 int key, HASH_VAL *seeked_data)
607 HASH_ITEM *item;
609 assert(hash != NULL);
610 sync_shared_hash(hash);
612 item= locate_entry(hash, key, seeked_data, 1 /* skip_deleted */);
613 if (item == NULL)
614 return NULL;
615 if (item->key == FREE_ENTRY )
616 return NULL;
618 return &item->data;
622 BOOL32 hash_add_item(HASH_CONTAINER* hash, int key, HASH_VAL *data)
624 HASH_SHARED *shared;
625 HASH_ITEM *item;
627 assert(hash != NULL);
629 sync_shared_hash(hash);
630 shared= hash->shared;
632 item=locate_entry(hash, key, data, 0 /* no skip_deleted */);
633 assert(item != NULL);
634 if (item->key == key)
635 return FALSE;
636 if (item->key == FREE_ENTRY)
637 shared->free_items--;
638 else
639 shared->deleted_items--;
641 item->key= key;
642 memcpy(&item->data, data, hash->bytes_per_item-sizeof(key));
644 if (shared->free_items < hash->min_free_items ||
645 shared->deleted_items > hash->min_free_items)
646 reorder_hash(hash);
648 return TRUE;
652 BOOL32 hash_delete_item(HASH_CONTAINER* hash, int key, HASH_VAL *seeked_data)
654 HASH_ITEM *item;
656 assert(hash != NULL);
657 sync_shared_hash(hash);
659 item=locate_entry(hash, key, seeked_data, 1 /* skip_deleted */);
660 if (item == NULL)
661 return FALSE;
663 if (item->key == FREE_ENTRY)
664 return FALSE;
666 item->key = DELETED_ENTRY;
667 hash->shared->deleted_items++;
669 return TRUE;
672 void *ret_null()
674 return NULL;
678 HASH_ITEM *access_local_hash(HASH_PTR ptr)
680 return ptr.ptr;
683 #endif /* CONFIG_IPC */