add a linked list utility.
This commit is contained in:
@@ -0,0 +1,301 @@
|
|||||||
|
#ifndef LIST_MALLOC
|
||||||
|
#define LIST_MALLOC malloc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LIST_FREE
|
||||||
|
#define LIST_FREE free
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//
|
||||||
|
// list.c
|
||||||
|
//
|
||||||
|
// Copyright (c) 2010 TJ Holowaychuk <[email protected]>
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "list.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocates a new list_node_t. NULL on failure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_node_new(void *val) {
|
||||||
|
list_node_t *self;
|
||||||
|
if (!(self = LIST_MALLOC(sizeof(list_node_t))))
|
||||||
|
return NULL;
|
||||||
|
self->prev = NULL;
|
||||||
|
self->next = NULL;
|
||||||
|
self->val = val;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a new list_t. NULL on failure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_t *
|
||||||
|
list_new(void) {
|
||||||
|
list_t *self;
|
||||||
|
if (!(self = LIST_MALLOC(sizeof(list_t))))
|
||||||
|
return NULL;
|
||||||
|
self->head = NULL;
|
||||||
|
self->tail = NULL;
|
||||||
|
self->free = NULL;
|
||||||
|
self->match = NULL;
|
||||||
|
self->len = 0;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove all the node in list.
|
||||||
|
* @self: Pointer to the list
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
list_clear(list_t *self) {
|
||||||
|
uint32_t len = self->len;
|
||||||
|
list_node_t *next;
|
||||||
|
list_node_t *curr = self->head;
|
||||||
|
|
||||||
|
while (len) {
|
||||||
|
next = curr->next;
|
||||||
|
if (self->free) self->free(curr->val);
|
||||||
|
LIST_FREE(curr);
|
||||||
|
curr = next;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the list.
|
||||||
|
* @self: Pointer to the list
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
list_destroy(list_t *self) {
|
||||||
|
list_clear(self);
|
||||||
|
LIST_FREE(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Append the given node to the list
|
||||||
|
* and return the node, NULL on failure.
|
||||||
|
* @self: Pointer to the list for popping node
|
||||||
|
* @node: the node to push
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_rpush(list_t *self, list_node_t *node) {
|
||||||
|
if (!node) return NULL;
|
||||||
|
|
||||||
|
if (self->len) {
|
||||||
|
node->prev = self->tail;
|
||||||
|
node->next = NULL;
|
||||||
|
self->tail->next = node;
|
||||||
|
self->tail = node;
|
||||||
|
} else {
|
||||||
|
self->head = self->tail = node;
|
||||||
|
node->prev = node->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
++self->len;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return / detach the last node in the list, or NULL.
|
||||||
|
* @self: Pointer to the list for popping node
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_rpop(list_t *self) {
|
||||||
|
if (!self->len) return NULL;
|
||||||
|
|
||||||
|
list_node_t *node = self->tail;
|
||||||
|
|
||||||
|
if (--self->len) {
|
||||||
|
(self->tail = node->prev)->next = NULL;
|
||||||
|
} else {
|
||||||
|
self->tail = self->head = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->next = node->prev = NULL;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return / detach the first node in the list, or NULL.
|
||||||
|
* @self: Pointer to the list for popping node
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_lpop(list_t *self) {
|
||||||
|
if (!self->len) return NULL;
|
||||||
|
|
||||||
|
list_node_t *node = self->head;
|
||||||
|
|
||||||
|
if (--self->len) {
|
||||||
|
(self->head = node->next)->prev = NULL;
|
||||||
|
} else {
|
||||||
|
self->head = self->tail = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
node->next = node->prev = NULL;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepend the given node to the list
|
||||||
|
* and return the node, NULL on failure.
|
||||||
|
* @self: Pointer to the list for pushing node
|
||||||
|
* @node: the node to push
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_lpush(list_t *self, list_node_t *node) {
|
||||||
|
if (!node) return NULL;
|
||||||
|
|
||||||
|
if (self->len) {
|
||||||
|
node->next = self->head;
|
||||||
|
node->prev = NULL;
|
||||||
|
self->head->prev = node;
|
||||||
|
self->head = node;
|
||||||
|
} else {
|
||||||
|
self->head = self->tail = node;
|
||||||
|
node->prev = node->next = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
++self->len;
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the node associated to val or NULL.
|
||||||
|
* @self: Pointer to the list for finding given value
|
||||||
|
* @val: Value to find
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_find(list_t *self, void *val) {
|
||||||
|
list_iterator_t *it = list_iterator_new(self, LIST_HEAD);
|
||||||
|
list_node_t *node;
|
||||||
|
|
||||||
|
while ((node = list_iterator_next(it))) {
|
||||||
|
if (self->match) {
|
||||||
|
if (self->match(val, node->val)) {
|
||||||
|
list_iterator_destroy(it);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (val == node->val) {
|
||||||
|
list_iterator_destroy(it);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list_iterator_destroy(it);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the node at the given index or NULL.
|
||||||
|
* @self: Pointer to the list for finding given index
|
||||||
|
* @index: the index of node in the list
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_at(list_t *self, int index) {
|
||||||
|
list_direction_t direction = LIST_HEAD;
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
direction = LIST_TAIL;
|
||||||
|
index = ~index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((unsigned)index < self->len) {
|
||||||
|
list_iterator_t *it = list_iterator_new(self, direction);
|
||||||
|
list_node_t *node = list_iterator_next(it);
|
||||||
|
while (index--) node = list_iterator_next(it);
|
||||||
|
list_iterator_destroy(it);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove the given node from the list, freeing it and it's value.
|
||||||
|
* @self: Pointer to the list to delete a node
|
||||||
|
* @node: Pointer the node to be deleted
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
list_remove(list_t *self, list_node_t *node) {
|
||||||
|
node->prev
|
||||||
|
? (node->prev->next = node->next)
|
||||||
|
: (self->head = node->next);
|
||||||
|
|
||||||
|
node->next
|
||||||
|
? (node->next->prev = node->prev)
|
||||||
|
: (self->tail = node->prev);
|
||||||
|
|
||||||
|
if (self->free) self->free(node->val);
|
||||||
|
|
||||||
|
LIST_FREE(node);
|
||||||
|
--self->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a new list_iterator_t. NULL on failure.
|
||||||
|
* Accepts a direction, which may be LIST_HEAD or LIST_TAIL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_iterator_t *
|
||||||
|
list_iterator_new(list_t *list, list_direction_t direction) {
|
||||||
|
list_node_t *node = direction == LIST_HEAD
|
||||||
|
? list->head
|
||||||
|
: list->tail;
|
||||||
|
return list_iterator_new_from_node(node, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a new list_iterator_t with the given start
|
||||||
|
* node. NULL on failure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_iterator_t *
|
||||||
|
list_iterator_new_from_node(list_node_t *node, list_direction_t direction) {
|
||||||
|
list_iterator_t *self;
|
||||||
|
if (!(self = LIST_MALLOC(sizeof(list_iterator_t))))
|
||||||
|
return NULL;
|
||||||
|
self->next = node;
|
||||||
|
self->direction = direction;
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the next list_node_t or NULL when no more
|
||||||
|
* nodes remain in the list.
|
||||||
|
*/
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_iterator_next(list_iterator_t *self) {
|
||||||
|
list_node_t *curr = self->next;
|
||||||
|
if (curr) {
|
||||||
|
self->next = self->direction == LIST_HEAD
|
||||||
|
? curr->next
|
||||||
|
: curr->prev;
|
||||||
|
}
|
||||||
|
return curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the list iterator.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
list_iterator_destroy(list_iterator_t *self) {
|
||||||
|
LIST_FREE(self);
|
||||||
|
self = NULL;
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
|
||||||
|
//
|
||||||
|
// list.h
|
||||||
|
//
|
||||||
|
// Copyright (c) 2010 TJ Holowaychuk <[email protected]>
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __LIST_H__
|
||||||
|
#define __LIST_H__
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_t iterator direction.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LIST_HEAD,
|
||||||
|
LIST_TAIL
|
||||||
|
} list_direction_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_t node struct.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct list_node {
|
||||||
|
struct list_node *prev;
|
||||||
|
struct list_node *next;
|
||||||
|
void *val;
|
||||||
|
} list_node_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_t struct.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
list_node_t *head;
|
||||||
|
list_node_t *tail;
|
||||||
|
uint32_t len;
|
||||||
|
void (*free)(void *val);
|
||||||
|
bool (*match)(void *a, void *b);
|
||||||
|
} list_t;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_t iterator struct.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
list_node_t *next;
|
||||||
|
list_direction_t direction;
|
||||||
|
} list_iterator_t;
|
||||||
|
|
||||||
|
// Node prototypes.
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_node_new(void *val);
|
||||||
|
|
||||||
|
// list_t prototypes.
|
||||||
|
|
||||||
|
list_t *
|
||||||
|
list_new(void);
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_rpush(list_t *self, list_node_t *node);
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_lpush(list_t *self, list_node_t *node);
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_find(list_t *self, void *val);
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_at(list_t *self, int index);
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_rpop(list_t *self);
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_lpop(list_t *self);
|
||||||
|
|
||||||
|
void
|
||||||
|
list_remove(list_t *self, list_node_t *node);
|
||||||
|
|
||||||
|
void
|
||||||
|
list_clear(list_t *self);
|
||||||
|
|
||||||
|
void
|
||||||
|
list_destroy(list_t *self);
|
||||||
|
|
||||||
|
// list_t iterator prototypes.
|
||||||
|
|
||||||
|
list_iterator_t *
|
||||||
|
list_iterator_new(list_t *list, list_direction_t direction);
|
||||||
|
|
||||||
|
list_iterator_t *
|
||||||
|
list_iterator_new_from_node(list_node_t *node, list_direction_t direction);
|
||||||
|
|
||||||
|
list_node_t *
|
||||||
|
list_iterator_next(list_iterator_t *self);
|
||||||
|
|
||||||
|
void
|
||||||
|
list_iterator_destroy(list_iterator_t *self);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* __CLIBS_LIST_H__ */
|
||||||
Reference in New Issue
Block a user