/*dlist.c by xianjimli*/
#include <stdio.h>
#include <stdlib.h>
#include "dlist.h"
typedef struct _DListNode
{
struct _DListNode* next;
struct _DListNode* prev;
void* data;
} DListNode;
struct _DList
{
DListNode* first;
void* data_destroy_ctx;
DListDataDestroyFunc data_destroy;
};
static void dlist_destroy_data(DList* thiz, void* data)
{
if (NULL != thiz->data_destroy) {
thiz->data_destroy(thiz->data_destroy_ctx, data);
}
return;
}
static DListNode* dlist_node_create(DList* thiz, void* data)
{
DListNode* node = (DListNode*)malloc(sizeof(DListNode));
if (NULL != node) {
node->next = NULL;
node->prev = NULL;
node->data = data;
}
return node;
}
static void dlist_node_destroy(DList* thiz, DListNode* node)
{
if (NULL != node) {
node->next = NULL;
node->prev = NULL;
dlist_destroy_data(thiz, node->data);
}
free(node);
return;
}
DList* dlist_create(DListDataDestroyFunc data_destroy, void* data_destroy_ctx)
{
DList* thiz = malloc(sizeof(DList));
if (NULL != thiz) {
thiz->first = NULL;
thiz->data_destroy = data_destroy;
thiz->data_destroy_ctx = data_destroy_ctx;
}
return thiz;
}
static DListNode* dlist_get_node(DList* thiz, size_t index, int fail_return_last)
{
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL, NULL);
iter = thiz->first;
while (iter != NULL && iter->next != NULL && index > 0) {
iter = iter->next;
index--;
}
if (!fail_return_last) {
iter = index > 0 ? NULL : iter;
}
return iter;
}
DListRet dlist_insert(DList* thiz, size_t index, void* data)
{
DListNode* node = NULL;
DListNode* cursor = NULL;
return_val_if_fail(NULL != thiz, DLIST_RET_INVALID_PARAMS);
if ((node = dlist_node_create(thiz, data)) == NULL) {
return DLIST_RET_OOM;
}
if (thiz->first == NULL) {
thiz->first = node;
return DLIST_RET_OK;
}
cursor = dlist_get_node(thiz, index, 1);
if (index < dlist_length(thiz)) {
if (cursor == thiz->first) {
thiz->first = node;
} else {
cursor->prev->next = node;
node->prev = cursor->prev;
}
node->next = cursor;
cursor->prev = node;
} else {
cursor->next = node;
node->prev = cursor;
}
return DLIST_RET_OK;
}
DListRet dlist_prepend(DList* thiz, void* data)
{
return dlist_insert(thiz, 0, data);
}
DListRet dlist_append(DList* thiz, void* data)
{
return dlist_insert(thiz, -1, data);
}
DListRet dlist_delete(DList* thiz, size_t index)
{
DListNode* cursor = dlist_get_node(thiz, index, 0);
return_val_if_fail(NULL != thiz, DLIST_RET_INVALID_PARAMS);
if (cursor != NULL) {
if (cursor == thiz->first) {
thiz->first = cursor->next;
}
if (cursor->next != NULL) {
cursor->next->prev = cursor->prev;
}
if (cursor->prev != NULL) {
cursor->prev->next = cursor->next;
}
dlist_node_destroy(thiz, cursor);
}
return DLIST_RET_OK;
}
DListRet dlist_get_by_index(DList* thiz, size_t index, void** data)
{
DListNode* cursor = dlist_get_node(thiz, index, 0);
return_val_if_fail(cursor != NULL, DLIST_RET_INVALID_PARAMS);
*data = cursor->data;
return DLIST_RET_OK;
}
DListRet dlist_set_by_index(DList* thiz, size_t index, void* data)
{
DListNode* cursor = dlist_get_node(thiz, index, 0);
return_val_if_fail(cursor != NULL, DLIST_RET_INVALID_PARAMS);
cursor->data = data;
return DLIST_RET_OK;
}
size_t dlist_length(DList* thiz)
{
size_t length = 0;
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL, 0);
iter = thiz->first;
while (iter != NULL)
{
length++;
iter = iter->next;
}
return length;
}
DListRet dlist_foreach(DList* thiz, DListDataVisitFunc visit, void* ctx)
{
DListNode* iter = NULL;
DListRet ret = DLIST_RET_OK;
return_val_if_fail(thiz != NULL && visit != NULL, DLIST_RET_INVALID_PARAMS);
iter = thiz->first;
while (iter != NULL) {
ret = visit(ctx, iter->data);
iter = iter->next;
}
return ret;
}
int dlist_find(DList* thiz, DListDataCompareFunc cmp, void* ctx)
{
int index = 0;
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL && cmp != NULL, -1);
iter = thiz->first;
while (iter != NULL) {
if (0 == cmp(ctx, iter->data)) {
break;
}
index++;
iter = iter->next;
}
return index;
}
void dlist_destroy(DList* thiz)
{
DListNode* iter = NULL;
DListNode* next = NULL;
return_if_fail(thiz != NULL);
iter = thiz->first;
while (iter != NULL) {
next = iter->next;
dlist_destroy_data(thiz, iter);
iter = next;
}
thiz->first = NULL;
free(thiz);
return;
}
#ifdef DLIST_TEST
#include <assert.h>
static void dlist_data_destroy(void* ctx, void* data)
{
}
static int dlist_data_cmp(void* ctx, void* data)
{
return (int)ctx - (int)data;
}
static DListRet dlist_data_visit(void* ctx, void* data)
{
int* expected = (int*)ctx;
assert((*expected) == (int)data);
(*expected)--;
return DLIST_RET_OK;
}
void test_int_dlist(void)
{
int i = 0;
int data = 0;
int n = 100;
int s = 0;
DList* dlist = dlist_create(NULL, NULL);
for (i = 0; i < n; i++) {
assert(DLIST_RET_OK == dlist_append(dlist, (void*)i));
assert(dlist_length(dlist) == i+1);
assert(DLIST_RET_OK == dlist_get_by_index(dlist, i, (void**)&data));
assert(i == data);
assert(DLIST_RET_OK == dlist_set_by_index(dlist, i, (void*)(i*2)));
assert(DLIST_RET_OK == dlist_get_by_index(dlist, i, (void**)&data));
assert(data == 2*i);
assert(DLIST_RET_OK == dlist_set_by_index(dlist, i, (void*)i));
assert(i == dlist_find(dlist, dlist_data_cmp, (void*)i));
}
data = dlist_length(dlist);
for (i = 0; i < n; i++) {
assert(DLIST_RET_OK == dlist_get_by_index(dlist, 0, (void**)&data));
assert(i == data);
assert(dlist_length(dlist) == (n-i));
assert(dlist_delete(dlist, 0) == DLIST_RET_OK);
assert(dlist_length(dlist) == (n-i-1));
if ((i + 1) < n) {
assert(dlist_get_by_index(dlist, 0, (void**)&data) == DLIST_RET_OK);
assert(data == (i+1));
}
}
assert(dlist_length(dlist) == 0);
for (i = 0; i < n; i++) {
assert(dlist_prepend(dlist, (void*)i) == DLIST_RET_OK);
assert(dlist_length(dlist) == i+1);
assert(dlist_get_by_index(dlist, 0, (void**)&data) == DLIST_RET_OK);
assert(data == i);
assert(dlist_set_by_index(dlist, 0 ,(void*)(2*i)) == DLIST_RET_OK);
assert(dlist_get_by_index(dlist, 0, (void**)&data) == DLIST_RET_OK);
assert(data == 2*i);
assert(DLIST_RET_OK == dlist_set_by_index(dlist, 0, (void*)i));
}
i = n - 1;
assert(DLIST_RET_OK == dlist_foreach(dlist, dlist_data_visit, &i));
s = dlist_length(dlist);
for (i = 0; i < n; i++) {
assert(dlist_insert(dlist, i, (void*)i) == DLIST_RET_OK);
assert(dlist_length(dlist) == (s+i+1));
assert(dlist_get_by_index(dlist, i, (void**)&data) == DLIST_RET_OK);
assert(data == i);
assert(dlist_set_by_index(dlist, i, (void*)(2*i)) == DLIST_RET_OK);
assert(dlist_get_by_index(dlist, i, (void**)&data) == DLISech(NULL, NULL, NULL) == DLIST_RET_INVALID_RET_OK);
assert(data == 2*i);
}
dlist_destroy(dlist);
return;
}
static void test_invalid_params(void)
{
assert(dlist_length(NULL) == DLIST_RET_INVALID_PARAMS);
assert(dlist_prepend(NULL, 0) == DLIST_RET_INVALID_PARAMS);
assert(dlist_delete(NULL, 0) == DLIST_RET_INVALID_PARAMS);
assert(dlist_insert(NULL, 0, NULL) == DLIST_RET_INVALID_PARAMS);
assert(dlist_set_by_index(NULL, 0, NULL) == DLIST_RET_INVALID_PARAMS);
assert(dlist_get_by_index(NULL, 0, NULL) == DLIST_RET_INVALID_PARAMS);
assert(dlist_find(NULL, NULL, NULL) < 0);
assert(dlist_append(NULL, 0) == DLIST_RET_INVALID_PARAMS);
assert(dlist_foreach(NULL, NULL, NULL) == DLIST_RET_INVALID_PARAMS);
}
int main(int argc,char** argv)
{
printf("%s\n", "test_dlist_begin");
test_int_dlist();
return 0;
}
#endif /*DLIST_TEST*/
/*dlist.h*/
#include <stdio.h>
#ifndef _DLIST_H
#define _DLIST_H
#ifdef __cplusplus
extern "c" {
#endif /*__cplusplus*/
typedef enum _DListRet
{
DLIST_RET_OK,
DLIST_RET_OOM,
DLIST_RET_STOP,
DLIST_RET_INVALID_PARAMS,
DLIST_RET_FAIL
} DListRet;
struct _DList;
typedef struct _DList DList;
typedef void (*DListDataDestroyFunc)(void* ctx, void* data);
typedef int (*DListDataCompareFunc)(void* ctx, void* data);
typedef DListRet (*DListDataVisitFunc)(void* ctx, void* data);
DList* dlist_create(DListDataDestroyFunc data_destroy, void* data_destroy_ctx);
DListRet dlist_insert(DList* thiz, size_t index, void* data);
DListRet dlist_prepend(DList* thiz, void* data);
DListRet dlist_append(DList* thiz, void* data);
DListRet dlist_delete(DList* thiz, size_t index);
DListRet dlist_get_by_index(DList* thiz, size_t index, void** data);
DListRet dlist_set_by_index(DList* thiz, size_t index, void* data);
size_t dlist_length(DList* thiz);
int dlist_find(DList* thiz, DListDataCompareFunc cmp, void* ctx);
DListRet dlist_foreach(DList* thiz, DListDataVisitFunc visit, void* ctx);
void dlist_destroy(DList* thiz);
#define return_if_fail(p) if (!(p)) \
{printf("%s:%d Warning:"#p" failed.\n", \
__func__, __LINE__); return;}
#define return_val_if_fail(p, ret) if (!(p)) \
{printf("%s:%d Warning:"#p" failed.\n", \
__func__, __LINE__); return (ret);}
#ifdef __cplusplus
}
#endif /*__cplusplus*/
#endif /*_DLIST_H*/
/*Makefile*/all: gcc -g -shared dlist.c -o libdlist.so gcc -g -DDLIST_TEST dlist.c -o dlist_testclean: rm *test *.exe *.so *.o