mirror of
https://github.com/X11Libre/xserver.git
synced 2026-03-24 08:04:30 +00:00
Using calloc() instead of malloc() as preventive measure, so there never can be any hidden bugs or leaks due uninitialized memory. The extra cost of using this compiler intrinsic should be practically impossible to measure - in many cases a good compiler can even deduce if certain areas really don't need to be zero'd (because they're written to right after allocation) and create more efficient machine code. The code pathes in question are pretty cold anyways, so it's probably not worth even thinking about potential extra runtime costs. Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
297 lines
6.9 KiB
C
297 lines
6.9 KiB
C
#include <dix-config.h>
|
|
|
|
#include <stdlib.h>
|
|
#include "misc.h"
|
|
#include "hashtable.h"
|
|
|
|
/* HashResourceID */
|
|
#include "resource.h"
|
|
|
|
#define INITHASHSIZE 6
|
|
#define MAXHASHSIZE 11
|
|
|
|
struct HashTableRec {
|
|
int keySize;
|
|
int dataSize;
|
|
|
|
int elements; /* number of elements inserted */
|
|
int bucketBits; /* number of buckets is 1 << bucketBits */
|
|
struct xorg_list *buckets; /* array of bucket list heads */
|
|
|
|
HashFunc hash;
|
|
HashCompareFunc compare;
|
|
|
|
void *cdata;
|
|
};
|
|
|
|
typedef struct {
|
|
struct xorg_list l;
|
|
void *key;
|
|
void *data;
|
|
} BucketRec, *BucketPtr;
|
|
|
|
HashTable
|
|
ht_create(int keySize,
|
|
int dataSize,
|
|
HashFunc hash,
|
|
HashCompareFunc compare,
|
|
void *cdata)
|
|
{
|
|
int c;
|
|
int numBuckets;
|
|
HashTable ht = calloc(1, sizeof(struct HashTableRec));
|
|
|
|
if (!ht) {
|
|
return NULL;
|
|
}
|
|
|
|
ht->keySize = keySize;
|
|
ht->dataSize = dataSize;
|
|
ht->hash = hash;
|
|
ht->compare = compare;
|
|
ht->elements = 0;
|
|
ht->bucketBits = INITHASHSIZE;
|
|
numBuckets = 1 << ht->bucketBits;
|
|
ht->buckets = xallocarray(numBuckets, sizeof(*ht->buckets));
|
|
ht->cdata = cdata;
|
|
|
|
if (ht->buckets) {
|
|
for (c = 0; c < numBuckets; ++c) {
|
|
xorg_list_init(&ht->buckets[c]);
|
|
}
|
|
return ht;
|
|
} else {
|
|
free(ht);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void
|
|
ht_destroy(HashTable ht)
|
|
{
|
|
int c;
|
|
BucketPtr it, tmp;
|
|
int numBuckets = 1 << ht->bucketBits;
|
|
for (c = 0; c < numBuckets; ++c) {
|
|
xorg_list_for_each_entry_safe(it, tmp, &ht->buckets[c], l) {
|
|
xorg_list_del(&it->l);
|
|
free(it->key);
|
|
free(it->data);
|
|
free(it);
|
|
}
|
|
}
|
|
free(ht->buckets);
|
|
free(ht);
|
|
}
|
|
|
|
static Bool
|
|
double_size(HashTable ht)
|
|
{
|
|
struct xorg_list *newBuckets;
|
|
int numBuckets = 1 << ht->bucketBits;
|
|
int newBucketBits = ht->bucketBits + 1;
|
|
int newNumBuckets = 1 << newBucketBits;
|
|
int c;
|
|
|
|
newBuckets = xallocarray(newNumBuckets, sizeof(*ht->buckets));
|
|
if (newBuckets) {
|
|
for (c = 0; c < newNumBuckets; ++c) {
|
|
xorg_list_init(&newBuckets[c]);
|
|
}
|
|
|
|
for (c = 0; c < numBuckets; ++c) {
|
|
BucketPtr it, tmp;
|
|
xorg_list_for_each_entry_safe(it, tmp, &ht->buckets[c], l) {
|
|
struct xorg_list *newBucket =
|
|
&newBuckets[ht->hash(ht->cdata, it->key, newBucketBits)];
|
|
xorg_list_del(&it->l);
|
|
xorg_list_add(&it->l, newBucket);
|
|
}
|
|
}
|
|
free(ht->buckets);
|
|
|
|
ht->buckets = newBuckets;
|
|
ht->bucketBits = newBucketBits;
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
void *
|
|
ht_add(HashTable ht, const void *key)
|
|
{
|
|
unsigned index = ht->hash(ht->cdata, key, ht->bucketBits);
|
|
struct xorg_list *bucket = &ht->buckets[index];
|
|
BucketRec *elem = calloc(1, sizeof(BucketRec));
|
|
if (!elem) {
|
|
goto outOfMemory;
|
|
}
|
|
elem->key = calloc(1, ht->keySize);
|
|
if (!elem->key) {
|
|
goto outOfMemory;
|
|
}
|
|
/* we avoid signaling an out-of-memory error if dataSize is 0 */
|
|
elem->data = calloc(1, ht->dataSize);
|
|
if (ht->dataSize && !elem->data) {
|
|
goto outOfMemory;
|
|
}
|
|
xorg_list_add(&elem->l, bucket);
|
|
++ht->elements;
|
|
|
|
memcpy(elem->key, key, ht->keySize);
|
|
|
|
if (ht->elements > 4 * (1 << ht->bucketBits) &&
|
|
ht->bucketBits < MAXHASHSIZE) {
|
|
if (!double_size(ht)) {
|
|
--ht->elements;
|
|
xorg_list_del(&elem->l);
|
|
goto outOfMemory;
|
|
}
|
|
}
|
|
|
|
/* if memory allocation has failed due to dataSize being 0, return
|
|
a "dummy" pointer pointing at the of the key */
|
|
return elem->data ? elem->data : ((char*) elem->key + ht->keySize);
|
|
|
|
outOfMemory:
|
|
if (elem) {
|
|
free(elem->key);
|
|
free(elem->data);
|
|
free(elem);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
ht_remove(HashTable ht, const void *key)
|
|
{
|
|
unsigned index = ht->hash(ht->cdata, key, ht->bucketBits);
|
|
struct xorg_list *bucket = &ht->buckets[index];
|
|
BucketPtr it;
|
|
|
|
xorg_list_for_each_entry(it, bucket, l) {
|
|
if (ht->compare(ht->cdata, key, it->key) == 0) {
|
|
xorg_list_del(&it->l);
|
|
--ht->elements;
|
|
free(it->key);
|
|
free(it->data);
|
|
free(it);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void *
|
|
ht_find(HashTable ht, const void *key)
|
|
{
|
|
unsigned index = ht->hash(ht->cdata, key, ht->bucketBits);
|
|
struct xorg_list *bucket = &ht->buckets[index];
|
|
BucketPtr it;
|
|
|
|
xorg_list_for_each_entry(it, bucket, l) {
|
|
if (ht->compare(ht->cdata, key, it->key) == 0) {
|
|
return it->data ? it->data : ((char*) it->key + ht->keySize);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
ht_dump_distribution(HashTable ht)
|
|
{
|
|
int c;
|
|
int numBuckets = 1 << ht->bucketBits;
|
|
for (c = 0; c < numBuckets; ++c) {
|
|
BucketPtr it;
|
|
int n = 0;
|
|
|
|
xorg_list_for_each_entry(it, &ht->buckets[c], l) {
|
|
++n;
|
|
}
|
|
printf("%d: %d\n", c, n);
|
|
}
|
|
}
|
|
|
|
/* Picked the function from http://burtleburtle.net/bob/hash/doobs.html by
|
|
Bob Jenkins, which is released in public domain */
|
|
static CARD32
|
|
one_at_a_time_hash(const void *data, int len)
|
|
{
|
|
CARD32 hash;
|
|
int i;
|
|
const char *key = data;
|
|
for (hash=0, i=0; i<len; ++i) {
|
|
hash += key[i];
|
|
hash += (hash << 10);
|
|
hash ^= (hash >> 6);
|
|
}
|
|
hash += (hash << 3);
|
|
hash ^= (hash >> 11);
|
|
hash += (hash << 15);
|
|
return hash;
|
|
}
|
|
|
|
unsigned
|
|
ht_generic_hash(void *cdata, const void *ptr, int numBits)
|
|
{
|
|
HtGenericHashSetupPtr setup = cdata;
|
|
return one_at_a_time_hash(ptr, setup->keySize) & ~((~0U) << numBits);
|
|
}
|
|
|
|
int
|
|
ht_generic_compare(void *cdata, const void *l, const void *r)
|
|
{
|
|
HtGenericHashSetupPtr setup = cdata;
|
|
return memcmp(l, r, setup->keySize);
|
|
}
|
|
|
|
unsigned
|
|
ht_resourceid_hash(void * cdata, const void * data, int numBits)
|
|
{
|
|
const XID* idPtr = data;
|
|
XID id = *idPtr & RESOURCE_ID_MASK;
|
|
(void) cdata;
|
|
return HashResourceID(id, numBits);
|
|
}
|
|
|
|
int
|
|
ht_resourceid_compare(void* cdata, const void* a, const void* b)
|
|
{
|
|
const XID* xa = a;
|
|
const XID* xb = b;
|
|
(void) cdata;
|
|
return
|
|
*xa < *xb ? -1 :
|
|
*xa > *xb ? 1 :
|
|
0;
|
|
}
|
|
|
|
void
|
|
ht_dump_contents(HashTable ht,
|
|
void (*print_key)(void *opaque, void *key),
|
|
void (*print_value)(void *opaque, void *value),
|
|
void* opaque)
|
|
{
|
|
int c;
|
|
int numBuckets = 1 << ht->bucketBits;
|
|
for (c = 0; c < numBuckets; ++c) {
|
|
BucketPtr it;
|
|
int n = 0;
|
|
|
|
printf("%d: ", c);
|
|
xorg_list_for_each_entry(it, &ht->buckets[c], l) {
|
|
if (n > 0) {
|
|
printf(", ");
|
|
}
|
|
print_key(opaque, it->key);
|
|
printf("->");
|
|
print_value(opaque, it->data);
|
|
++n;
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|