From 0ae8ab7b4a1494e14ba254124937a80abebd86e8 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Sat, 5 Sep 2026 11:26:40 -0500 Subject: [PATCH] Add janet_array_{get/set} for easier bounds checked access. These functions also maintain barrier invariant(s). Also extend the primtive collection optimization to tuples, for which it is much simpler. For tuples, only maintain the invariant lazily. If on first collection, all elements are primitive, we flag as such and never check again. --- src/core/array.c | 13 +++++++++++++ src/core/gc.c | 11 +++++++++-- src/include/janet.h | 3 +++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/core/array.c b/src/core/array.c index 9812a508..6736ade7 100644 --- a/src/core/array.c +++ b/src/core/array.c @@ -121,6 +121,19 @@ void janet_array_setcount(JanetArray *array, int32_t count) { array->count = count; } +/* Set an element in the array with assert bounds check. */ +void janet_array_set(JanetArray *array, int32_t index, Janet x) { + janet_assert(index >= 0 && index < array->count, "bad array index"); + array->data[index] = x; + array_elem_primcheck(array, x); +} + +/* For symmetry with janet_array_set with assert bounds check. Direct indexing is prefered for perf. */ +Janet janet_array_get(JanetArray *array, int32_t index) { + janet_assert(index >= 0 && index < array->count, "bad array index"); + return array->data[index]; +} + /* Push a value to the top of the array */ void janet_array_push(JanetArray *array, Janet x) { if (array->count == INT32_MAX) { diff --git a/src/core/gc.c b/src/core/gc.c index 86c1df8f..6ad92530 100644 --- a/src/core/gc.c +++ b/src/core/gc.c @@ -217,8 +217,15 @@ recur: static void janet_mark_tuple(const Janet *tuple) { if (janet_gc_reachable(janet_tuple_head(tuple))) return; - janet_gc_mark(janet_tuple_head(tuple)); - janet_mark_many(tuple, janet_tuple_length(tuple)); + JanetTupleHead *head = janet_tuple_head(tuple); + janet_gc_mark((JanetGCObject *) head); + if (head->gc.flags & JANET_TUPLE_FLAG_PRIMITIVES) { + /* TODO - debug routine to check array for non-primitives. If we find any, assert */ + return; + } + if (janet_mark_many(tuple, janet_tuple_length(tuple))) { + head->gc.flags |= JANET_TUPLE_FLAG_PRIMITIVES; + } } /* Helper to mark function environments */ diff --git a/src/include/janet.h b/src/include/janet.h index 774bc6d4..1d37ecba 100644 --- a/src/include/janet.h +++ b/src/include/janet.h @@ -1113,6 +1113,7 @@ struct JanetAbstractHead { /* Mark an array as containing only primitive types. Hint for the garbage collector */ #define JANET_ARRAY_FLAG_PRIMITIVES ((int32_t)0x10000) +#define JANET_TUPLE_FLAG_PRIMITIVES ((int32_t)0x20000) /* Don't collide with JANET_TUPLE_FLAG_BRACKETCTOR */ /* Source mapping structure for a bytecode instruction */ struct JanetSourceMapping { @@ -1781,6 +1782,8 @@ JANET_API void janet_array_setcount(JanetArray *array, int32_t count); JANET_API void janet_array_push(JanetArray *array, Janet x); JANET_API Janet janet_array_pop(JanetArray *array); JANET_API Janet janet_array_peek(JanetArray *array); +JANET_API void janet_array_set(JanetArray *array, int32_t index, Janet x); +JANET_API Janet janet_array_get(JanetArray *array, int32_t index); /* Buffer functions */ #define JANET_BUFFER_FLAG_NO_REALLOC 0x10000