Skip to content

Instantly share code, notes, and snippets.

@gotcake
Created October 29, 2013 19:45
Show Gist options
  • Save gotcake/7221283 to your computer and use it in GitHub Desktop.
Save gotcake/7221283 to your computer and use it in GitHub Desktop.
A small implementation of reference counting for C. Frees the programmer from keeping track of pointers and knowing when to free them when they are shared between multiple structures and functions.
#include "reference_counting.h"
#include <stdio.h>
#define NULL 0
// reference counting....
static struct m_ref {
void *ptr;
int count;
struct m_ref *next;
};
// the buckets
static struct m_ref **ref_buckets;
// the number of buckets
static int ref_bucket_count = 0;
// whether or not the hash table has been inited
static int ref_table_inited = 0;
// the load on the table
static int ref_table_load = 0;
static inline void init_ref_table() {
int count = 17, i = 0;
ref_bucket_count = count;
ref_buckets = malloc(sizeof(struct m_ref*) * count);
for (; i<count; ++i)
ref_buckets[i] = NULL;
ref_table_inited = 1;
}
static inline unsigned int m_hash_ref(void *ptr) {
unsigned int x = (unsigned long)ptr % 4294967296;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x) * 0x45d9f3b;
x = ((x >> 16) ^ x);
return x % ref_bucket_count;
}
static inline struct m_ref *get_ref_table_entry(int hash, void *ptr) {
struct m_ref *bucket = ref_buckets[hash];
while (bucket != NULL && bucket->ptr != ptr)
bucket = bucket->next;
return bucket;
}
static inline void remove_ref_entry(int hash, void *ptr) {
struct m_ref *bucket = ref_buckets[hash], *prev = NULL;
--ref_table_load;
if (bucket->ptr == ptr) {
ref_buckets[hash] = bucket->next;
} else {
while (bucket != NULL && bucket->ptr != ptr) {
prev = bucket;
bucket = bucket->next;
}
if (bucket != NULL)
prev->next = bucket->next;
}
}
static inline void create_ref_entry(int hash, void *ptr, int count) {
struct m_ref *bucket = ref_buckets[hash];
if (bucket == NULL) {
bucket = ref_buckets[hash] = malloc(sizeof(struct m_ref));
bucket->ptr = ptr;
bucket->count = count;
bucket->next = NULL;
} else {
while (bucket->next != NULL)
bucket = bucket->next;
struct m_ref *nbucket = malloc(sizeof(struct m_ref));
nbucket->ptr = ptr;
nbucket->count = count;
nbucket->next = NULL;
bucket->next = nbucket;
}
}
static inline void expand_ref_table() {
struct m_ref **buckets = ref_buckets;
int oldCount = ref_bucket_count;
ref_bucket_count = ref_bucket_count * 2 + 1;
ref_buckets = malloc(sizeof(struct m_ref*) * ref_bucket_count);
int i=0;
for (; i<oldCount; ++i) {
struct m_ref *bucket = buckets[i], *old;
while (bucket != NULL) {
create_ref_entry(m_hash_ref(bucket->ptr), bucket->ptr, bucket->count);
old = bucket;
bucket = bucket->next;
free(old);
}
}
free(buckets);
}
static inline void init_ref_entry(int hash, void *ptr) {
if (++ref_table_load > ref_bucket_count) {
expand_ref_table();
hash = m_hash_ref(ptr);
}
create_ref_entry(hash, ptr, 1);
}
void *grab(unsigned long memorySize) {
void *ptr = malloc(memorySize);
use(ptr);
return ptr;
}
void use(void *heapBlockPtr) {
if (heapBlockPtr == NULL) {
puts("ERROR: Attempted use of null pointer in reference counting.\n");
abort(); // cannot use null pointer
}
if (ref_table_inited == 0)
init_ref_table();
unsigned int hash = m_hash_ref(heapBlockPtr);
struct m_ref *ref = get_ref_table_entry(hash, heapBlockPtr);
if (ref == NULL) {
init_ref_entry(hash, heapBlockPtr);
} else
++(ref->count);
}
void release(void *heapBlockPtr) {
if (ref_table_inited == 0)
init_ref_table();
unsigned int hash = m_hash_ref(heapBlockPtr);
struct m_ref *ref = get_ref_table_entry(hash, heapBlockPtr);
if (ref != NULL) {
if (--(ref->count) == 0) {
remove_ref_entry(hash, heapBlockPtr);
free(ref);
free(heapBlockPtr);
}
} else {
puts("ERROR: Memory reference not found. Pointer not managed by reference counter.\n");
abort(); // ref not found. pointer not in use.
}
}
/*
* File: reference_counting.h
* Author: Aaron Cake
*
* Defines a set of functions that can be used to manage memory via reference counting
*
* Created on September 24, 2013, 8:34 PM
*/
#ifndef REFERENCE_COUNTING_H
#define REFERENCE_COUNTING_H
#ifdef __cplusplus
extern "C" {
#endif
/**
* Declare usage of a memory block.
* Increment the internal counter for the given memory block by 1.
* @param heapBlockPtr the pointer to the beginning of a memory block
*/
void use(void *heapBlockPtr);
/**
* Declare release of a memory block.
* Decrement the internal counter for the given memory block by 1.
* @param heapBlockPtr the pointer to the beginning of a memory block
*/
void release(void *heapBlockPtr);
/**
* Malloc and use a memory block of the given size
* @param memorySize the size in bytes of the memory block
* @return a pointer to the begining of the memory block
*/
void *grab(unsigned long memorySize);
#ifdef __cplusplus
}
#endif
#endif /* REFERENCE_COUNTING_H */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment