A generic implementation of LinkedList in C

Example


#include "list.h"
typedef struct IntElem {
    int data;
    struct IntElem *next;
} IntElem_t;

int main(int argc, char const *argv[])
{

    LinkedList *list = LinkedListInit();
    IntElem_t *iter, *rm;
    for (int i = 0; i < 10; ++i) {
        IntElem_t *item = malloc(sizeof(IntElem_t));
        item->data = i;
        if (i == 5) rm = item;
        LinkedListAppend(struct IntElem, list, item);
    }
    LinkedListForEach(IntElem_t, list, iter, {
        printf("%d\n", iter->data);
    })
    LinkedListRemove(IntElem_t, list, rm);
    LinkedListForEach(IntElem_t, list, iter, {
        printf("%d\n", iter->data);
    })
    return 0;
}   
                        

in file linked_list.h


/**
* @brief General LinkedList data structure
*        Elements in the linked-list should be defined like
*        struct Element {
*            typename data;
*            struct Element *next; // the `next` member can be defined 
*                                  // anywhere in the structure
*        }
*        
*/
typedef struct {
	void *elem_head;
	void *elem_tail;
	size_t sz;
	void (*dealloc)(void *elem);
} LinkedList;

LinkedList *LinkedListInit();

/**
* @brief Implementation of appending an item to the linked-list
*        This function is wrapped by a macro function.
* 
* @param list The linked-list
* @param elem element to append
* @param offset offset to the `next` member in the struct
* @return uint8_t 0 on success and 1 on failure
*/
uint8_t __LinkedListAppend(LinkedList *list, void *elem, uint64_t offset);

uint8_t __LinkedListRemove(LinkedList *list, void *elem, uint64_t offset);
void __LinkedListDestroy(LinkedList *list, uint64_t offset, uint8_t free_elem);
void *__LinkedListGet(LinkedList *list, size_t idx, uint64_t offset);
void LinkedListSetDealloc(LinkedList *list, void (*dealloc)(void *elem));
void __LinkedListMergeSort(LinkedList *list, int (*compare)(void *, void *), uint64_t offset);
#define LinkedListAppend(typename, list, elem) { \
	uint64_t offset = offsetof(typename, next); \
	if ((__LinkedListAppend(list, elem, offset))) { \
		fatalf("LinkedList at <0x%x> append failed", list); \
	}; \
} while (0); \

#define LinkedListRemove(typename, list, elem) ({ \
	void *ret; \
	do { \
		uint64_t offset = offsetof(typename, next); \
		ret = __LinkedListRemove(list, elem, offset); \
	} while (0); \
	ret; \
}) \

#define LinkedListDestroy(typename, list, free_elem) { \
	uint64_t offset = offsetof(typename, next); \
	__LinkedListDestroy(list, offset, free_elem); \
} while (0); \

#define LinkedListGet(typename, list, idx) ({ \
	void *ret; \
	do { \
	uint64_t offset = offsetof(typename, next); \
	ret = (void *)__LinkedListGet(list, idx, offset); \
	} while (0); \
	ret; \
}) \

#define LinkedListMergeSort(typename, list, compare)  { \
	uint64_t offset = offsetof(typename, next); \
	__LinkedListMergeSort(list, compare, offset); \
} while (0); \

#define LinkedListForEach(typename, list, vvar, code) { \
	uint64_t offset = offsetof(typename, next); \
	vvar = (typename *) list->elem_head; \
	while (vvar) { \
		code; \
		vvar = (typename *)(*(void **)((void *)vvar + offset)); \
	} \
} \

#define LinkedListForEachInRange(typename, list, vvar, from, to, code) { \
	uint64_t offset = offsetof(typename, next); \
	vvar = (typename *) list->elem_head; \
	while (vvar) { \
		code; \
		vvar = (typename *)(*(void **)((void *)vvar + offset)); \
	} \
} \

#define LinkedListNext(typename, vvar) ((typename *)(*(void **)((void *)vvar + offsetof(typename, vvar))))
                        

in file linked_list.c


LinkedList *LinkedListInit()
{
	LinkedList *list = malloc(sizeof(LinkedList));
	memset(list, 0, sizeof(LinkedList));
	return list;
}

uint8_t __LinkedListAppend(LinkedList *list, void *elem, uint64_t offset)
{
	void *curr, **next;
	if (list->elem_head == NULL) {
		list->elem_head = list->elem_tail = elem;
		list->sz = 1;
		return 0;
	} else {
		next = (list->elem_tail + offset);
		*next = elem;
		list->elem_tail = elem;
		list->sz++;
		return 0;
	}
	return 1;
}

uint8_t __LinkedListRemove(LinkedList *list, void *elem, uint64_t offset)
{
	void **curr = &list->elem_head;
	while ((*curr) != elem) {
		curr = (void **)(*curr + offset);
	}
	*curr = (*(void **)(elem + offset));
}


void __LinkedListDestroy(LinkedList *list, uint64_t offset, uint8_t free_elem)
{
	if (free_elem && (list->sz > 0)) {
		if (!list->dealloc) {
			warnf("No deallocation function found. Skip")
		} else {
			void *curr = list->elem_head, *next;
			while (curr != list->elem_tail) {
				next = (*(void **)(curr + offset));
				list->dealloc(curr);
					
				curr = next;
			}
			list->dealloc(curr);
		}
	}
	free(list);
}

void *__LinkedListGet(LinkedList *list, size_t idx, uint64_t offset) 
{
	void *curr = list->elem_head;
	size_t i = 0;
	while (i < idx) {
		curr = (*(void **)(curr + offset));
		i++;
	}
	return curr;
}

void LinkedListSetDealloc(LinkedList *list, void (*dealloc)(void *elem))
{
	list->dealloc = dealloc;
}

static void *LinkedListMerge(void *a, void *b, int (*compare)(void *, void *), uint64_t offset)
{
	void *result = NULL;
	void **next;
	if (a == NULL) return b;
	else if (b == NULL) return a;
	if (compare(a, b) <= 0) {
		result = a;
		next = (void **)(result + offset);
		*next = LinkedListMerge((*(void **)(a + offset)), b, compare, offset);
	} else {
		result = b;
		next = (void **)(result + offset);
		*next = LinkedListMerge(a, ((*(void **)(b + offset))), compare, offset);
	}
	return result;
}

static void LinkedListSplit(void *source, void **front_ref, void **back_ref, uint64_t offset)
{
	void *a, *b;
	void **next;
	a = source;
	b = (*(void **)(a + offset));
	while (b != NULL) {
		b = (*(void **)(b + offset));
		if (b != NULL) {
			a = (*(void **)(a + offset));
			b = (*(void **)(b + offset));
		}
	}
	*front_ref = source;
	*back_ref = (*(void **)(a + offset));
	next = (void **)(a + offset);
	*next = NULL;
}

static void __LinkedListMergeSort_helper(void **head_ref, int (*compare)(void *, void *), uint64_t offset)
{
	void *head = *head_ref;
	void *a, *b;
	if ((head == NULL) || ((*(void **)(head + offset)) == NULL)) {
		return;
	}
	LinkedListSplit(head, &a, &b, offset);
	__LinkedListMergeSort_helper(&a, compare, offset);
	__LinkedListMergeSort_helper(&b, compare, offset);
	*head_ref = LinkedListMerge(a, b, compare, offset);
}

void __LinkedListMergeSort(LinkedList *list, int (*compare)(void *, void *), uint64_t offset)
{
	__LinkedListMergeSort_helper(&list->elem_head, compare, offset);
	void *curr = list->elem_head;
	for (size_t i = 0; i < list->sz - 1; ++i) {
		curr = (*(void **)(curr + offset));
	}
	list->elem_tail = curr;
}