darling-libobjc2/hash_table.h
theraven bdf97cf64e Added hopscotch table implementation and rewrote the class table to use it. This is done so that the class table and protocol tables can share the same code (currently there is no protocol table).
Note that concurrent resizing has not yet been implemented.  That means that there is a hard limit on the number of classes that can be loaded.  This is currently set to quite a small number for testing, to stress the hash table.  If you're experiencing problems as a result, then please let me know and I will increase it.
2010-01-07 18:22:13 +00:00

229 lines
6.1 KiB
C

/**
* hash_table.h provides a template for implementing hopscotch hash tables.
*
* Several macros must be defined before including this file:
*
* MAP_TABLE_NAME defines the name of the table. All of the operations and
* types related to this table will be prefixed with this value.
*
* MAP_TABLE_COMPARE_FUNCTION defines the function used for testing a key
* against a value in the table for equality. This must take two void*
* arguments. The first is the key and the second is the value.
*
* MAP_TABLE_HASH_KEY and MAP_TABLE_HASH_VALUE define a pair of functions that
* takes a key and a value pointer respectively as their argument and returns
* an int32_t representing the hash.
*
* Optionally, MAP_TABLE_STATIC_SIZE may be defined, to define a table type
* which has a static size.
*/
#ifndef MAP_TABLE_NAME
# error You must define MAP_TABLE_NAME.
#endif
#ifndef MAP_TABLE_COMPARE_FUNCTION
# error You must define MAP_TABLE_COMPARE_FUNCTION.
#endif
#ifndef MAP_TABLE_HASH_KEY
# error You must define MAP_TABLE_HASH_KEY
#endif
#ifndef MAP_TABLE_HASH_VALUE
# error You must define MAP_TABLE_HASH_VALUE
#endif
// Horrible multiple indirection to satisfy the weird precedence rules in cpp
#define REALLY_PREFIX_SUFFIX(x,y) x ## y
#define PREFIX_SUFFIX(x, y) REALLY_PREFIX_SUFFIX(x, y)
/**
* PREFIX(x) macro adds the table name prefix to the argument.
*/
#define PREFIX(x) PREFIX_SUFFIX(MAP_TABLE_NAME, x)
typedef struct PREFIX(_table_cell_struct)
{
uint32_t secondMaps;
void *value;
} *PREFIX(_table_cell);
#ifdef MAP_TABLE_STATIC_SIZE
typedef struct
{
unsigned int table_used;
struct PREFIX(_table_cell_struct) table[MAP_TABLE_STATIC_SIZE];
} PREFIX(_table);
# define TABLE_SIZE(x) MAP_TABLE_STATIC_SIZE
#else
typedef struct
{
unsigned int table_size;
unsigned int table_used;
struct PREFIX(_table_cell_struct) table[1];
} PREFIX(_table);
# define TABLE_SIZE(x) (x->table_size)
#endif
struct PREFIX(_table_enumerator)
{
unsigned int seen;
unsigned int index;
};
static inline PREFIX(_table_cell) PREFIX(_table_lookup)(PREFIX(_table) *table,
uint32_t hash)
{
hash = hash % TABLE_SIZE(table);
return &table->table[hash];
}
static int PREFIX(_table_move_gap)(PREFIX(_table) *table, uint32_t fromHash,
uint32_t toHash, PREFIX(_table_cell) emptyCell)
{
for (uint32_t hash = fromHash - 32 ; hash < fromHash ; hash++)
{
// Get the cell n before the hash.
PREFIX(_table_cell) cell = PREFIX(_table_lookup)(table, hash);
// If this node is a primary entry move it down
if (MAP_TABLE_HASH_VALUE(cell->value) == hash)
{
emptyCell->value = cell->value;
cell->secondMaps |= (1 << ((fromHash - hash) - 1));
cell->value = NULL;
if (hash - toHash < 32)
{
return 1;
}
return PREFIX(_table_move_gap)(table, hash, toHash, cell);
}
int hop = __builtin_ffs(cell->secondMaps);
if (hop > 0 && (hash + hop) < fromHash)
{
PREFIX(_table_cell) hopCell = PREFIX(_table_lookup)(table, hash+hop);
emptyCell->value = hopCell->value;
// Update the hop bit for the new offset
cell->secondMaps |= (1 << ((fromHash - hash) - 1));
// Clear the hop bit in the original cell
cell->secondMaps &= ~(1 << (hop - 1));
hopCell->value = NULL;
if (hash - toHash < 32)
{
return 1;
}
return PREFIX(_table_move_gap)(table, hash + hop, toHash, hopCell);
}
}
return 0;
}
static int PREFIX(_table_rebalance)(PREFIX(_table) *table, uint32_t hash)
{
for (unsigned i=32 ; i<TABLE_SIZE(table) ; i++)
{
PREFIX(_table_cell) cell = PREFIX(_table_lookup)(table, hash + i);
if (NULL == cell->value)
{
// We've found a free space, try to move it up.
return PREFIX(_table_move_gap)(table, hash + i, hash, cell);
}
}
return 0;
}
static int PREFIX(_insert)(PREFIX(_table) *table,
const void *key,
void *value)
{
uint32_t hash = MAP_TABLE_HASH_KEY(key);
PREFIX(_table_cell) cell = PREFIX(_table_lookup)(table, hash);
if (NULL == cell->value)
{
cell->value = value;
table->table_used++;
return 1;
}
/* If this cell is full, try the next one. */
for (unsigned int i=0 ; i<32 ; i++)
{
PREFIX(_table_cell) second =
PREFIX(_table_lookup)(table, hash+i);
if (NULL == second->value)
{
cell->secondMaps |= (1 << (i-1));
second->value = value;
table->table_used++;
return 1;
}
}
/* If this virtual cell is full, rebalance the hash from this point and
* try again. */
if (PREFIX(_table_rebalance)(table, hash))
{
return PREFIX(_insert)(table, key, value);
}
return 0;
}
static void *PREFIX(_table_get)(PREFIX(_table) *table, const void *key)
{
uint32_t hash = MAP_TABLE_HASH_KEY(key);
PREFIX(_table_cell) cell = PREFIX(_table_lookup)(table, hash);
// Value does not exist.
if (NULL == cell->value)
{
return NULL;
}
if (MAP_TABLE_COMPARE_FUNCTION(key, cell->value))
{
return cell->value;
}
uint32_t jump = cell->secondMaps;
// Look at each offset defined by the jump table to find the displaced location.
for (int hop = __builtin_ffs(jump) ; hop > 0 ; hop = __builtin_ffs(jump))
{
PREFIX(_table_cell) hopCell = PREFIX(_table_lookup)(table, hash+hop);
if (MAP_TABLE_COMPARE_FUNCTION(key, hopCell->value))
{
return hopCell->value;
}
// Clear the most significant bit and try again.
jump &= ~(1 << (hop-1));
}
return NULL;
}
void *PREFIX(_next)(PREFIX(_table) *table,
struct PREFIX(_table_enumerator) **state)
{
if (NULL == *state)
{
*state = calloc(1, sizeof(struct PREFIX(_table_enumerator)));
}
if ((*state)->seen >= table->table_used)
{
free(*state);
return NULL;
}
while ((++((*state)->index)) < TABLE_SIZE(table))
{
if (table->table[(*state)->index].value != NULL)
{
return table->table[(*state)->index].value;
}
}
// Should not be reached, but may be if the table is unsafely modified.
free(*state);
return NULL;
}
#undef TABLE_SIZE
#undef REALLY_PREFIX_SUFFIX
#undef PREFIX_SUFFIX
#undef PREFIX
#undef MAP_TABLE_NAME
#undef MAP_TABLE_COMPARE_FUNCTION
#undef MAP_TABLE_HASH_KEY
#undef MAP_TABLE_HASH_VALUE
#ifdef MAP_TABLE_STATIC_SIZE
# undef MAP_TABLE_STATIC_SIZE
#endif