array.c: restore arena around heap sort per-call protections

heapify() and heap_delete_root() call mrb_gc_protect() to guard a
C-local mrb_value across potential GC points inside sort_cmp().
They were missing the matching mrb_gc_arena_save/restore pair, so
each call leaked one arena slot.  For O(n log n) heapify calls under
MRB_GC_FIXED_ARENA this overflows the 100-slot arena and raises
NoMemoryError, e.g. during Array#repeated_permutation tests.

Wrap each function body with mrb_gc_arena_save/restore, matching
the pattern already used in insertion_sort() above.

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-04-15 08:59:07 +09:00
parent 449040400a
commit bbf7c43c37
+4
View File
@@ -2290,6 +2290,7 @@ sort_cmp(mrb_state *mrb, mrb_value ary, mrb_value a_val, mrb_value b_val, mrb_va
static void
heapify(mrb_state *mrb, mrb_value ary, mrb_value *a, mrb_int index, mrb_int size, mrb_value blk)
{
int ai = mrb_gc_arena_save(mrb);
mrb_value val = a[index]; /* save root to hole */
mrb_gc_protect(mrb, val);
@@ -2308,6 +2309,7 @@ heapify(mrb_state *mrb, mrb_value ary, mrb_value *a, mrb_int index, mrb_int size
index = child;
}
a[index] = val; /* place saved value */
mrb_gc_arena_restore(mrb, ai);
}
/* Floyd's bottom-up heap deletion: sift the hole down to a leaf without
@@ -2317,6 +2319,7 @@ heapify(mrb_state *mrb, mrb_value ary, mrb_value *a, mrb_int index, mrb_int size
static void
heap_delete_root(mrb_state *mrb, mrb_value ary, mrb_value *a, mrb_int size, mrb_value blk)
{
int ai = mrb_gc_arena_save(mrb);
/* a[0] already holds the value to be re-inserted (set by caller) */
mrb_value last = a[0];
mrb_gc_protect(mrb, last);
@@ -2347,6 +2350,7 @@ heap_delete_root(mrb_state *mrb, mrb_value ary, mrb_value *a, mrb_int size, mrb_
hole = parent;
}
a[hole] = last;
mrb_gc_arena_restore(mrb, ai);
}
static void