#include "glheader.h"
#include "imports.h"
#include "glthread.h"
#include "hash.h"
#define TABLE_SIZE 1023
#define HASH_FUNC(K) ((K) % TABLE_SIZE)
struct HashEntry {
GLuint Key;
void *Data;
struct HashEntry *Next;
};
struct _mesa_HashTable {
struct HashEntry *Table[TABLE_SIZE];
GLuint MaxKey;
_glthread_Mutex Mutex;
GLboolean InDeleteAll;
};
struct _mesa_HashTable *
_mesa_NewHashTable(void)
{
struct _mesa_HashTable *table = CALLOC_STRUCT(_mesa_HashTable);
if (table) {
_glthread_INIT_MUTEX(table->Mutex);
}
return table;
}
void
_mesa_DeleteHashTable(struct _mesa_HashTable *table)
{
GLuint pos;
assert(table);
for (pos = 0; pos < TABLE_SIZE; pos++) {
struct HashEntry *entry = table->Table[pos];
while (entry) {
struct HashEntry *next = entry->Next;
if (entry->Data) {
_mesa_problem(NULL,
"In _mesa_DeleteHashTable, found non-freed data");
}
_mesa_free(entry);
entry = next;
}
}
_glthread_DESTROY_MUTEX(table->Mutex);
_mesa_free(table);
}
void *
_mesa_HashLookup(const struct _mesa_HashTable *table, GLuint key)
{
GLuint pos;
const struct HashEntry *entry;
assert(table);
assert(key);
pos = HASH_FUNC(key);
entry = table->Table[pos];
while (entry) {
if (entry->Key == key) {
return entry->Data;
}
entry = entry->Next;
}
return NULL;
}
void
_mesa_HashInsert(struct _mesa_HashTable *table, GLuint key, void *data)
{
GLuint pos;
struct HashEntry *entry;
assert(table);
assert(key);
_glthread_LOCK_MUTEX(table->Mutex);
if (key > table->MaxKey)
table->MaxKey = key;
pos = HASH_FUNC(key);
for (entry = table->Table[pos]; entry; entry = entry->Next) {
if (entry->Key == key) {
#if 0
if (entry->Data) {
_mesa_problem(NULL, "Memory leak detected in _mesa_HashInsert");
}
#endif
entry->Data = data;
_glthread_UNLOCK_MUTEX(table->Mutex);
return;
}
}
entry = MALLOC_STRUCT(HashEntry);
entry->Key = key;
entry->Data = data;
entry->Next = table->Table[pos];
table->Table[pos] = entry;
_glthread_UNLOCK_MUTEX(table->Mutex);
}
void
_mesa_HashRemove(struct _mesa_HashTable *table, GLuint key)
{
GLuint pos;
struct HashEntry *entry, *prev;
assert(table);
assert(key);
if (table->InDeleteAll) {
_mesa_problem(NULL, "_mesa_HashRemove illegally called from "
"_mesa_HashDeleteAll callback function");
return;
}
_glthread_LOCK_MUTEX(table->Mutex);
pos = HASH_FUNC(key);
prev = NULL;
entry = table->Table[pos];
while (entry) {
if (entry->Key == key) {
if (prev) {
prev->Next = entry->Next;
}
else {
table->Table[pos] = entry->Next;
}
_mesa_free(entry);
_glthread_UNLOCK_MUTEX(table->Mutex);
return;
}
prev = entry;
entry = entry->Next;
}
_glthread_UNLOCK_MUTEX(table->Mutex);
}
void
_mesa_HashDeleteAll(struct _mesa_HashTable *table,
void (*callback)(GLuint key, void *data, void *userData),
void *userData)
{
GLuint pos;
ASSERT(table);
ASSERT(callback);
_glthread_LOCK_MUTEX(table->Mutex);
table->InDeleteAll = GL_TRUE;
for (pos = 0; pos < TABLE_SIZE; pos++) {
struct HashEntry *entry, *next;
for (entry = table->Table[pos]; entry; entry = next) {
callback(entry->Key, entry->Data, userData);
next = entry->Next;
_mesa_free(entry);
}
table->Table[pos] = NULL;
}
table->InDeleteAll = GL_FALSE;
_glthread_UNLOCK_MUTEX(table->Mutex);
}
void
_mesa_HashWalk(const struct _mesa_HashTable *table,
void (*callback)(GLuint key, void *data, void *userData),
void *userData)
{
struct _mesa_HashTable *table2 = (struct _mesa_HashTable *) table;
GLuint pos;
ASSERT(table);
ASSERT(callback);
_glthread_UNLOCK_MUTEX(table2->Mutex);
for (pos = 0; pos < TABLE_SIZE; pos++) {
struct HashEntry *entry;
for (entry = table->Table[pos]; entry; entry = entry->Next) {
callback(entry->Key, entry->Data, userData);
}
}
_glthread_UNLOCK_MUTEX(table2->Mutex);
}
GLuint
_mesa_HashFirstEntry(struct _mesa_HashTable *table)
{
GLuint pos;
assert(table);
_glthread_LOCK_MUTEX(table->Mutex);
for (pos = 0; pos < TABLE_SIZE; pos++) {
if (table->Table[pos]) {
_glthread_UNLOCK_MUTEX(table->Mutex);
return table->Table[pos]->Key;
}
}
_glthread_UNLOCK_MUTEX(table->Mutex);
return 0;
}
GLuint
_mesa_HashNextEntry(const struct _mesa_HashTable *table, GLuint key)
{
const struct HashEntry *entry;
GLuint pos;
assert(table);
assert(key);
pos = HASH_FUNC(key);
for (entry = table->Table[pos]; entry ; entry = entry->Next) {
if (entry->Key == key) {
break;
}
}
if (!entry) {
return 0;
}
if (entry->Next) {
return entry->Next->Key;
}
else {
pos++;
while (pos < TABLE_SIZE) {
if (table->Table[pos]) {
return table->Table[pos]->Key;
}
pos++;
}
return 0;
}
}
void
_mesa_HashPrint(const struct _mesa_HashTable *table)
{
GLuint pos;
assert(table);
for (pos = 0; pos < TABLE_SIZE; pos++) {
const struct HashEntry *entry = table->Table[pos];
while (entry) {
_mesa_debug(NULL, "%u %p\n", entry->Key, entry->Data);
entry = entry->Next;
}
}
}
GLuint
_mesa_HashFindFreeKeyBlock(struct _mesa_HashTable *table, GLuint numKeys)
{
const GLuint maxKey = ~((GLuint) 0);
_glthread_LOCK_MUTEX(table->Mutex);
if (maxKey - numKeys > table->MaxKey) {
_glthread_UNLOCK_MUTEX(table->Mutex);
return table->MaxKey + 1;
}
else {
GLuint freeCount = 0;
GLuint freeStart = 1;
GLuint key;
for (key = 1; key != maxKey; key++) {
if (_mesa_HashLookup(table, key)) {
freeCount = 0;
freeStart = key+1;
}
else {
freeCount++;
if (freeCount == numKeys) {
_glthread_UNLOCK_MUTEX(table->Mutex);
return freeStart;
}
}
}
_glthread_UNLOCK_MUTEX(table->Mutex);
return 0;
}
}
#if 0
static void
test_hash_walking(void)
{
struct _mesa_HashTable *t = _mesa_NewHashTable();
const GLuint limit = 50000;
GLuint i;
for (i = 0; i < limit; i++) {
GLuint dummy;
GLuint k = (rand() % (limit * 10)) + 1;
while (_mesa_HashLookup(t, k)) {
k = (rand() % (limit * 10)) + 1;
}
_mesa_HashInsert(t, k, &dummy);
}
{
GLuint k = _mesa_HashFirstEntry(t);
GLuint count = 0;
while (k) {
GLuint knext = _mesa_HashNextEntry(t, k);
assert(knext != k);
_mesa_HashRemove(t, k);
count++;
k = knext;
}
assert(count == limit);
k = _mesa_HashFirstEntry(t);
assert(k==0);
}
_mesa_DeleteHashTable(t);
}
void
_mesa_test_hash_functions(void)
{
int a, b, c;
struct _mesa_HashTable *t;
t = _mesa_NewHashTable();
_mesa_HashInsert(t, 501, &a);
_mesa_HashInsert(t, 10, &c);
_mesa_HashInsert(t, 0xfffffff8, &b);
assert(_mesa_HashLookup(t,501));
assert(!_mesa_HashLookup(t,1313));
assert(_mesa_HashFindFreeKeyBlock(t, 100));
_mesa_DeleteHashTable(t);
test_hash_walking();
}
#endif