1
0
mirror of https://github.com/janet-lang/janet synced 2024-11-28 19:19:53 +00:00

Tables created via table_init cannot leak memory.

Before, if Janet paniced without calling table_deinit
on a table created via table_init, Janet leaked memory.
This changes tables so that tables created via table_init
us scratch memory for auto cleanup instead of normal
malloc/free.
This commit is contained in:
Calvin Rose 2019-06-05 17:08:49 -04:00
parent efb2ab06cb
commit 64a80c57e3
2 changed files with 45 additions and 12 deletions

View File

@ -265,7 +265,7 @@ static void janet_deinit_block(JanetGCObject *mem) {
free(((JanetArray *) mem)->data); free(((JanetArray *) mem)->data);
break; break;
case JANET_MEMORY_TABLE: case JANET_MEMORY_TABLE:
janet_table_deinit((JanetTable *) mem); free(((JanetTable *) mem)->data);
break; break;
case JANET_MEMORY_FIBER: case JANET_MEMORY_FIBER:
free(((JanetFiber *)mem)->data); free(((JanetFiber *)mem)->data);

View File

@ -27,14 +27,32 @@
#include <math.h> #include <math.h>
#endif #endif
/* Initialize a table */ #define JANET_TABLE_FLAG_STACK 0x10000
JanetTable *janet_table_init(JanetTable *table, int32_t capacity) {
static void *janet_memalloc_empty_local(int32_t count) {
int32_t i;
void *mem = janet_smalloc(count * sizeof(JanetKV));
JanetKV *mmem = (JanetKV *)mem;
for (i = 0; i < count; i++) {
JanetKV *kv = mmem + i;
kv->key = janet_wrap_nil();
kv->value = janet_wrap_nil();
}
return mem;
}
static JanetTable *janet_table_init_impl(JanetTable *table, int32_t capacity, int stackalloc) {
JanetKV *data; JanetKV *data;
capacity = janet_tablen(capacity); capacity = janet_tablen(capacity);
if (stackalloc) table->gc.flags = JANET_TABLE_FLAG_STACK;
if (capacity) { if (capacity) {
data = (JanetKV *) janet_memalloc_empty(capacity); if (stackalloc) {
if (NULL == data) { data = janet_memalloc_empty_local(capacity);
JANET_OUT_OF_MEMORY; } else {
data = (JanetKV *) janet_memalloc_empty(capacity);
if (NULL == data) {
JANET_OUT_OF_MEMORY;
}
} }
table->data = data; table->data = data;
table->capacity = capacity; table->capacity = capacity;
@ -48,15 +66,20 @@ JanetTable *janet_table_init(JanetTable *table, int32_t capacity) {
return table; return table;
} }
/* Initialize a table */
JanetTable *janet_table_init(JanetTable *table, int32_t capacity) {
return janet_table_init_impl(table, capacity, 1);
}
/* Deinitialize a table */ /* Deinitialize a table */
void janet_table_deinit(JanetTable *table) { void janet_table_deinit(JanetTable *table) {
free(table->data); janet_sfree(table->data);
} }
/* Create a new table */ /* Create a new table */
JanetTable *janet_table(int32_t capacity) { JanetTable *janet_table(int32_t capacity) {
JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable)); JanetTable *table = janet_gcalloc(JANET_MEMORY_TABLE, sizeof(JanetTable));
return janet_table_init(table, capacity); return janet_table_init_impl(table, capacity, 0);
} }
/* Find the bucket that contains the given key. Will also return /* Find the bucket that contains the given key. Will also return
@ -68,9 +91,15 @@ JanetKV *janet_table_find(JanetTable *t, Janet key) {
/* Resize the dictionary table. */ /* Resize the dictionary table. */
static void janet_table_rehash(JanetTable *t, int32_t size) { static void janet_table_rehash(JanetTable *t, int32_t size) {
JanetKV *olddata = t->data; JanetKV *olddata = t->data;
JanetKV *newdata = (JanetKV *) janet_memalloc_empty(size); JanetKV *newdata;
if (NULL == newdata) { int islocal = t->gc.flags & JANET_TABLE_FLAG_STACK;
JANET_OUT_OF_MEMORY; if (islocal) {
newdata = (JanetKV *) janet_memalloc_empty_local(size);
} else {
newdata = (JanetKV *) janet_memalloc_empty(size);
if (NULL == newdata) {
JANET_OUT_OF_MEMORY;
}
} }
int32_t i, oldcapacity; int32_t i, oldcapacity;
oldcapacity = t->capacity; oldcapacity = t->capacity;
@ -84,7 +113,11 @@ static void janet_table_rehash(JanetTable *t, int32_t size) {
*newkv = *kv; *newkv = *kv;
} }
} }
free(olddata); if (islocal) {
janet_sfree(olddata);
} else {
free(olddata);
}
} }
/* Get a value out of the table */ /* Get a value out of the table */