diff --git a/.gitignore b/.gitignore index 511a24f4..123cf5fb 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ xxd.exe # VSCode .vs .clangd +.cache # Swap files *.swp diff --git a/examples/weak-tables.janet b/examples/weak-tables.janet index 6d892fe7..b4288b60 100644 --- a/examples/weak-tables.janet +++ b/examples/weak-tables.janet @@ -1,6 +1,6 @@ -(def weak-k (table/new 10 :k)) -(def weak-v (table/new 10 :v)) -(def weak-kv (table/new 10 :kv)) +(def weak-k (table/weak-keys 10)) +(def weak-v (table/weak-values 10)) +(def weak-kv (table/weak 10)) (put weak-kv (gensym) 10) (put weak-kv :hello :world) diff --git a/src/core/array.c b/src/core/array.c index 3923a4e1..32ee5dfa 100644 --- a/src/core/array.c +++ b/src/core/array.c @@ -30,9 +30,7 @@ #include -/* Creates a new array */ -JanetArray *janet_array(int32_t capacity) { - JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray)); +static void janet_array_impl(JanetArray *array, int32_t capacity) { Janet *data = NULL; if (capacity > 0) { janet_vm.next_collection += capacity * sizeof(Janet); @@ -44,6 +42,19 @@ JanetArray *janet_array(int32_t capacity) { array->count = 0; array->capacity = capacity; array->data = data; +} + +/* Creates a new array */ +JanetArray *janet_array(int32_t capacity) { + JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY, sizeof(JanetArray)); + janet_array_impl(array, capacity); + return array; +} + +/* Creates a new array with weak references */ +JanetArray *janet_array_weak(int32_t capacity) { + JanetArray *array = janet_gcalloc(JANET_MEMORY_ARRAY_WEAK, sizeof(JanetArray)); + janet_array_impl(array, capacity); return array; } @@ -132,6 +143,15 @@ JANET_CORE_FN(cfun_array_new, return janet_wrap_array(array); } +JANET_CORE_FN(cfun_array_weak, + "(array/weak capacity)", + "Creates a new empty array with a pre-allocated capacity and support for weak references. Similar to `array/new`.") { + janet_fixarity(argc, 1); + int32_t cap = janet_getinteger(argv, 0); + JanetArray *array = janet_array_weak(cap); + return janet_wrap_array(array); +} + JANET_CORE_FN(cfun_array_new_filled, "(array/new-filled count &opt value)", "Creates a new array of `count` elements, all set to `value`, which defaults to nil. Returns the new array.") { @@ -352,6 +372,7 @@ JANET_CORE_FN(cfun_array_clear, void janet_lib_array(JanetTable *env) { JanetRegExt array_cfuns[] = { JANET_CORE_REG("array/new", cfun_array_new), + JANET_CORE_REG("array/weak", cfun_array_weak), JANET_CORE_REG("array/new-filled", cfun_array_new_filled), JANET_CORE_REG("array/fill", cfun_array_fill), JANET_CORE_REG("array/pop", cfun_array_pop), diff --git a/src/core/gc.c b/src/core/gc.c index 473e6f6e..9c9be5bb 100644 --- a/src/core/gc.c +++ b/src/core/gc.c @@ -164,7 +164,9 @@ static void janet_mark_array(JanetArray *array) { if (janet_gc_reachable(array)) return; janet_gc_mark(array); - janet_mark_many(array->data, array->count); + if (janet_gc_type((JanetGCObject *) array) == JANET_MEMORY_ARRAY) { + janet_mark_many(array->data, array->count); + } } static void janet_mark_table(JanetTable *table) { @@ -392,23 +394,32 @@ void janet_sweep() { if (current->flags & (JANET_MEM_REACHABLE | JANET_MEM_DISABLED)) { /* Check for dead references */ enum JanetMemoryType type = janet_gc_type(current); - JanetTable *table = (JanetTable *) current; - int check_values = (type == JANET_MEMORY_TABLE_WEAKV) || (type == JANET_MEMORY_TABLE_WEAKKV); - int check_keys = (type == JANET_MEMORY_TABLE_WEAKK) || (type == JANET_MEMORY_TABLE_WEAKKV); - JanetKV *end = table->data + table->capacity; - JanetKV *kvs = table->data; - while (kvs < end) { - int drop = 0; - if (check_keys && !janet_check_liveref(kvs->key)) drop = 1; - if (check_values && !janet_check_liveref(kvs->value)) drop = 1; - if (drop) { - /* Inlined from janet_table_remove without search */ - table->count--; - table->deleted++; - kvs->key = janet_wrap_nil(); - kvs->value = janet_wrap_false(); + if (type == JANET_MEMORY_ARRAY_WEAK) { + JanetArray *array = (JanetArray *) current; + for (uint32_t i = 0; i < (uint32_t) array->count; i++) { + if (!janet_check_liveref(array->data[i])) { + array->data[i] = janet_wrap_nil(); + } + } + } else { + JanetTable *table = (JanetTable *) current; + int check_values = (type == JANET_MEMORY_TABLE_WEAKV) || (type == JANET_MEMORY_TABLE_WEAKKV); + int check_keys = (type == JANET_MEMORY_TABLE_WEAKK) || (type == JANET_MEMORY_TABLE_WEAKKV); + JanetKV *end = table->data + table->capacity; + JanetKV *kvs = table->data; + while (kvs < end) { + int drop = 0; + if (check_keys && !janet_check_liveref(kvs->key)) drop = 1; + if (check_values && !janet_check_liveref(kvs->value)) drop = 1; + if (drop) { + /* Inlined from janet_table_remove without search */ + table->count--; + table->deleted++; + kvs->key = janet_wrap_nil(); + kvs->value = janet_wrap_false(); + } + kvs++; } - kvs++; } } current = next; diff --git a/src/core/gc.h b/src/core/gc.h index 6c7acabb..e5f99884 100644 --- a/src/core/gc.h +++ b/src/core/gc.h @@ -59,7 +59,8 @@ enum JanetMemoryType { JANET_MEMORY_THREADED_ABSTRACT, JANET_MEMORY_TABLE_WEAKK, JANET_MEMORY_TABLE_WEAKV, - JANET_MEMORY_TABLE_WEAKKV + JANET_MEMORY_TABLE_WEAKKV, + JANET_MEMORY_ARRAY_WEAK }; /* To allocate collectable memory, one must call janet_alloc, initialize the memory, diff --git a/src/core/table.c b/src/core/table.c index 0d370d11..d6174027 100644 --- a/src/core/table.c +++ b/src/core/table.c @@ -309,23 +309,49 @@ JanetTable *janet_table_proto_flatten(JanetTable *t) { /* C Functions */ JANET_CORE_FN(cfun_table_new, - "(table/new capacity &opt weak)", + "(table/new capacity)", "Creates a new empty table with pre-allocated memory " "for `capacity` entries. This means that if one knows the number of " "entries going into a table on creation, extra memory allocation " - "can be avoided. Optionally provide a keyword flags `:kv` to create a table with " - "weak referenecs to keys, values, or both. " + "can be avoided. " "Returns the new table.") { - janet_arity(argc, 1, 2); + janet_fixarity(argc, 1); int32_t cap = janet_getnat(argv, 0); - if (argc == 1) { - return janet_wrap_table(janet_table(cap)); - } + return janet_wrap_table(janet_table(cap)); +} +/* uint32_t flags = janet_getflags(argv, 1, "kv"); if (flags == 0) return janet_wrap_table(janet_table(cap)); if (flags == 1) return janet_wrap_table(janet_table_weakk(cap)); if (flags == 2) return janet_wrap_table(janet_table_weakv(cap)); return janet_wrap_table(janet_table_weakkv(cap)); + */ + +JANET_CORE_FN(cfun_table_weak, + "(table/weak capacity)", + "Creates a new empty table with weak references to keys and values. Similar to `table/new`. " + "Returns the new table.") { + janet_fixarity(argc, 1); + int32_t cap = janet_getnat(argv, 0); + return janet_wrap_table(janet_table_weakkv(cap)); +} + +JANET_CORE_FN(cfun_table_weak_keys, + "(table/weak-keys capacity)", + "Creates a new empty table with weak references to keys and normal references to values. Similar to `table/new`. " + "Returns the new table.") { + janet_fixarity(argc, 1); + int32_t cap = janet_getnat(argv, 0); + return janet_wrap_table(janet_table_weakk(cap)); +} + +JANET_CORE_FN(cfun_table_weak_values, + "(table/weak-values capacity)", + "Creates a new empty table with normal references to keys and weak references to values. Similar to `table/new`. " + "Returns the new table.") { + janet_fixarity(argc, 1); + int32_t cap = janet_getnat(argv, 0); + return janet_wrap_table(janet_table_weakv(cap)); } JANET_CORE_FN(cfun_table_getproto, @@ -401,6 +427,9 @@ JANET_CORE_FN(cfun_table_proto_flatten, void janet_lib_table(JanetTable *env) { JanetRegExt table_cfuns[] = { JANET_CORE_REG("table/new", cfun_table_new), + JANET_CORE_REG("table/weak", cfun_table_weak), + JANET_CORE_REG("table/weak-keys", cfun_table_weak_keys), + JANET_CORE_REG("table/weak-values", cfun_table_weak_values), JANET_CORE_REG("table/to-struct", cfun_table_tostruct), JANET_CORE_REG("table/getproto", cfun_table_getproto), JANET_CORE_REG("table/setproto", cfun_table_setproto), diff --git a/src/include/janet.h b/src/include/janet.h index e9017259..24e3b25d 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -1590,6 +1590,7 @@ JANET_API double janet_rng_double(JanetRNG *rng); /* Array functions */ JANET_API JanetArray *janet_array(int32_t capacity); +JANET_API JanetArray *janet_array_weak(int32_t capacity); JANET_API JanetArray *janet_array_n(const Janet *elements, int32_t n); JANET_API void janet_array_ensure(JanetArray *array, int32_t capacity, int32_t growth); JANET_API void janet_array_setcount(JanetArray *array, int32_t count);