1 /***************************************************************************
2 * Copyright 1995 Michael Veksler. mveksler@vnet.ibm.com
3 ***************************************************************************
5 * Purpose : dynamically growing hash, may use shared or local memory.
6 ***************************************************************************
10 #include <sys/types.h>
13 #include "generic_hash.h"
15 #define ROUND_UP4(num) (( (num)+3) & ~3)
18 #define DELETED_ENTRY ((DWORD)-1)
20 #define NO_OF_PRIMES 512
21 #define GET_ITEM(items,size,i)\
23 ( ((char *)(items))+ \
26 static HASH_ITEM
*locate_entry(HASH_CONTAINER
* hash
, DWORD key
,
27 HASH_VAL
*seeked_data
, BOOL skip_deleted
);
29 static void copy_hash_items(HASH_CONTAINER
*hash
, HASH_ITEM
*old_items
,
32 static BOOL 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
;
39 /* binary search for `num' in the `primes' array */
40 static BOOL
prime_binary_search_found(int num
)
42 int min_idx
, max_idx
, idx
;
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
])
51 if (num
< primes
[idx
])
59 static BOOL
is_prime(int num
)
62 if ((num
& 0x1) == 0) /* can be divided by 2 */
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)
74 if (num
< primes
[i
] * primes
[i
])
80 static void setup_primes()
88 /* count in modulo 6 to avoid numbers that divide by 2 or 3 */
89 for (num
=5 ; ; num
+=6) {
91 primes
[no_of_primes
++]=num
;
92 if (no_of_primes
>= NO_OF_PRIMES
)
95 if (is_prime(num
+2)) {
96 primes
[no_of_primes
++]=num
+2;
97 if (no_of_primes
>= NO_OF_PRIMES
)
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()
111 int pow2before
, pow2after
;
112 int min_range
, max_range
;
119 no_of_best_primes
= 0;
120 for (i
=0 ; i
< no_of_primes
; i
++){
123 if (num
> pow2after
) {
124 pow2before
= pow2after
;
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
;
142 max_idx
=no_of_best_primes
-1;
145 idx
= (max_idx
+ min_idx
) >> 1;
146 if (num
== best_primes
[idx
])
148 if (num
< best_primes
[idx
]) {
150 if (max_idx
<= min_idx
)
151 return best_primes
[idx
];
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
)
166 int pow2less
, pow2more
;
167 int min_range
, max_range
;
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
++)
181 pow2more
= 1 << (log2
+1);
182 min_range
= pow2less
+ (pow2less
>> 3);
183 max_range
= pow2more
- (pow2more
>> 3);
188 num
|= 1; /* make sure num can't be divided by 2 */
191 if (num
>= max_range
) {
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) */
205 /* FIXME: This can be done before compiling. (uning a script)*/
206 static void setup_arrays()
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
)
231 for (i
=hash
->shared
->total_items
-1 ; i
>= 0 ; i
--) {
232 item
= &GET_ITEM(items
,hash
->bytes_per_item
,i
);
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
) {
240 memcpy(&located
, &item
,
241 hash
->bytes_per_item
);
242 item
->key
= DELETED_ENTRY
;
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
;
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
);
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
,
278 HASH_SHARED
*shared
= hash
->shared
;
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
;
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
;
306 if (shared
->deleted_items
> hash
->min_free_items
) {
307 collect_garbge(hash
);
310 n_items
= best_prime(shared
->total_items
* HASH_REALLOC_JUMPS
);
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
);
319 collect_garbge(hash
);
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 */
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
;
344 HASH_ITEM dummy_item
;
346 hash
= (HASH_CONTAINER
*) malloc(sizeof(HASH_CONTAINER
) );
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
);
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
);
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
;
374 assert(access_mem
!= NULL
);
375 if (! arrays_initialized
)
378 items
=access_mem(shared
->items
);
379 hash
= attach_no_check(items
, bytes_per_datum
);
383 hash
->shared_was_malloced
= FALSE
;
384 hash
->shared
= shared
;
389 HASH_CONTAINER
*create_remote_hash(HASH_SHARED
*shared
,
392 HASH_PTR (*allocate_mem
)(int size
),
393 HASH_ITEM
*(*access_mem
)(HASH_PTR
))
395 HASH_CONTAINER
*hash
;
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
)
407 if (total_items
< MIN_HASH
)
408 total_items
= MIN_HASH
;
410 total_items
= best_prime(total_items
);
412 hash
= attach_no_check(NULL
, bytes_per_datum
);
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
) {
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;
445 /* hash constructor: create brand new hash */
446 HASH_CONTAINER
*create_hash(int bytes_per_datum
, int total_items
)
448 HASH_CONTAINER
*hash
;
452 shared
= (HASH_SHARED
*)malloc(sizeof(HASH_SHARED
));
456 hash
= create_remote_hash(shared
, bytes_per_datum
, total_items
,
457 HASH_MEM_ALLOC
, HASH_MEM_ACCESS
);
464 hash
->shared_was_malloced
= TRUE
;
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
))
476 assert(allocate_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
)
490 assert(load
>30); /* no sence to realloc with less than */
491 /* 50% load, limiting to 30% to be on */
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
)
503 hash
->free_mem(hash
->shared
->items
);
504 if (hash
->shared_was_malloced
)
509 /* hash destructor: just detach hash, without destroing it (makes */
510 /* sence in shared memory environment) */
511 void detach_hash(HASH_CONTAINER
*hash
)
518 /********** Hash usage *************/
519 static __inline__ BOOL
520 correct_entry(HASH_ITEM
*item
, int key
, HASH_VAL
*seeked_data
,
521 HASH_ITEM_TEST
*is_correct_item
, BOOL skip_deleted
)
528 return skip_deleted
? FALSE
: TRUE
;
531 if (item
->key
!= key
)
533 if (is_correct_item
!= NULL
)
534 return is_correct_item(&item
->data
, seeked_data
);
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
, BOOL skip_deleted
)
554 DWORD hash_idx
, hash_leaps
;
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
) )
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
) )
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
)
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
)
609 assert(hash
!= NULL
);
610 sync_shared_hash(hash
);
612 item
= locate_entry(hash
, key
, seeked_data
, 1 /* skip_deleted */);
615 if (item
->key
== FREE_ENTRY
)
622 BOOL
hash_add_item(HASH_CONTAINER
* hash
, int key
, HASH_VAL
*data
)
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
)
636 if (item
->key
== FREE_ENTRY
)
637 shared
->free_items
--;
639 shared
->deleted_items
--;
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
)
652 BOOL
hash_delete_item(HASH_CONTAINER
* hash
, int key
, HASH_VAL
*seeked_data
)
656 assert(hash
!= NULL
);
657 sync_shared_hash(hash
);
659 item
=locate_entry(hash
, key
, seeked_data
, 1 /* skip_deleted */);
663 if (item
->key
== FREE_ENTRY
)
666 item
->key
= DELETED_ENTRY
;
667 hash
->shared
->deleted_items
++;
678 HASH_ITEM
*access_local_hash(HASH_PTR ptr
)
683 #endif /* CONFIG_IPC */