/*
* Copyright © 2009 - 2012 Intel Corporation
* Copyright © 1988 - 2004 Keith Packard and Bart Massey .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING
* FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE .
*
* Except as contained in this notice , the names of the authors
* or their institutions shall not be used in advertising or
* otherwise to promote the sale , use or other dealings in this
* Software without prior written authorization from the
* authors .
*
* Authors :
* Eric Anholt < eric @ anholt . net >
* Keith Packard < keithp @ keithp . com >
*/
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "hash_table.h"
#include "macros.h"
#include "ralloc.h"
#include "set.h"
#include "fast_urem_by_const.h"
/*
* From Knuth - - a good choice for hash / rehash values is p , p - 2 where
* p and p - 2 are both prime . These tables are sized to have an extra 10 %
* free to avoid exponential performance degradation as the hash table fills
*/
static const uint32_t deleted_key_value;
static const void *deleted_key = &deleted_key_value;
static const struct {
uint32_t max_entries, size, rehash;
uint64_t size_magic, rehash_magic;
} hash_sizes[] = {
#define ENTRY(max_entries, size, rehash) \
{ max_entries, size, rehash, \
REMAINDER_MAGIC(size), REMAINDER_MAGIC(rehash) }
ENTRY(2 , 5 , 3 ),
ENTRY(4 , 7 , 5 ),
ENTRY(8 , 13 , 11 ),
ENTRY(16 , 19 , 17 ),
ENTRY(32 , 43 , 41 ),
ENTRY(64 , 73 , 71 ),
ENTRY(128 , 151 , 149 ),
ENTRY(256 , 283 , 281 ),
ENTRY(512 , 571 , 569 ),
ENTRY(1024 , 1153 , 1151 ),
ENTRY(2048 , 2269 , 2267 ),
ENTRY(4096 , 4519 , 4517 ),
ENTRY(8192 , 9013 , 9011 ),
ENTRY(16384 , 18043 , 18041 ),
ENTRY(32768 , 36109 , 36107 ),
ENTRY(65536 , 72091 , 72089 ),
ENTRY(131072 , 144409 , 144407 ),
ENTRY(262144 , 288361 , 288359 ),
ENTRY(524288 , 576883 , 576881 ),
ENTRY(1048576 , 1153459 , 1153457 ),
ENTRY(2097152 , 2307163 , 2307161 ),
ENTRY(4194304 , 4613893 , 4613891 ),
ENTRY(8388608 , 9227641 , 9227639 ),
ENTRY(16777216 , 18455029 , 18455027 ),
ENTRY(33554432 , 36911011 , 36911009 ),
ENTRY(67108864 , 73819861 , 73819859 ),
ENTRY(134217728 , 147639589 , 147639587 ),
ENTRY(268435456 , 295279081 , 295279079 ),
ENTRY(536870912 , 590559793 , 590559791 ),
ENTRY(1073741824 , 1181116273 , 1181116271 ),
ENTRY(2147483648 ul, 2362232233 ul, 2362232231 ul )
};
ASSERTED static inline bool
key_pointer_is_reserved(const void *key)
{
return key == NULL || key == deleted_key;
}
static int
entry_is_free(struct set_entry *entry)
{
return entry->key == NULL;
}
static int
entry_is_deleted(struct set_entry *entry)
{
return entry->key == deleted_key;
}
static int
entry_is_present(struct set_entry *entry)
{
return entry->key != NULL && entry->key != deleted_key;
}
struct set *
_mesa_set_create(void *mem_ctx,
uint32_t (*key_hash_function)(const void *key),
bool (*key_equals_function)(const void *a,
const void *b))
{
struct set *ht;
ht = ralloc(mem_ctx, struct set);
if (ht == NULL)
return NULL;
ht->size_index = 0 ;
ht->size = hash_sizes[ht->size_index].size;
ht->rehash = hash_sizes[ht->size_index].rehash;
ht->size_magic = hash_sizes[ht->size_index].size_magic;
ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic;
ht->max_entries = hash_sizes[ht->size_index].max_entries;
ht->key_hash_function = key_hash_function;
ht->key_equals_function = key_equals_function;
ht->table = rzalloc_array(ht, struct set_entry, ht->size);
ht->entries = 0 ;
ht->deleted_entries = 0 ;
if (ht->table == NULL) {
ralloc_free(ht);
return NULL;
}
return ht;
}
struct set *
_mesa_set_clone(struct set *set, void *dst_mem_ctx)
{
struct set *clone;
clone = ralloc(dst_mem_ctx, struct set);
if (clone == NULL)
return NULL;
memcpy(clone, set, sizeof (struct set));
clone->table = ralloc_array(clone, struct set_entry, clone->size);
if (clone->table == NULL) {
ralloc_free(clone);
return NULL;
}
memcpy(clone->table, set->table, clone->size * sizeof (struct set_entry));
return clone;
}
/**
* Frees the given set .
*
* If delete_function is passed , it gets called on each entry present before
* freeing .
*/
void
_mesa_set_destroy(struct set *ht, void (*delete_function)(struct set_entry *entry))
{
if (!ht)
return ;
if (delete_function) {
set_foreach (ht, entry) {
delete_function(entry);
}
}
ralloc_free(ht->table);
ralloc_free(ht);
}
/**
* Clears all values from the given set .
*
* If delete_function is passed , it gets called on each entry present before
* the set is cleared .
*/
void
_mesa_set_clear(struct set *set, void (*delete_function)(struct set_entry *entry))
{
if (!set)
return ;
set_foreach (set, entry) {
if (delete_function)
delete_function(entry);
entry->key = deleted_key;
}
set->entries = set->deleted_entries = 0 ;
}
/**
* Finds a set entry with the given key and hash of that key .
*
* Returns NULL if no entry is found .
*/
static struct set_entry *
set_search(const struct set *ht, uint32_t hash, const void *key)
{
assert(!key_pointer_is_reserved(key));
uint32_t size = ht->size;
uint32_t start_address = util_fast_urem32(hash, size, ht->size_magic);
uint32_t double_hash = util_fast_urem32(hash, ht->rehash,
ht->rehash_magic) + 1 ;
uint32_t hash_address = start_address;
do {
struct set_entry *entry = ht->table + hash_address;
if (entry_is_free(entry)) {
return NULL;
} else if (entry_is_present(entry) && entry->hash == hash) {
if (ht->key_equals_function(key, entry->key)) {
return entry;
}
}
hash_address += double_hash;
if (hash_address >= size)
hash_address -= size;
} while (hash_address != start_address);
return NULL;
}
struct set_entry *
_mesa_set_search(const struct set *set, const void *key)
{
assert(set->key_hash_function);
return set_search(set, set->key_hash_function(key), key);
}
struct set_entry *
_mesa_set_search_pre_hashed(const struct set *set, uint32_t hash,
const void *key)
{
assert(set->key_hash_function == NULL ||
hash == set->key_hash_function(key));
return set_search(set, hash, key);
}
static void
set_add_rehash(struct set *ht, uint32_t hash, const void *key)
{
uint32_t size = ht->size;
uint32_t start_address = util_fast_urem32(hash, size, ht->size_magic);
uint32_t double_hash = util_fast_urem32(hash, ht->rehash,
ht->rehash_magic) + 1 ;
uint32_t hash_address = start_address;
do {
struct set_entry *entry = ht->table + hash_address;
if (likely(entry->key == NULL)) {
entry->hash = hash;
entry->key = key;
return ;
}
hash_address = hash_address + double_hash;
if (hash_address >= size)
hash_address -= size;
} while (true);
}
static void
set_rehash(struct set *ht, unsigned new_size_index)
{
struct set old_ht;
struct set_entry *table;
if (new_size_index >= ARRAY_SIZE(hash_sizes))
return ;
table = rzalloc_array(ht, struct set_entry,
hash_sizes[new_size_index].size);
if (table == NULL)
return ;
old_ht = *ht;
ht->table = table;
ht->size_index = new_size_index;
ht->size = hash_sizes[ht->size_index].size;
ht->rehash = hash_sizes[ht->size_index].rehash;
ht->size_magic = hash_sizes[ht->size_index].size_magic;
ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic;
ht->max_entries = hash_sizes[ht->size_index].max_entries;
ht->entries = 0 ;
ht->deleted_entries = 0 ;
set_foreach(&old_ht, entry) {
set_add_rehash(ht, entry->hash, entry->key);
}
ht->entries = old_ht.entries;
ralloc_free(old_ht.table);
}
void
_mesa_set_resize(struct set *set, uint32_t entries)
{
/* You can't shrink a set below its number of entries */
if (set->entries > entries)
entries = set->entries;
unsigned size_index = 0 ;
while (hash_sizes[size_index].max_entries < entries)
size_index++;
set_rehash(set, size_index);
}
/**
* Find a matching entry for the given key , or insert it if it doesn ' t already
* exist .
*
* Note that insertion may rearrange the table on a resize or rehash ,
* so previously found hash_entries are no longer valid after this function .
*/
static struct set_entry *
set_search_or_add(struct set *ht, uint32_t hash, const void *key, bool *found)
{
struct set_entry *available_entry = NULL;
assert(!key_pointer_is_reserved(key));
if (ht->entries >= ht->max_entries) {
set_rehash(ht, ht->size_index + 1 );
} else if (ht->deleted_entries + ht->entries >= ht->max_entries) {
set_rehash(ht, ht->size_index);
}
uint32_t size = ht->size;
uint32_t start_address = util_fast_urem32(hash, size, ht->size_magic);
uint32_t double_hash = util_fast_urem32(hash, ht->rehash,
ht->rehash_magic) + 1 ;
uint32_t hash_address = start_address;
do {
struct set_entry *entry = ht->table + hash_address;
if (!entry_is_present(entry)) {
/* Stash the first available entry we find */
if (available_entry == NULL)
available_entry = entry;
if (entry_is_free(entry))
break ;
}
if (!entry_is_deleted(entry) &&
entry->hash == hash &&
ht->key_equals_function(key, entry->key)) {
if (found)
*found = true;
return entry;
}
hash_address = hash_address + double_hash;
if (hash_address >= size)
hash_address -= size;
} while (hash_address != start_address);
if (available_entry) {
/* There is no matching entry, create it. */
if (entry_is_deleted(available_entry))
ht->deleted_entries--;
available_entry->hash = hash;
available_entry->key = key;
ht->entries++;
if (found)
*found = false ;
return available_entry;
}
/* We could hit here if a required resize failed. An unchecked-malloc
* application could ignore this result .
*/
return NULL;
}
/**
* Inserts the key with the given hash into the table .
*
* Note that insertion may rearrange the table on a resize or rehash ,
* so previously found hash_entries are no longer valid after this function .
*/
static struct set_entry *
set_add(struct set *ht, uint32_t hash, const void *key)
{
struct set_entry *entry = set_search_or_add(ht, hash, key, NULL);
if (unlikely(!entry))
return NULL;
/* Note: If a matching entry already exists, this will replace it. This is
* a relatively common feature of hash tables , with the alternative
* generally being " insert the new value as well , and return it first when
* the key is searched for " .
*
* Note that the hash table doesn ' t have a delete callback . If freeing of
* old keys is required to avoid memory leaks , use the alternative
* _ mesa_set_search_or_add function and implement the replacement yourself .
*/
entry->key = key;
return entry;
}
struct set_entry *
_mesa_set_add(struct set *set, const void *key)
{
assert(set->key_hash_function);
return set_add(set, set->key_hash_function(key), key);
}
struct set_entry *
_mesa_set_add_pre_hashed(struct set *set, uint32_t hash, const void *key)
{
assert(set->key_hash_function == NULL ||
hash == set->key_hash_function(key));
return set_add(set, hash, key);
}
struct set_entry *
_mesa_set_search_and_add(struct set *set, const void *key, bool *replaced)
{
assert(set->key_hash_function);
return _mesa_set_search_and_add_pre_hashed(set,
set->key_hash_function(key),
key, replaced);
}
struct set_entry *
_mesa_set_search_and_add_pre_hashed(struct set *set, uint32_t hash,
const void *key, bool *replaced)
{
assert(set->key_hash_function == NULL ||
hash == set->key_hash_function(key));
struct set_entry *entry = set_search_or_add(set, hash, key, replaced);
if (unlikely(!entry))
return NULL;
/* This implements the replacement, same as _mesa_set_add(). The user will
* be notified if we ' re overwriting a found entry .
*/
entry->key = key;
return entry;
}
struct set_entry *
_mesa_set_search_or_add(struct set *set, const void *key)
{
assert(set->key_hash_function);
return set_search_or_add(set, set->key_hash_function(key), key, NULL);
}
struct set_entry *
_mesa_set_search_or_add_pre_hashed(struct set *set, uint32_t hash,
const void *key)
{
assert(set->key_hash_function == NULL ||
hash == set->key_hash_function(key));
return set_search_or_add(set, hash, key, NULL);
}
/**
* This function deletes the given hash table entry .
*
* Note that deletion doesn ' t otherwise modify the table , so an iteration over
* the table deleting entries is safe .
*/
void
_mesa_set_remove(struct set *ht, struct set_entry *entry)
{
if (!entry)
return ;
entry->key = deleted_key;
ht->entries--;
ht->deleted_entries++;
}
/**
* Removes the entry with the corresponding key , if exists .
*/
void
_mesa_set_remove_key(struct set *set, const void *key)
{
_mesa_set_remove(set, _mesa_set_search(set, key));
}
/**
* This function is an iterator over the hash table .
*
* Pass in NULL for the first entry , as in the start of a for loop . Note that
* an iteration over the table is O ( table_size ) not O ( entries ) .
*/
struct set_entry *
_mesa_set_next_entry(const struct set *ht, struct set_entry *entry)
{
if (entry == NULL)
entry = ht->table;
else
entry = entry + 1 ;
for (; entry != ht->table + ht->size; entry++) {
if (entry_is_present(entry)) {
return entry;
}
}
return NULL;
}
struct set_entry *
_mesa_set_random_entry(struct set *ht,
int (*predicate)(struct set_entry *entry))
{
struct set_entry *entry;
uint32_t i = rand() % ht->size;
if (ht->entries == 0 )
return NULL;
for (entry = ht->table + i; entry != ht->table + ht->size; entry++) {
if (entry_is_present(entry) &&
(!predicate || predicate(entry))) {
return entry;
}
}
for (entry = ht->table; entry != ht->table + i; entry++) {
if (entry_is_present(entry) &&
(!predicate || predicate(entry))) {
return entry;
}
}
return NULL;
}
/**
* Helper to create a set with pointer keys .
*/
struct set *
_mesa_pointer_set_create(void *mem_ctx)
{
return _mesa_set_create(mem_ctx, _mesa_hash_pointer,
_mesa_key_pointer_equal);
}
Messung V0.5 in Prozent C=90 H=92 G=90
¤ Dauer der Verarbeitung: 0.10 Sekunden
¤
*© Formatika GbR, Deutschland