Add hash map routines
This commit is contained in:
parent
fd98d65ef6
commit
edea2a350d
@ -36,7 +36,8 @@ void *XmdBufferBreak (XmdBuffer *buffer);
|
|||||||
/* XmdBufferLength returns the amount of elements stored in a buffer. */
|
/* XmdBufferLength returns the amount of elements stored in a buffer. */
|
||||||
Cardinal XmdBufferLength (XmdBuffer *buffer);
|
Cardinal XmdBufferLength (XmdBuffer *buffer);
|
||||||
|
|
||||||
/* XmdBufferFree frees the buffer and any data associated with it. */
|
/* XmdBufferFree frees the buffer and any data associated with it. Note that if
|
||||||
|
the buffer contains pointers, the values they point to will not be freed. */
|
||||||
void XmdBufferFree (XmdBuffer *buffer);
|
void XmdBufferFree (XmdBuffer *buffer);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
#ifndef _XmdLauncher_h
|
|
||||||
#define _XmdLauncher_h
|
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <X11/Intrinsic.h>
|
|
||||||
#include <Xm/Xm.h>
|
|
||||||
|
|
||||||
/* TODO */
|
|
||||||
|
|
||||||
#endif
|
|
39
libXmd/include/Xmd/Map.h
Normal file
39
libXmd/include/Xmd/Map.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef _XmdMap_h
|
||||||
|
#define _XmdMap_h
|
||||||
|
|
||||||
|
#include <X11/Intrinsic.h>
|
||||||
|
|
||||||
|
/* XmdMapKey is used to key XmdMap. */
|
||||||
|
typedef long unsigned int XmdMapKey;
|
||||||
|
|
||||||
|
/* XmdMap is a hash map that is keyed by an integer and can store any type of
|
||||||
|
element. */
|
||||||
|
typedef struct _XmdMap XmdMap;
|
||||||
|
|
||||||
|
/* XmdMapIterator is a function that can iterate over a map. */
|
||||||
|
typedef Bool (*XmdMapIterator) (XmdMap *map, XmdMapKey key, void *value);
|
||||||
|
|
||||||
|
/* XmdMapNew creates a new map.*/
|
||||||
|
XmdMap *XmdMapNew ();
|
||||||
|
|
||||||
|
/* XmdMapGet retrieves a value from a map. */
|
||||||
|
void *XmdMapGet (XmdMap *map, XmdMapKey key);
|
||||||
|
|
||||||
|
/* XmdMapSet sets a value in a map. The previous value associated with the key,
|
||||||
|
if it exists, is overwritten by the new value. If the value is set to NULL,
|
||||||
|
the key/value combination is deleted from the map. This function returns
|
||||||
|
the previous value associated with the key so it can be free'd if necessary.
|
||||||
|
*/
|
||||||
|
void *XmdMapSet (XmdMap *map, XmdMapKey key, void *value);
|
||||||
|
|
||||||
|
/* XmdMapIterate calls iterator for each key/value pair in a map. */
|
||||||
|
void XmdMapIterate (XmdMap *map, XmdMapIterator iterator);
|
||||||
|
|
||||||
|
/* XmdMapLength returns the number of key/value entries stored in a map. */
|
||||||
|
Cardinal XmdMapLength (XmdMap *map);
|
||||||
|
|
||||||
|
/* XmdMapFree frees the map and any data associated with it. Note that this will
|
||||||
|
not free any of the map's values. */
|
||||||
|
void XmdMapFree (XmdMap *map);
|
||||||
|
|
||||||
|
#endif
|
161
libXmd/src/Map.c
Normal file
161
libXmd/src/Map.c
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
#include <Xmd/Map.h>
|
||||||
|
#include <X11/Intrinsic.h>
|
||||||
|
|
||||||
|
#define XmdMAP_GROWTH_FACTOR 2
|
||||||
|
#define XmdMAP_INITIAL_CAPACITY 16
|
||||||
|
|
||||||
|
typedef struct _XmdMapElement {
|
||||||
|
XmdMapKey key;
|
||||||
|
void *value;
|
||||||
|
struct _XmdMapElement *sister;
|
||||||
|
} XmdMapElement;
|
||||||
|
|
||||||
|
struct _XmdMap {
|
||||||
|
Cardinal length;
|
||||||
|
Cardinal capacity;
|
||||||
|
XmdMapElement **data;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void XmdMapResizeIfNeeded (XmdMap *map);
|
||||||
|
static Cardinal XmdMapHash (XmdMap *map, XmdMapKey key);
|
||||||
|
|
||||||
|
XmdMap *XmdMapNew () {
|
||||||
|
XmdMap *map = XtNew(XmdMap);
|
||||||
|
if (map == NULL) goto fail;
|
||||||
|
|
||||||
|
map->length = 0;
|
||||||
|
map->capacity = XmdMAP_INITIAL_CAPACITY;
|
||||||
|
map->data = (XmdMapElement **) (
|
||||||
|
XtCalloc(map->capacity, sizeof(XmdMapElement *)));
|
||||||
|
if (map->data == NULL) goto fail;
|
||||||
|
|
||||||
|
return map;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
XmdMapFree(map);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *XmdMapGet (XmdMap *map, XmdMapKey key) {
|
||||||
|
XmdMapElement *element = map->data[XmdMapHash(map, key)];
|
||||||
|
while (element != NULL) {
|
||||||
|
if (element->key == key) return element->value;
|
||||||
|
element = element->sister;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *XmdMapSetInternal (XmdMap *map, XmdMapKey key, void *value) {
|
||||||
|
/* find bucket */
|
||||||
|
XmdMapElement **destination = &map->data[XmdMapHash(map, key)];
|
||||||
|
/* find position in bucket */
|
||||||
|
while (*destination != NULL) {
|
||||||
|
if ((*destination)->key == key) {
|
||||||
|
/* element exists already */
|
||||||
|
void *previous = (*destination)->value;
|
||||||
|
if (value == NULL) {
|
||||||
|
/* if setting to NULL, free old element */
|
||||||
|
XmdMapElement *element = *destination;
|
||||||
|
*destination = element->sister;
|
||||||
|
XtFree((char *)(element));
|
||||||
|
} else {
|
||||||
|
/* otherwise, replace value */
|
||||||
|
(*destination)->value = value;
|
||||||
|
}
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
destination = &((*destination)->sister);
|
||||||
|
}
|
||||||
|
/* element does not exist already */
|
||||||
|
if (value != NULL) {
|
||||||
|
/* allocate new element and append */
|
||||||
|
*destination = XtNew(XmdMapElement);
|
||||||
|
(*destination)->key = key;
|
||||||
|
(*destination)->value = value;
|
||||||
|
(*destination)->sister = NULL;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *XmdMapSet (XmdMap *map, XmdMapKey key, void *value) {
|
||||||
|
void *previous = XmdMapSetInternal(map, key, value);
|
||||||
|
if (value == NULL) {
|
||||||
|
if (previous != NULL) map->length --;
|
||||||
|
} else {
|
||||||
|
if (previous == NULL) map->length ++;
|
||||||
|
}
|
||||||
|
XmdMapResizeIfNeeded(map);
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmdMapIterate (XmdMap *map, XmdMapIterator iterator) {
|
||||||
|
for (Cardinal index = 0; index < map->capacity; index ++) {
|
||||||
|
XmdMapElement *element = map->data[index];
|
||||||
|
while (element != NULL) {
|
||||||
|
if(!iterator(map, element->key, element->value)) break;
|
||||||
|
element = element->sister;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cardinal XmdMaplength (XmdMap *map) {
|
||||||
|
return map->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmdMapFree (XmdMap *map) {
|
||||||
|
if (map == NULL) return;
|
||||||
|
XtFree((char *)(map->data));
|
||||||
|
XtFree((char *)(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
void XmdMapResizeIfNeeded (XmdMap *map) {
|
||||||
|
Cardinal oldCapacity = map->capacity;
|
||||||
|
XmdMapElement **oldData = map->data;
|
||||||
|
|
||||||
|
/* figure out what the new capacity should be. if the map length is
|
||||||
|
very small compared to the map capacity, shrink the map to save
|
||||||
|
memory. if the map length is getting large compared to the map
|
||||||
|
capacity, grow the map to reduce collisions. if the map length is
|
||||||
|
neither too big nor too small, do nothing because rehashing is an
|
||||||
|
expensive operation. */
|
||||||
|
if (map->length + XmdMAP_INITIAL_CAPACITY < map->capacity / 4) {
|
||||||
|
map->capacity /= XmdMAP_GROWTH_FACTOR;
|
||||||
|
} else if (map->length > (map->capacity * 2) / 3) {
|
||||||
|
map->capacity *= XmdMAP_GROWTH_FACTOR;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* rehash */
|
||||||
|
map->data = (XmdMapElement **) (
|
||||||
|
XtCalloc(map->capacity, sizeof(XmdMapElement)));
|
||||||
|
for (Cardinal index = 0; index < oldCapacity; index ++) {
|
||||||
|
XmdMapElement *element = oldData[index];
|
||||||
|
while (element != NULL) {
|
||||||
|
XmdMapSetInternal(map, element->key, element->value);
|
||||||
|
element = element->sister;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XtFree((char *)(oldData));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Cardinal XmdMapHash (XmdMap *map, XmdMapKey key) {
|
||||||
|
/* https://web.archive.org/web/20160329102146/http://elliottback.com/wp/
|
||||||
|
hashmap-implementation-in-c/ */
|
||||||
|
|
||||||
|
/* Robert Jenkins' 32 bit Mix Function */
|
||||||
|
key += (key << 12);
|
||||||
|
key ^= (key >> 22);
|
||||||
|
key += (key << 4);
|
||||||
|
key ^= (key >> 9);
|
||||||
|
key += (key << 10);
|
||||||
|
key ^= (key >> 2);
|
||||||
|
key += (key << 7);
|
||||||
|
key ^= (key >> 12);
|
||||||
|
|
||||||
|
/* Knuth's Multiplicative Method */
|
||||||
|
key = (key >> 3) * 2654435761;
|
||||||
|
|
||||||
|
return key % map->capacity;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user