@@ -166,9 +166,17 @@ typedef struct mrb_heap_page {
166166struct mrb_heap_page *next;
167167struct mrb_heap_page *free_next;
168168mrb_bool old:1;
169+mrb_bool region:1; /* from contiguous region, not malloc */
169170RVALUE objects[MRB_HEAP_PAGE_SIZE];
170171} mrb_heap_page;
171172173+typedef struct mrb_heap_region {
174+struct mrb_heap_region *next;
175+uint8_t *base; /* start of user buffer */
176+size_t size; /* buffer size in bytes */
177+uint16_t page_count; /* pages carved from region */
178+} mrb_heap_region;
179+172180#define GC_STEP_SIZE 1024
173181174182/* white: 001 or 010, black: 100, gray: 000, red:111 */
@@ -283,6 +291,17 @@ static mrb_bool
283291heap_p(mrb_gc *gc, const struct RBasic *object)
284292{
285293mrb_heap_page* page;
294+mrb_heap_region *region;
295+296+/* fast path: check contiguous regions via arithmetic */
297+for (region = gc->regions; region; region = region->next) {
298+uintptr_t addr = (uintptr_t)object;
299+uintptr_t base = (uintptr_t)region->base;
300+uintptr_t end = base + (size_t)region->page_count * sizeof(mrb_heap_page);
301+if (addr >= base && addr < end) {
302+return TRUE;
303+ }
304+ }
286305287306page = gc->heaps;
288307while (page) {
@@ -306,9 +325,17 @@ mrb_object_dead_p(mrb_state *mrb, struct RBasic *object)
306325}
307326308327static void
309-add_heap(mrb_state *mrb, mrb_gc *gc)
328+link_heap_page(mrb_gc *gc, mrb_heap_page *page)
329+{
330+page->next = gc->heaps;
331+gc->heaps = page;
332+page->free_next = gc->free_heaps;
333+gc->free_heaps = page;
334+}
335+336+static void
337+init_heap_page(mrb_heap_page *page)
310338{
311-mrb_heap_page *page = (mrb_heap_page*)mrb_calloc(mrb, 1, sizeof(mrb_heap_page));
312339RVALUE *p, *e;
313340RVALUE *prev = NULL;
314341@@ -318,12 +345,50 @@ add_heap(mrb_state *mrb, mrb_gc *gc)
318345prev = p;
319346 }
320347page->freelist = prev;
348+}
321349322-page->next = gc->heaps;
323-gc->heaps = page;
350+static void
351+add_heap(mrb_state *mrb, mrb_gc *gc)
352+{
353+mrb_heap_page *page = (mrb_heap_page*)mrb_calloc(mrb, 1, sizeof(mrb_heap_page));
354+init_heap_page(page);
355+link_heap_page(gc, page);
356+}
324357325-page->free_next = gc->free_heaps;
326-gc->free_heaps = page;
358+MRB_API int
359+mrb_gc_add_region(mrb_state *mrb, void *start, size_t size)
360+{
361+mrb_gc *gc = &mrb->gc;
362+uint8_t *base = (uint8_t*)start;
363+mrb_heap_region *region;
364+uint16_t page_count;
365+uint16_t i;
366+367+/* align base to pointer size */
368+uintptr_t align = sizeof(void*);
369+uintptr_t offset = ((uintptr_t)base + align - 1) & ~(align - 1);
370+size -= (size_t)(offset - (uintptr_t)base);
371+base = (uint8_t*)offset;
372+373+page_count = (uint16_t)(size / sizeof(mrb_heap_page));
374+if (page_count == 0) return 0;
375+376+region = (mrb_heap_region*)mrb_malloc(mrb, sizeof(mrb_heap_region));
377+region->base = base;
378+region->size = size;
379+region->page_count = page_count;
380+region->next = gc->regions;
381+gc->regions = region;
382+383+/* carve pages from the contiguous buffer */
384+for (i = 0; i < page_count; i++) {
385+mrb_heap_page *page = (mrb_heap_page*)(base + (size_t)i * sizeof(mrb_heap_page));
386+memset(page, 0, sizeof(mrb_heap_page));
387+page->region = TRUE;
388+init_heap_page(page);
389+link_heap_page(gc, page);
390+ }
391+return page_count;
327392}
328393329394#define DEFAULT_GC_INTERVAL_RATIO 200
@@ -345,6 +410,7 @@ mrb_gc_init(mrb_state *mrb, mrb_gc *gc)
345410gc->current_white_part = GC_WHITE_A;
346411gc->heaps = NULL;
347412gc->free_heaps = NULL;
413+gc->regions = NULL;
348414add_heap(mrb, gc);
349415gc->interval_ratio = DEFAULT_GC_INTERVAL_RATIO;
350416gc->step_ratio = DEFAULT_GC_STEP_RATIO;
@@ -370,14 +436,25 @@ free_heap(mrb_state *mrb, mrb_gc *gc)
370436if (p->as.free.tt != MRB_TT_FREE)
371437obj_free(mrb, &p->as.basic, TRUE);
372438 }
373-mrb_free(mrb, tmp);
439+if (!tmp->region) {
440+mrb_free(mrb, tmp);
441+ }
374442 }
375443}
376444377445void
378446mrb_gc_destroy(mrb_state *mrb, mrb_gc *gc)
379447{
380448free_heap(mrb, gc);
449+/* free region descriptors (buffer memory belongs to the caller) */
450+ {
451+mrb_heap_region *region = gc->regions;
452+while (region) {
453+mrb_heap_region *next = region->next;
454+mrb_free(mrb, region);
455+region = next;
456+ }
457+ }
381458#ifndef MRB_GC_FIXED_ARENA
382459mrb_free(mrb, gc->arena);
383460#endif
@@ -1120,7 +1197,7 @@ incremental_sweep_phase(mrb_state *mrb, mrb_gc *gc, size_t limit)
11201197 }
1121119811221199/* free dead slot */
1123-if (dead_slot) {
1200+if (dead_slot && !page->region) {
11241201mrb_heap_page *next = page->next;
1125120211261203if (prev) prev->next = next;