#include #include #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; }