diff options
Diffstat (limited to 'tests/clar/clar_libgit2_alloc.c')
-rw-r--r-- | tests/clar/clar_libgit2_alloc.c | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/tests/clar/clar_libgit2_alloc.c b/tests/clar/clar_libgit2_alloc.c new file mode 100644 index 0000000..e930379 --- /dev/null +++ b/tests/clar/clar_libgit2_alloc.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "clar_libgit2_alloc.h" + +static size_t bytes_available; + +/* + * The clar allocator uses a tagging mechanism for pointers that + * prepends the actual pointer's number bytes as `size_t`. + * + * First, this is required in order to be able to implement + * proper bookkeeping of allocated bytes in both `free` and + * `realloc`. + * + * Second, it may also be able to spot bugs that are + * otherwise hard to grasp, as the returned pointer cannot be + * free'd directly via free(3P). Instead, one is forced to use + * the tandem of `cl__malloc` and `cl__free`, as otherwise the + * code is going to crash hard. This is considered to be a + * feature, as it helps e.g. in finding cases where by accident + * malloc(3P) and free(3P) were used instead of git__malloc and + * git__free, respectively. + * + * The downside is obviously that each allocation grows by + * sizeof(size_t) bytes. As the allocator is for testing purposes + * only, this tradeoff is considered to be perfectly fine, + * though. + */ + +static void *cl__malloc(size_t len, const char *file, int line) +{ + char *ptr = NULL; + size_t alloclen; + + GIT_UNUSED(file); + GIT_UNUSED(line); + + if (len > bytes_available) + goto out; + + if (GIT_ADD_SIZET_OVERFLOW(&alloclen, len, sizeof(size_t)) || + (ptr = malloc(alloclen)) == NULL) + goto out; + memcpy(ptr, &len, sizeof(size_t)); + + bytes_available -= len; + +out: + return ptr ? ptr + sizeof(size_t) : NULL; +} + +static void cl__free(void *ptr) +{ + if (ptr) { + char *p = ptr; + size_t len; + memcpy(&len, p - sizeof(size_t), sizeof(size_t)); + free(p - sizeof(size_t)); + bytes_available += len; + } +} + +static void *cl__realloc(void *ptr, size_t size, const char *file, int line) +{ + size_t copybytes = 0; + char *p = ptr; + void *new; + + if (p) + memcpy(©bytes, p - sizeof(size_t), sizeof(size_t)); + + if (copybytes > size) + copybytes = size; + + if ((new = cl__malloc(size, file, line)) == NULL) + goto out; + + if (p) { + memcpy(new, p, copybytes); + cl__free(p); + } + +out: + return new; +} + +void cl_alloc_limit(size_t bytes) +{ + git_allocator alloc; + + alloc.gmalloc = cl__malloc; + alloc.grealloc = cl__realloc; + alloc.gfree = cl__free; + + git_allocator_setup(&alloc); + + bytes_available = bytes; +} + +void cl_alloc_reset(void) +{ + git_allocator stdalloc; + git_stdalloc_init_allocator(&stdalloc); + git_allocator_setup(&stdalloc); +} |