Created
October 29, 2013 19:45
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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. | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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