#include <utils/Log.h>
#include "allocation_tracker.h"
+#include "allocator.h"
#include "hash_functions.h"
#include "hash_map.h"
#include "osi.h"
bool freed;
} allocation_t;
+
+// Hidden constructor for hash map for our use only. Everything else should use the
+// normal interface.
+hash_map_t *hash_map_new_internal(
+ size_t size,
+ hash_index_fn hash_fn,
+ key_free_fn key_fn,
+ data_free_fn,
+ const allocator_t *zeroed_allocator);
+
static bool allocation_entry_freed_checker(hash_map_entry_t *entry, void *context);
+static void *untracked_calloc(size_t size);
+
+static const allocator_t untracked_calloc_allocator = {
+ untracked_calloc,
+ free
+};
static hash_map_t *allocations;
static pthread_mutex_t lock;
if (allocations)
return;
- allocations = hash_map_new(ALLOCATION_HASH_MAP_SIZE, hash_function_knuth, NULL, free);
pthread_mutex_init(&lock, NULL);
+ allocations = hash_map_new_internal(
+ ALLOCATION_HASH_MAP_SIZE,
+ hash_function_knuth,
+ NULL,
+ free,
+ &untracked_calloc_allocator);
}
void allocation_tracker_reset(void) {
return true;
}
+
+static void *untracked_calloc(size_t size) {
+ return calloc(size, 1);
+}
#include <list.h>
#include <hash_map.h>
+#include "allocator.h"
+
struct hash_map_t;
typedef struct hash_map_bucket_t {
hash_index_fn hash_fn;
key_free_fn key_fn;
data_free_fn data_fn;
+ const allocator_t *allocator;
} hash_map_t;
+// Hidden constructor for list, only to be used by us.
+list_t *list_new_internal(list_free_cb callback, const allocator_t *zeroed_allocator);
+
static void bucket_free_(void *data);
static hash_map_entry_t *find_bucket_entry_(list_t *hash_bucket_list,
const void *key);
-// Returns a new, empty hash_map. Returns NULL if not enough memory could be allocated
-// for the hash_map structure. The returned hash_map must be freed with |hash_map_free|.
-// The |num_bucket| specifies the number of hashable buckets for the map and must not
-// be zero. The |hash_fn| specifies a hash function to be used and must not be NULL.
-// The |key_fn| and |data_fn| are called whenever a hash_map element is removed from
-// the hash_map. They can be used to release resources held by the hash_map element,
-// e.g. memory or file descriptor. |key_fn| and |data_fn| may be NULL if no cleanup
-// is necessary on element removal.
-hash_map_t * hash_map_new(size_t num_bucket, hash_index_fn hash_fn,
- key_free_fn key_fn, data_free_fn data_fn) {
+// Hidden constructor, only to be used by the allocation tracker. Behaves the same as
+// |hash_map_new|, except you get to specify the allocator.
+hash_map_t * hash_map_new_internal(
+ size_t num_bucket,
+ hash_index_fn hash_fn,
+ key_free_fn key_fn,
+ data_free_fn data_fn,
+ const allocator_t *zeroed_allocator) {
assert(hash_fn != NULL);
assert(num_bucket > 0);
+ assert(zeroed_allocator != NULL);
- hash_map_t *hash_map = calloc(sizeof(hash_map_t), 1);
+ hash_map_t *hash_map = zeroed_allocator->alloc(sizeof(hash_map_t));
if (hash_map == NULL)
return NULL;
hash_map->hash_fn = hash_fn;
hash_map->key_fn = key_fn;
hash_map->data_fn = data_fn;
+ hash_map->allocator = zeroed_allocator;
hash_map->num_bucket = num_bucket;
- hash_map->bucket = calloc(sizeof(hash_map_bucket_t), num_bucket);
+ hash_map->bucket = zeroed_allocator->alloc(sizeof(hash_map_bucket_t) * num_bucket);
if (hash_map->bucket == NULL) {
- free(hash_map);
+ zeroed_allocator->free(hash_map);
return NULL;
}
return hash_map;
}
+// Returns a new, empty hash_map. Returns NULL if not enough memory could be allocated
+// for the hash_map structure. The returned hash_map must be freed with |hash_map_free|.
+// The |num_bucket| specifies the number of hashable buckets for the map and must not
+// be zero. The |hash_fn| specifies a hash function to be used and must not be NULL.
+// The |key_fn| and |data_fn| are called whenever a hash_map element is removed from
+// the hash_map. They can be used to release resources held by the hash_map element,
+// e.g. memory or file descriptor. |key_fn| and |data_fn| may be NULL if no cleanup
+// is necessary on element removal.
+hash_map_t * hash_map_new(
+ size_t num_bucket,
+ hash_index_fn hash_fn,
+ key_free_fn key_fn,
+ data_free_fn data_fn) {
+ return hash_map_new_internal(num_bucket, hash_fn, key_fn, data_fn, &allocator_calloc);
+}
+
// Frees the hash_map. This function accepts NULL as an argument, in which case it
// behaves like a no-op.
void hash_map_free(hash_map_t *hash_map) {
if (hash_map == NULL)
return;
hash_map_clear(hash_map);
- free(hash_map->bucket);
- free(hash_map);
+ hash_map->allocator->free(hash_map->bucket);
+ hash_map->allocator->free(hash_map);
}
// Returns true if the hash_map is empty (has no elements), false otherwise.
hash_index_t hash_key = hash_map->hash_fn(key) % hash_map->num_bucket;
if (hash_map->bucket[hash_key].list == NULL) {
- hash_map->bucket[hash_key].list = list_new(bucket_free_);
+ hash_map->bucket[hash_key].list = list_new_internal(bucket_free_, hash_map->allocator);
if (hash_map->bucket[hash_key].list == NULL)
return false;
}
} else {
hash_map->hash_size++;
}
- hash_map_entry = calloc(sizeof(hash_map_entry_t), 1);
+ hash_map_entry = hash_map->allocator->alloc(sizeof(hash_map_entry_t));
if (hash_map_entry == NULL)
return false;
static void bucket_free_(void *data) {
assert(data != NULL);
hash_map_entry_t *hash_map_entry = (hash_map_entry_t *)data;
+ const hash_map_t *hash_map = hash_map_entry->hash_map;
- if (hash_map_entry->hash_map->key_fn)
- hash_map_entry->hash_map->key_fn((void *)hash_map_entry->key);
- if (hash_map_entry->hash_map->data_fn)
- hash_map_entry->hash_map->data_fn(hash_map_entry->data);
- free(hash_map_entry);
+ if (hash_map->key_fn)
+ hash_map->key_fn((void *)hash_map_entry->key);
+ if (hash_map->data_fn)
+ hash_map->data_fn(hash_map_entry->data);
+ hash_map->allocator->free(hash_map_entry);
}
static hash_map_entry_t * find_bucket_entry_(list_t *hash_bucket_list,
#include <assert.h>
+#include "allocator.h"
#include "list.h"
#include "osi.h"
list_node_t *tail;
size_t length;
list_free_cb free_cb;
+ const allocator_t *allocator;
} list_t;
static list_node_t *list_free_node_(list_t *list, list_node_t *node);
+// Hidden constructor, only to be used by the hash map for the allocation tracker.
+// Behaves the same as |list_new|, except you get to specify the allocator.
+list_t *list_new_internal(list_free_cb callback, const allocator_t *zeroed_allocator) {
+ list_t *list = (list_t *)zeroed_allocator->alloc(sizeof(list_t));
+ if (!list)
+ return NULL;
+
+ list->free_cb = callback;
+ list->allocator = zeroed_allocator;
+ return list;
+}
+
// Returns a new, empty list. Returns NULL if not enough memory could be allocated
// for the list structure. The returned list must be freed with |list_free|. The
// |callback| specifies a function to be called whenever a list element is removed
// memory or file descriptor. |callback| may be NULL if no cleanup is necessary on
// element removal.
list_t *list_new(list_free_cb callback) {
- list_t *list = (list_t *)calloc(sizeof(list_t), 1);
- if (list)
- list->free_cb = callback;
- return list;
+ return list_new_internal(callback, &allocator_calloc);
}
// Frees the list. This function accepts NULL as an argument, in which case it
// behaves like a no-op.
void list_free(list_t *list) {
- if (list != NULL)
- list_clear(list);
+ if (!list)
+ return;
- free(list);
+ list_clear(list);
+ list->allocator->free(list);
}
// Returns true if the list is empty (has no elements), false otherwise.
assert(node != NULL);
assert(data != NULL);
- list_node_t *node = (list_node_t *)malloc(sizeof(list_node_t));
+ list_node_t *node = (list_node_t *)list->allocator->alloc(sizeof(list_node_t));
if (!node)
return false;
assert(list != NULL);
assert(data != NULL);
- list_node_t *node = (list_node_t *)malloc(sizeof(list_node_t));
+ list_node_t *node = (list_node_t *)list->allocator->alloc(sizeof(list_node_t));
if (!node)
return false;
node->next = list->head;
assert(list != NULL);
assert(data != NULL);
- list_node_t *node = (list_node_t *)malloc(sizeof(list_node_t));
+ list_node_t *node = (list_node_t *)list->allocator->alloc(sizeof(list_node_t));
if (!node)
return false;
node->next = NULL;
if (list->free_cb)
list->free_cb(node->data);
- free(node);
+ list->allocator->free(node);
--list->length;
return next;