Add string-keyed map and fixed memory leak
This commit is contained in:
parent
edea2a350d
commit
854fbf371c
39
libXmd/include/Xmd/StringMap.h
Normal file
39
libXmd/include/Xmd/StringMap.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef _XmdStringMap_h
|
||||||
|
#define _XmdStringMap_h
|
||||||
|
|
||||||
|
#include <X11/Intrinsic.h>
|
||||||
|
|
||||||
|
/* XmdStringMap is a hash map that is keyed by a string and can store any type
|
||||||
|
of element. */
|
||||||
|
typedef struct _XmdStringMap XmdStringMap;
|
||||||
|
|
||||||
|
/* XmdStringMapIterator is a function that can iterate over a string map. */
|
||||||
|
typedef Bool (*XmdStringMapIterator) (
|
||||||
|
XmdStringMap *map, String key, void *value);
|
||||||
|
|
||||||
|
/* XmdStringMapNew creates a new map.*/
|
||||||
|
XmdStringMap *XmdStringMapNew ();
|
||||||
|
|
||||||
|
/* XmdStringMapGet retrieves a value from a map. */
|
||||||
|
void *XmdStringMapGet (XmdStringMap *map, String key);
|
||||||
|
|
||||||
|
/* XmdStringMapSet 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 *XmdStringMapSet (XmdStringMap *map, String key, void *value);
|
||||||
|
|
||||||
|
/* XmdStringMapIterate calls iterator for each key/value pair in a map. */
|
||||||
|
void XmdStringMapIterate (XmdStringMap *map, XmdStringMapIterator iterator);
|
||||||
|
|
||||||
|
/* XmdStringMapLength returns the number of key/value entries stored in a map.
|
||||||
|
*/
|
||||||
|
Cardinal XmdStringMapLength (XmdStringMap *map);
|
||||||
|
|
||||||
|
/* XmdStringMapFree frees the map and any data associated with it. Note that
|
||||||
|
this will not free any of the map's values. */
|
||||||
|
void XmdStringMapFree (XmdStringMap *map);
|
||||||
|
|
||||||
|
#endif
|
@ -133,7 +133,9 @@ void XmdMapResizeIfNeeded (XmdMap *map) {
|
|||||||
XmdMapElement *element = oldData[index];
|
XmdMapElement *element = oldData[index];
|
||||||
while (element != NULL) {
|
while (element != NULL) {
|
||||||
XmdMapSetInternal(map, element->key, element->value);
|
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
157
libXmd/src/StringMap.c
Normal 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user