[
  {
    "path": "MIT-LICENSE",
    "content": "Copyright (c) 2010 Narihiro Nakamura\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "#\nCC = gcc\nSRCS = gc.c\nBIN = gc\n\nall: clean gc\n\nclean:\n\trm -f gc\n\ngc: $(SRCS)\n\t$(CC) -g -o gc $(SRCS)\n\ngc_debug:\n\t$(CC) -g -DDO_DEBUG -O0 -o gc $(SRCS)\n\ntest: clean gc_debug\n\t./gc test\n"
  },
  {
    "path": "README",
    "content": "= mini gc\n\n= Description\n\nThis is a simple and minimum GC. The purpose of this project is education.\n\n= Algorithms\n\n* mark and sweep\n\n= Usage\n\n    $ make\n    $ ./gc test\n"
  },
  {
    "path": "gc.c",
    "content": "#ifdef DO_DEBUG\n#define DEBUG(exp) (exp)\n#else\n#define DEBUG(exp)\n#endif\n\n#ifndef DO_DEBUG\n#define NDEBUG\n#endif\n\n#include <stdio.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <assert.h>\n#include <unistd.h>\n#include <setjmp.h>\n#include \"gc.h\"\n\n/* ========================================================================== */\n/*  mini_gc_malloc                                                            */\n/* ========================================================================== */\n\ntypedef struct header {\n    size_t flags;\n    size_t size;\n    struct header *next_free;\n} Header;\n\ntypedef struct gc_heap {\n    Header *slot;\n    size_t size;\n} GC_Heap;\n\n#define TINY_HEAP_SIZE 0x4000\n#define PTRSIZE ((size_t) sizeof(void *))\n#define HEADER_SIZE ((size_t) sizeof(Header))\n#define HEAP_LIMIT 10000\n#define ALIGN(x,a) (((x) + (a - 1)) & ~(a - 1))\n#define NEXT_HEADER(x) ((Header *)((size_t)(x+1) + x->size))\n\n/* flags */\n#define FL_ALLOC 0x1\n#define FL_MARK 0x2\n#define FL_SET(x, f) (((Header *)x)->flags |= f)\n#define FL_UNSET(x, f) (((Header *)x)->flags &= ~(f))\n#define FL_TEST(x, f) (((Header *)x)->flags & f)\n\nstatic Header *free_list;\nstatic GC_Heap gc_heaps[HEAP_LIMIT];\nstatic size_t gc_heaps_used = 0;\n\n\nstatic Header *\nadd_heap(size_t req_size)\n{\n    void *p;\n    Header *align_p;\n\n    if (gc_heaps_used >= HEAP_LIMIT) {\n        fputs(\"OutOfMemory Error\", stderr);\n        abort();\n    }\n\n    if (req_size < TINY_HEAP_SIZE)\n        req_size = TINY_HEAP_SIZE;\n\n    if((p = sbrk(req_size + PTRSIZE + HEADER_SIZE)) == (void *)-1)\n        return NULL;\n\n    /* address alignment */\n    align_p = gc_heaps[gc_heaps_used].slot = (Header *)ALIGN((size_t)p, PTRSIZE);\n    req_size = gc_heaps[gc_heaps_used].size = req_size;\n    align_p->size = req_size;\n    align_p->next_free = align_p;\n    gc_heaps_used++;\n\n    return align_p;\n}\n\nstatic Header *\ngrow(size_t req_size)\n{\n    Header *cp, *up;\n\n    if (!(cp = add_heap(req_size)))\n        return NULL;\n\n    up = (Header *) cp;\n    mini_gc_free((void *)(up+1));\n    return free_list;\n}\n\nvoid *\nmini_gc_malloc(size_t req_size)\n{\n    Header *p, *prevp;\n    size_t do_gc = 0;\n\n    req_size = ALIGN(req_size, PTRSIZE);\n\n    if (req_size <= 0) {\n        return NULL;\n    }\n    if ((prevp = free_list) == NULL) {\n        if (!(p = add_heap(TINY_HEAP_SIZE))) {\n            return NULL;\n        }\n        prevp = free_list = p;\n    }\n    for (p = prevp->next_free; ; prevp = p, p = p->next_free) {\n        if (p->size >= req_size) {\n            if (p->size == req_size)\n                /* just fit */\n                prevp->next_free = p->next_free;\n            else {\n                /* too big */\n                p->size -= (req_size + HEADER_SIZE);\n                p = NEXT_HEADER(p);\n                p->size = req_size;\n            }\n            free_list = prevp;\n            FL_SET(p, FL_ALLOC);\n            return (void *)(p+1);\n        }\n        if (p == free_list) {\n            if (!do_gc) {\n                garbage_collect();\n                do_gc = 1;\n            }\n            else if ((p = grow(req_size)) == NULL)\n                return NULL;\n        }\n    }\n}\n\nvoid\nmini_gc_free(void *ptr)\n{\n    Header *target, *hit;\n\n    target = (Header *)ptr - 1;\n\n    /* search join point of target to free_list */\n    for (hit = free_list; !(target > hit && target < hit->next_free); hit = hit->next_free)\n        /* heap end? And hit(search)? */\n        if (hit >= hit->next_free &&\n            (target > hit || target < hit->next_free))\n            break;\n\n    if (NEXT_HEADER(target) == hit->next_free) {\n        /* merge */\n        target->size += (hit->next_free->size + HEADER_SIZE);\n        target->next_free = hit->next_free->next_free;\n    }\n    else {\n        /* join next free block */\n        target->next_free = hit->next_free;\n    }\n    if (NEXT_HEADER(hit) == target) {\n        /* merge */\n        hit->size += (target->size + HEADER_SIZE);\n        hit->next_free = target->next_free;\n    }\n    else {\n        /* join before free block */\n        hit->next_free = target;\n    }\n    free_list = hit;\n    target->flags = 0;\n}\n\n\n\n\n/* ========================================================================== */\n/*  mini_gc                                                                   */\n/* ========================================================================== */\n\nstruct root_range {\n    void * start;\n    void * end;\n};\n\n#define IS_MARKED(x) (FL_TEST(x, FL_ALLOC) && FL_TEST(x, FL_MARK))\n#define ROOT_RANGES_LIMIT 1000\n\nstatic struct root_range root_ranges[ROOT_RANGES_LIMIT];\nstatic size_t root_ranges_used = 0;\nstatic void * stack_start = NULL;\nstatic void * stack_end = NULL;\nstatic GC_Heap *hit_cache = NULL;\n\nstatic GC_Heap *\nis_pointer_to_heap(void *ptr)\n{\n    size_t i;\n\n    if (hit_cache &&\n        ((void *)hit_cache->slot) <= ptr &&\n        (size_t)ptr < (((size_t)hit_cache->slot) + hit_cache->size))\n        return hit_cache;\n\n    for (i = 0; i < gc_heaps_used;  i++) {\n        if ((((void *)gc_heaps[i].slot) <= ptr) &&\n            ((size_t)ptr < (((size_t)gc_heaps[i].slot) + gc_heaps[i].size))) {\n            hit_cache = &gc_heaps[i];\n            return &gc_heaps[i];\n        }\n    }\n    return NULL;\n}\n\nstatic Header *\nget_header(GC_Heap *gh, void *ptr)\n{\n    Header *p, *pend, *pnext;\n\n    pend = (Header *)(((size_t)gh->slot) + gh->size);\n    for (p = gh->slot; p < pend; p = pnext) {\n        pnext = NEXT_HEADER(p);\n        if ((void *)(p+1) <= ptr && ptr < (void *)pnext) {\n            return p;\n        }\n    }\n    return NULL;\n}\n\nvoid\ngc_init(void)\n{\n    long dummy;\n\n    /* referenced bdw-gc mark_rts.c */\n    dummy = 42;\n\n    /* check stack grow */\n    stack_start = ((void *)&dummy);\n}\n\nstatic void\nset_stack_end(void)\n{\n    void *tmp;\n    long dummy;\n\n    /* referenced bdw-gc mark_rts.c */\n    dummy = 42;\n\n    stack_end = (void *)&dummy;\n}\n\nstatic void gc_mark_range(void *start, void *end);\n\nstatic void\ngc_mark(void * ptr)\n{\n    GC_Heap *gh;\n    Header *hdr;\n\n    /* mark check */\n    if (!(gh = is_pointer_to_heap(ptr))) return;\n    if (!(hdr = get_header(gh, ptr))) return;\n    if (!FL_TEST(hdr, FL_ALLOC)) return;\n    if (FL_TEST(hdr, FL_MARK)) return;\n\n    /* marking */\n    FL_SET(hdr, FL_MARK);\n    DEBUG(printf(\"mark ptr : %p, header : %p\\n\", ptr, hdr));\n\n    /* mark children */\n    gc_mark_range((void *)(hdr+1), (void *)NEXT_HEADER(hdr));\n}\n\nstatic void\ngc_mark_range(void *start, void *end)\n{\n    void *p;\n\n    for (p = start; p < end; p++) {\n        gc_mark(*(void **)p);\n    }\n}\n\nstatic void\ngc_mark_register(void)\n{\n    jmp_buf env;\n    size_t i;\n    \n    setjmp(env);\n    for (i = 0; i < sizeof(env); i++) {\n        gc_mark(((void **)env)[i]);\n    }\n}\n\nstatic void\ngc_mark_stack(void)\n{\n    set_stack_end();\n    if (stack_start > stack_end) {\n        gc_mark_range(stack_end, stack_start);\n    }\n    else {\n        gc_mark_range(stack_start, stack_end);\n    }\n}\n\nstatic void\ngc_sweep(void)\n{\n    size_t i;\n    Header *p, *pend, *pnext;\n\n    for (i = 0; i < gc_heaps_used; i++) {\n        pend = (Header *)(((size_t)gc_heaps[i].slot) + gc_heaps[i].size);\n        for (p = gc_heaps[i].slot; p < pend; p = NEXT_HEADER(p)) {\n            if (FL_TEST(p, FL_ALLOC)) {\n                if (FL_TEST(p, FL_MARK)) {\n                    DEBUG(printf(\"mark unset : %p\\n\", p));\n                    FL_UNSET(p, FL_MARK);\n                }\n                else {\n                    mini_gc_free(p+1);\n                }\n            }\n        }\n    }\n}\n\nvoid\nadd_roots(void * start, void * end)\n{\n    void *tmp;\n    if (start > end) {\n        tmp = start;\n        start = end;\n        end = tmp;\n    }\n    root_ranges[root_ranges_used].start = start;\n    root_ranges[root_ranges_used].end = end;\n    root_ranges_used++;\n\n    if (root_ranges_used >= ROOT_RANGES_LIMIT) {\n        fputs(\"Root OverFlow\", stderr);\n        abort();\n    }\n}\n\nvoid\ngarbage_collect(void)\n{\n    size_t i;\n\n    /* marking machine context */\n    gc_mark_register();\n    gc_mark_stack();\n\n    /* marking roots */\n    for (i = 0; i < root_ranges_used; i++) {\n        gc_mark_range(root_ranges[i].start, root_ranges[i].end);\n    }\n\n    /* sweeping */\n    gc_sweep();\n}\n\n\n/* ========================================================================== */\n/*  test                                                                      */\n/* ========================================================================== */\n\nstatic void\ntest_mini_gc_malloc_free(void)\n{\n    void *p1, *p2, *p3;\n\n    /* malloc check */\n    p1 = (void *)mini_gc_malloc(10);\n    p2 = (void *)mini_gc_malloc(10);\n    p3 = (void *)mini_gc_malloc(10);\n    assert(((Header *)p1-1)->size == ALIGN(10, PTRSIZE));\n    assert(((Header *)p1-1)->flags == FL_ALLOC);\n    assert((Header *)(((size_t)(free_list+1)) + free_list->size) == ((Header *)p3-1));\n\n    /* free check */\n    mini_gc_free(p1);\n    mini_gc_free(p3);\n    mini_gc_free(p2);\n    assert(free_list->next_free == free_list);\n    assert((void *)gc_heaps[0].slot == (void *)free_list);\n    assert(gc_heaps[0].size == TINY_HEAP_SIZE);\n    assert(((Header *)p1-1)->flags == 0);\n\n    /* grow check */\n    p1 = mini_gc_malloc(TINY_HEAP_SIZE+80);\n    assert(gc_heaps_used == 2);\n    assert(gc_heaps[1].size == (TINY_HEAP_SIZE+80));\n    mini_gc_free(p1);\n}\n\nstatic void\ntest_garbage_collect(void) {\n    void *p;\n    p = mini_gc_malloc(100);\n    assert(FL_TEST((((Header *)p)-1), FL_ALLOC));\n    p = 0;\n    garbage_collect();\n}\n\nstatic void\ntest_garbage_collect_load_test(void) {\n    void *p;\n    int i;\n    for (i = 0; i < 2000; i++) {\n        p = mini_gc_malloc(100);\n    }\n    assert((((Header *)p)-1)->flags);\n    assert(stack_end != stack_start);\n}\n\nstatic void\ntest(void)\n{\n    gc_init();\n    test_mini_gc_malloc_free();\n    test_garbage_collect();\n    test_garbage_collect_load_test();\n}\n\n\nint\nmain(int argc, char **argv)\n{\n    if (argc == 2 && strcmp(argv[1], \"test\") == 0)  test();\n    return 0;\n}\n"
  },
  {
    "path": "gc.h",
    "content": "#ifndef _MINI_GC\n#define _MINI_GC\n\nvoid mini_gc_free(void *ptr);\nvoid * mini_gc_malloc(size_t req_size);\n\nvoid garbage_collect(void);\nvoid gc_init(void);\nvoid add_roots(void * start, void * end);\n\n#endif\n\n"
  }
]