--- /dev/null
+#include <liba.h>
+#include <setjmp.h>
+#ifdef __APPLE__
+#include <mach-o/getsect.h>
+#endif
+
+/* the real main lives here */
+#undef main
+
+/* TODO:
+ * Process all increments *then* all decrements
+*/
+
+#define THOLD 0.65
+#define DELETED ((void*)(intptr_t)-1)
+#define NUM_SLOTS 4u
+#define LOG_SIZE 1023u
+#define NUM_PRIMES (sizeof(Primes)/sizeof(unsigned int))
+
+enum {
+ INCREMENT = 0,
+ DECREMENT = 1,
+};
+
+typedef struct {
+ uintptr_t index;
+ uintptr_t log[LOG_SIZE];
+} buffer_t;
+
+typedef struct {
+ uintptr_t refcount;
+ uintptr_t data[];
+} object_t;
+
+typedef struct {
+ void* slots[NUM_SLOTS];
+} bucket_t;
+
+static void obj_addref(object_t* obj);
+static void obj_delref(object_t* obj);
+static void gc_collect(void);
+static void log_add(object_t* obj, int op);
+
+typedef struct {
+ size_t size;
+ size_t nslots;
+ size_t thold;
+ void** slots;
+} hash_t;
+
+static void hash_init(hash_t* hash, int nslots);
+static void hash_grow(hash_t* hash);
+static void hash_add(hash_t* hash, void* entry);
+static int hash_del(hash_t* hash, void* entry);
+
+static intptr_t* Stack_Bot = 0;
+static buffer_t Log = {0};
+static hash_t ZCT;
+static unsigned int Primes[] = {
+ 1543, 3079, 6151, 12289, 24593,
+ 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
+ 12582917, 25165843, 50331653, 100663319, 201326611, 402653189,
+ 805306457, 1610612741
+};
+
+/* Public Routines
+ ***************************************/
+
+void* gc_alloc(size_t sz)
+{
+ sz = (sz / sizeof(intptr_t)) + ((sz % sizeof(intptr_t)) ? 1 : 0);
+ object_t* obj = ecalloc(1, sizeof(object_t) + sz);
+ obj->refcount = 0;
+ hash_add(&ZCT, obj->data);
+ return obj->data;
+}
+
+void gc_addref(void* p)
+{
+ log_add((object_t*)p-1, INCREMENT);
+}
+
+void gc_delref(void* p)
+{
+ log_add((object_t*)p-1, DECREMENT);
+}
+
+int main(int argc, char** argv)
+{
+ (void)argc, (void)argv;
+ Stack_Bot = &(intptr_t){0};
+ Log.index = 0;
+ hash_init(&ZCT,0);
+ return usermain(argc, argv);
+}
+
+/* Private Routines
+ ***************************************/
+
+static void gc_scan_region(hash_t* oldzct, intptr_t* p_beg, intptr_t* p_end)
+{
+ /* scan the region word by word */
+ for (; p_beg < p_end; p_beg++)
+ {
+ void* ptr = (void*)*(volatile intptr_t*)p_beg;
+ if (hash_del(oldzct, ptr))
+ {
+ hash_add(&ZCT, ptr);
+ }
+ }
+}
+
+static void gc_scan_stack(hash_t* oldzct)
+{
+ /* save the registers on the stack */
+ jmp_buf regs;
+ (void)setjmp(regs);
+ intptr_t* stack_top = (intptr_t*)®s;
+
+ if (Stack_Bot < stack_top)
+ {
+ stack_top = (intptr_t*)(®s+1);
+ gc_scan_region(oldzct, (intptr_t*)Stack_Bot, (intptr_t*)stack_top);
+ }
+ else
+ {
+ gc_scan_region(oldzct, (intptr_t*)stack_top, (intptr_t*)Stack_Bot);
+ }
+}
+
+static void gc_scan_globals(hash_t* oldzct)
+{
+#ifdef __APPLE__
+ intptr_t rbeg = (intptr_t )get_edata();
+ intptr_t rend = (intptr_t )get_end();
+#else
+ extern intptr_t etext, end;
+ intptr_t rbeg = (intptr_t)&etext;
+ intptr_t rend = (intptr_t)&end;
+#endif
+ intptr_t mask = sizeof(intptr_t)-1;
+ if (rbeg & mask)
+ {
+ rbeg = (rbeg & ~mask) + sizeof(intptr_t);
+ }
+ if (rend & mask)
+ {
+ rend = (rbeg & ~mask);
+ }
+#ifdef __APPLE__
+ printf("%p %p %p %p-%p\n",
+ (void*)get_etext(),
+ (void*)get_edata(),
+ (void*)get_end(),
+ (void*)rbeg,
+ (void*)rend
+ );
+#else
+ printf("%p %p %p-%p\n",
+ (void*)&etext,
+ (void*)&end,
+ (void*)rbeg,
+ (void*)rend
+ );
+#endif
+ gc_scan_region(oldzct, (intptr_t*)rbeg, (intptr_t*)rend);
+}
+
+static void gc_collect(void)
+{
+ /* process the log */
+ for (uintptr_t i = 0; i < Log.index; i++)
+ {
+ object_t* obj = (object_t*)(Log.log[i] & ~DECREMENT);
+ if (Log.log[i] & DECREMENT)
+ {
+ obj_delref(obj);
+ }
+ else
+ {
+ obj_addref(obj);
+ }
+ }
+ Log.index = 0;
+
+ /* identify which ZCT entries are still live */
+ hash_t oldzct = ZCT;
+ hash_init(&ZCT, oldzct.nslots);
+ gc_scan_stack(&oldzct);
+ gc_scan_globals(&oldzct);
+
+ /* free the rest as they are dead for sure */
+ size_t nslots = Primes[oldzct.nslots];
+ void** slots = oldzct.slots;
+ for (size_t i = 0; i < nslots; i++)
+ {
+ if (slots[i] != NULL && slots[i] != DELETED)
+ {
+ free( ((object_t*)slots[i])-1 );
+ }
+ }
+ free(slots);
+}
+
+static void log_add(object_t* obj, int op)
+{
+ Log.log[Log.index++] = (((uintptr_t)obj) | op);
+ if (Log.index >= ((sizeof(Log)/sizeof(uintptr_t))-1))
+ {
+ /* process the log */
+ gc_collect();
+ }
+}
+
+static void obj_addref(object_t* obj)
+{
+ obj->refcount++;
+ if (obj->refcount == 1)
+ {
+ hash_del(&ZCT, obj->data);
+ }
+}
+
+static void obj_delref(object_t* obj)
+{
+ obj->refcount--;
+ if (obj->refcount == 0)
+ {
+ hash_add(&ZCT, obj->data);
+ }
+}
+
+/* Hashtable Routines
+ ***************************************/
+
+static uint64_t hash64(uint64_t key)
+{
+ key = (~key) + (key << 21);
+ key = key ^ (key >> 24);
+ key = (key + (key << 3)) + (key << 8);
+ key = key ^ (key >> 14);
+ key = (key + (key << 2)) + (key << 4);
+ key = key ^ (key >> 28);
+ key = key + (key << 31);
+ return key;
+}
+
+static void hash_init(hash_t* hash, int nslots)
+{
+ memset(hash, 0, sizeof(hash_t));
+ hash->nslots = nslots;
+ hash->thold = (size_t)(Primes[hash->nslots] * THOLD);
+ hash->slots = calloc(Primes[hash->nslots], sizeof(void*));
+}
+
+static void hash_grow(hash_t* hash)
+{
+ size_t nslots = Primes[hash->nslots];
+ void** slots = hash->slots;
+ hash->size = 0;
+ hash->nslots++;
+ hash->thold = (size_t)(Primes[hash->nslots] * THOLD);
+ hash->slots = calloc(Primes[hash->nslots], sizeof(void*));
+
+ for (size_t i = 0; i < nslots; i++)
+ {
+ if (slots[i] != NULL)
+ {
+ hash_add(hash, slots[i]);
+ }
+ }
+ free(slots);
+}
+
+static void hash_add(hash_t* hash, void* entry)
+{
+ /* check if a resize is needed first */
+ if (hash->size >= hash->thold)
+ {
+ hash_grow(hash);
+ }
+
+ /* now hash and add the new item */
+ uint64_t hcode = hash64((uint64_t)entry);
+ size_t index = (hcode % Primes[hash->nslots]);
+ while (1)
+ {
+ if (hash->slots[index] == entry)
+ {
+ break;
+ }
+ else if ((hash->slots[index] == NULL) || (hash->slots[index] == DELETED))
+ {
+ hash->slots[index] = entry;
+ hash->size++;
+ break;
+ }
+ else
+ {
+ index = (index == Primes[hash->nslots]-1 ? 0 : index+1);
+ }
+ }
+}
+
+static int hash_del(hash_t* hash, void* entry)
+{
+ uint64_t hcode = hash64((uint64_t)entry);
+ size_t index = (hcode % Primes[hash->nslots]);
+
+ while (1)
+ {
+ if (hash->slots[index] == NULL)
+ {
+ return 0;
+ }
+ else if (hash->slots[index] == entry)
+ {
+ hash->slots[index] = DELETED;
+ return 1;
+ }
+ else
+ {
+ index = (index == Primes[hash->nslots]-1 ? 0 : index+1);
+ }
+ }
+}