Add hash map routines

This commit is contained in:
Sasha Koshka 2023-11-12 13:53:08 -05:00
parent fd98d65ef6
commit edea2a350d
4 changed files with 202 additions and 11 deletions

View File

@ -36,7 +36,8 @@ void *XmdBufferBreak (XmdBuffer *buffer);
/* XmdBufferLength returns the amount of elements stored in a 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);
#endif

View File

@ -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
View 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
View 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;
}