Add string-keyed map and fixed memory leak

This commit is contained in:
Sasha Koshka
2023-11-12 15:07:08 -05:00
parent edea2a350d
commit 854fbf371c
3 changed files with 199 additions and 1 deletions

View File

@@ -133,7 +133,9 @@ void XmdMapResizeIfNeeded (XmdMap *map) {
XmdMapElement *element = oldData[index];
while (element != NULL) {
XmdMapSetInternal(map, element->key, element->value);
element = element->sister;
XmdMapElement *sister = element->sister;
XtFree((char *)(element));
element = sister;
}
}

157
libXmd/src/StringMap.c Normal file
View File

@@ -0,0 +1,157 @@
#include <Xmd/StringMap.h>
#include <X11/Intrinsic.h>
#define XmdStringMap_GROWTH_FACTOR 2
#define XmdStringMap_INITIAL_CAPACITY 16
typedef struct _XmdStringMapElement {
String key;
void *value;
struct _XmdStringMapElement *sister;
} XmdStringMapElement;
struct _XmdStringMap {
Cardinal length;
Cardinal capacity;
XmdStringMapElement **data;
};
static void XmdStringMapResizeIfNeeded (XmdStringMap *map);
static Cardinal XmdStringMapHash (XmdStringMap *map, String key);
XmdStringMap *XmdStringMapNew () {
XmdStringMap *map = XtNew(XmdStringMap);
if (map == NULL) goto fail;
map->length = 0;
map->capacity = XmdStringMap_INITIAL_CAPACITY;
map->data = (XmdStringMapElement **) (
XtCalloc(map->capacity, sizeof(XmdStringMapElement *)));
if (map->data == NULL) goto fail;
return map;
fail:
XmdStringMapFree(map);
return NULL;
}
void *XmdStringMapGet (XmdStringMap *map, String key) {
XmdStringMapElement *element = map->data[XmdStringMapHash(map, key)];
while (element != NULL) {
if (strcmp(element->key, key) == 0) return element->value;
element = element->sister;
}
return NULL;
}
void *XmdStringMapSetInternal (XmdStringMap *map, String key, void *value) {
/* find bucket */
Cardinal hash = XmdStringMapHash(map, key);
XmdStringMapElement **destination = &map->data[hash];
/* find position in bucket */
while (*destination != NULL) {
if (strcmp((*destination)->key, key) == 0) {
/* element exists already */
void *previous = (*destination)->value;
if (value == NULL) {
/* if setting to NULL, free old element */
XmdStringMapElement *element = *destination;
*destination = element->sister;
XtFree((char *)(element));
} else {
/* otherwise, replace value */
(*destination)->value = XtNewString(value);
}
return previous;
}
destination = &((*destination)->sister);
}
/* element does not exist already */
if (value != NULL) {
/* allocate new element and append */
*destination = XtNew(XmdStringMapElement);
(*destination)->key = XtNewString(key);
(*destination)->value = value;
(*destination)->sister = NULL;
}
return NULL;
}
void *XmdStringMapSet (XmdStringMap *map, String key, void *value) {
void *previous = XmdStringMapSetInternal(map, key, value);
if (value == NULL) {
if (previous != NULL) map->length --;
} else {
if (previous == NULL) map->length ++;
}
XmdStringMapResizeIfNeeded(map);
return previous;
}
void XmdStringMapIterate (XmdStringMap *map, XmdStringMapIterator iterator) {
for (Cardinal index = 0; index < map->capacity; index ++) {
XmdStringMapElement *element = map->data[index];
while (element != NULL) {
if(!iterator(map, element->key, element->value)) break;
element = element->sister;
}
}
}
Cardinal XmdStringMaplength (XmdStringMap *map) {
return map->length;
}
void XmdStringMapFree (XmdStringMap *map) {
if (map == NULL) return;
XtFree((char *)(map->data));
XtFree((char *)(map));
}
void XmdStringMapResizeIfNeeded (XmdStringMap *map) {
Cardinal oldCapacity = map->capacity;
XmdStringMapElement **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 + XmdStringMap_INITIAL_CAPACITY < map->capacity / 4) {
map->capacity /= XmdStringMap_GROWTH_FACTOR;
} else if (map->length > (map->capacity * 2) / 3) {
map->capacity *= XmdStringMap_GROWTH_FACTOR;
} else {
return;
}
/* rehash */
map->data = (XmdStringMapElement **) (
XtCalloc(map->capacity, sizeof(XmdStringMapElement)));
for (Cardinal index = 0; index < oldCapacity; index ++) {
XmdStringMapElement *element = oldData[index];
while (element != NULL) {
XmdStringMapSetInternal (
map,
element->key, element->value);
XmdStringMapElement *sister = element->sister;
XtFree(element->key);
XtFree((char *)(element));
element = sister;
}
}
XtFree((char *)(oldData));
}
static Cardinal XmdStringMapHash (XmdStringMap *map, String key) {
/* http://www.cse.yorku.ca/~oz/hash.html */
Cardinal hash = 5381;
Cardinal ch;
while ((ch = (Cardinal)(*key ++)) != 0) {
hash = ((hash << 5) + hash) + ch;
}
return hash % map->capacity;
}