summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Raible <raible@gmail.com>2024-02-06 10:36:00 -0800
committerEric Raible <raible@gmail.com>2024-02-06 20:01:18 -0800
commitbe6584e2b98a8e9151fd22cc7f045621c6b450f2 (patch)
treeaffa228ab31157eaf3de914b32a97ec9c00a4c2b
parent105d70f7b4ce7706652853ece2e35ea8c774005f (diff)
Allow use of a custom allocator in libtcc
When using libtcc it's reasonable to be able to use the application's memory allocator for all allocations, including tcc_new(), and including #define MEM_DEBUG Ideally the allocator would be stored in the TCCState, like TCCErrorFunc. That would imply a new API tcc_new_with_allocator(), but more importantly would require all uses of tcc_malloc(x) to be changed to s->tcc_malloc(x). That's a non-starter in my book. Instead I refactored the memory management code so that all allocations flow through tcc_realloc(). Which simply calls a function pointer, the default value of which is the previous tcc_realloc(). It then becomess trivial to install a new allocator with the new function: LIBTCCAPI void tcc_set_realloc(TCCReallocFunc realloc); The resulting code adds the trivial cost of an additional function call per allocation/free. It also doesn't distinguish between malloc failure and realloc failure, but since both just fprintf then exit() that seems unimportant to me. On the plus side the preprocessor magic is much more clear. The diffs don't hightlight that, but take a look at the result to see if you agree. All tests passed on my x86 linux box.
-rw-r--r--libtcc.c103
-rw-r--r--libtcc.h7
-rw-r--r--tcc.h5
-rw-r--r--tests/abitest.c1
4 files changed, 73 insertions, 43 deletions
diff --git a/libtcc.c b/libtcc.c
index 35002b6..d1aa998 100644
--- a/libtcc.c
+++ b/libtcc.c
@@ -245,30 +245,68 @@ ST_FUNC char *tcc_load_text(int fd)
/********************************************************/
/* memory management */
+/* we'll need the actual versions for a minute */
#undef free
-#undef malloc
#undef realloc
-void mem_error(const char *msg)
+static void *default_reallocator(void *ptr, unsigned long size)
{
- fprintf(stderr, "%s\n", msg);
- exit (1);
+ void *ptr1;
+ if (size == 0) {
+ free(ptr);
+ ptr1 = NULL;
+ }
+ else {
+ ptr1 = realloc(ptr, size);
+ if (!ptr1) {
+ fprintf(stderr, "memory full\n");
+ exit (1);
+ }
+ }
+ return ptr1;
+}
+
+static void libc_free(void *ptr)
+{
+ free(ptr);
}
-#ifndef MEM_DEBUG
+#define free(p) use_tcc_free(p)
+#define realloc(p, s) use_tcc_realloc(p, s)
+
+/* global so that every tcc_alloc()/tcc_free() call doesn't need to be changed */
+static TCCReallocFunc reallocator = default_reallocator;
+
+LIBTCCAPI void tcc_set_realloc(TCCReallocFunc realloc)
+{
+ reallocator = realloc;
+}
+
+LIBTCCAPI TCCReallocFunc tcc_get_realloc()
+{
+ return reallocator;
+}
+
+/* in case MEM_DEBUG is #defined */
+#undef tcc_free
+#undef tcc_malloc
+#undef tcc_realloc
+#undef tcc_mallocz
+#undef tcc_strdup
PUB_FUNC void tcc_free(void *ptr)
{
- free(ptr);
+ reallocator(ptr, 0);
}
PUB_FUNC void *tcc_malloc(unsigned long size)
{
- void *ptr;
- ptr = malloc(size ? size : 1);
- if (!ptr)
- mem_error("memory full (malloc)");
- return ptr;
+ return reallocator(0, size);
+}
+
+PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size)
+{
+ return reallocator(ptr, size);
}
PUB_FUNC void *tcc_mallocz(unsigned long size)
@@ -280,21 +318,6 @@ PUB_FUNC void *tcc_mallocz(unsigned long size)
return ptr;
}
-PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size)
-{
- void *ptr1;
- if (size == 0) {
- free(ptr);
- ptr1 = NULL;
- }
- else {
- ptr1 = realloc(ptr, size);
- if (!ptr1)
- mem_error("memory full (realloc)");
- }
- return ptr1;
-}
-
PUB_FUNC char *tcc_strdup(const char *str)
{
char *ptr;
@@ -303,7 +326,7 @@ PUB_FUNC char *tcc_strdup(const char *str)
return ptr;
}
-#else
+#ifdef MEM_DEBUG
#define MEM_DEBUG_MAGIC1 0xFEEDDEB1
#define MEM_DEBUG_MAGIC2 0xFEEDDEB2
@@ -356,10 +379,7 @@ PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line)
int ofs;
mem_debug_header_t *header;
- header = malloc(sizeof(mem_debug_header_t) + size);
- if (!header)
- mem_error("memory full (malloc)");
-
+ header = tcc_malloc(sizeof(mem_debug_header_t) + size);
header->magic1 = MEM_DEBUG_MAGIC1;
header->magic2 = MEM_DEBUG_MAGIC2;
header->size = size;
@@ -400,7 +420,7 @@ PUB_FUNC void tcc_free_debug(void *ptr)
if (header == mem_debug_chain)
mem_debug_chain = header->next;
POST_SEM(&mem_sem);
- free(header);
+ tcc_free(header);
}
PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line)
@@ -422,9 +442,7 @@ PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file
WAIT_SEM(&mem_sem);
mem_cur_size -= header->size;
mem_debug_chain_update = (header == mem_debug_chain);
- header = realloc(header, sizeof(mem_debug_header_t) + size);
- if (!header)
- mem_error("memory full (realloc)");
+ header = tcc_realloc(header, sizeof(mem_debug_header_t) + size);
header->size = size;
write32le(MEM_DEBUG_CHECK3(header), MEM_DEBUG_MAGIC3);
if (header->next)
@@ -473,6 +491,13 @@ PUB_FUNC void tcc_memcheck(int d)
POST_SEM(&mem_sem);
}
+/* restore the debug versions */
+#define tcc_free(ptr) tcc_free_debug(ptr)
+#define tcc_malloc(size) tcc_malloc_debug(size, __FILE__, __LINE__)
+#define tcc_mallocz(size) tcc_mallocz_debug(size, __FILE__, __LINE__)
+#define tcc_realloc(ptr,size) tcc_realloc_debug(ptr, size, __FILE__, __LINE__)
+#define tcc_strdup(str) tcc_strdup_debug(str, __FILE__, __LINE__)
+
#endif /* MEM_DEBUG */
#ifdef _WIN32
@@ -487,17 +512,13 @@ ST_FUNC int normalized_PATHCMP(const char *f1, const char *f2)
if (!!(p1 = realpath(f1, NULL))) {
if (!!(p2 = realpath(f2, NULL))) {
ret = PATHCMP(p1, p2);
- free(p2); /* using original free */
+ libc_free(p2); /* realpath() requirement */
}
- free(p1);
+ libc_free(p1);
}
return ret;
}
-#define free(p) use_tcc_free(p)
-#define malloc(s) use_tcc_malloc(s)
-#define realloc(p, s) use_tcc_realloc(p, s)
-
/********************************************************/
/* dynarrays */
diff --git a/libtcc.h b/libtcc.h
index 8450ba8..9261321 100644
--- a/libtcc.h
+++ b/libtcc.h
@@ -14,6 +14,13 @@ struct TCCState;
typedef struct TCCState TCCState;
typedef void (*TCCErrorFunc)(void *opaque, const char *msg);
+typedef void *(*TCCReallocFunc)(void *ptr, size_t size);
+
+/* to be used for all allocation (including tcc_new()), otherwise malloc(), realloc(), free() */
+LIBTCCAPI void tcc_set_realloc(TCCReallocFunc realloc);
+
+/* return current allocator */
+LIBTCCAPI TCCReallocFunc tcc_get_realloc();
/* create a new TCC compilation context */
LIBTCCAPI TCCState *tcc_new(void);
diff --git a/tcc.h b/tcc.h
index c36f2a6..30bfa38 100644
--- a/tcc.h
+++ b/tcc.h
@@ -1221,13 +1221,14 @@ ST_FUNC char *pstrncpy(char *out, const char *in, size_t num);
PUB_FUNC char *tcc_basename(const char *name);
PUB_FUNC char *tcc_fileextension (const char *name);
-#ifndef MEM_DEBUG
+/* all allocations - even MEM_DEBUG - use these */
PUB_FUNC void tcc_free(void *ptr);
PUB_FUNC void *tcc_malloc(unsigned long size);
PUB_FUNC void *tcc_mallocz(unsigned long size);
PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size);
PUB_FUNC char *tcc_strdup(const char *str);
-#else
+
+#ifdef MEM_DEBUG
#define tcc_free(ptr) tcc_free_debug(ptr)
#define tcc_malloc(size) tcc_malloc_debug(size, __FILE__, __LINE__)
#define tcc_mallocz(size) tcc_mallocz_debug(size, __FILE__, __LINE__)
diff --git a/tests/abitest.c b/tests/abitest.c
index 4a192bd..7a67bf7 100644
--- a/tests/abitest.c
+++ b/tests/abitest.c
@@ -1,3 +1,4 @@
+#include <unistd.h>
#include <libtcc.h>
#include <stdlib.h>
#include <stdio.h>